directory scoped imports

This commit is contained in:
nub31
2025-01-29 20:51:38 +01:00
parent 0c807d765c
commit 88aa2375ef
10 changed files with 69 additions and 68 deletions

6
.gitignore vendored
View File

@@ -33,6 +33,6 @@ Thumbs.db
Desktop.ini
.DS_Store
Nub.Lang/Nub.Lang/Output/*.o
Nub.Lang/Nub.Lang/Output/out
Nub.Lang/Nub.Lang/Output/out.asm
Nub.Lang/Nub.Lang/output/*.o
Nub.Lang/Nub.Lang/output/out
Nub.Lang/Nub.Lang/output/out.asm

View File

@@ -9,7 +9,7 @@ public class Lexer
["func"] = Symbol.Func,
["extern"] = Symbol.Extern,
["return"] = Symbol.Return,
["include"] = Symbol.Include,
["import"] = Symbol.Import,
["let"] = Symbol.Let,
["if"] = Symbol.If,
["else"] = Symbol.Else,
@@ -45,17 +45,14 @@ public class Lexer
['!'] = Symbol.Bang,
};
private readonly string _src;
private string _src = string.Empty;
private int _index;
public Lexer(string src)
public IReadOnlyCollection<Token> Lex(string src)
{
_src = src;
}
public IReadOnlyCollection<Token> Lex()
{
_index = 0;
List<Token> tokens = [];
while (Peek().HasValue)
{

View File

@@ -8,7 +8,7 @@ public class SymbolToken(Symbol symbol) : Token
public enum Symbol
{
Whitespace,
Include,
Import,
Extern,
Func,
Return,

View File

@@ -1,7 +0,0 @@
namespace Nub.Lang.Frontend.Parsing;
public class FileNode(IReadOnlyCollection<string> includes, IReadOnlyCollection<DefinitionNode> definitions) : Node
{
public IReadOnlyCollection<string> Includes { get; } = includes;
public IReadOnlyCollection<DefinitionNode> Definitions { get; } = definitions;
}

View File

@@ -0,0 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
public class ModuleNode(string path, IReadOnlyCollection<string> imports, IReadOnlyCollection<DefinitionNode> definitions) : Node
{
public string Path { get; } = path;
public IReadOnlyCollection<string> Imports { get; } = imports;
public IReadOnlyCollection<DefinitionNode> Definitions { get; } = definitions;
}

View File

@@ -6,38 +6,37 @@ namespace Nub.Lang.Frontend.Parsing;
public class Parser
{
private readonly Token[] _tokens;
private IReadOnlyCollection<Token> _tokens = [];
private int _index;
public Parser(IReadOnlyCollection<Token> tokens)
{
_tokens = tokens.ToArray();
}
public FileNode ParseFile(string relativePath)
public ModuleNode ParseModule(IReadOnlyCollection<Token> tokens, string path)
{
_index = 0;
_tokens = tokens;
List<DefinitionNode> definitions = [];
List<string> includes = [];
while (TryExpectSymbol(Symbol.Include))
{
var name = ExpectLiteral();
if (name.Type is not StringType)
{
throw new Exception("Using statements must have a string literal value");
}
TryExpectSymbol(Symbol.Semicolon);
includes.Add(name.Value);
}
List<string> imports = [];
while (Peek().HasValue)
{
definitions.Add(ParseDefinition());
if (TryExpectSymbol(Symbol.Import))
{
var name = ExpectLiteral();
if (name.Type is not StringType)
{
throw new Exception("Import statements must have a string literal value");
}
TryExpectSymbol(Symbol.Semicolon);
imports.Add(name.Value);
}
else
{
definitions.Add(ParseDefinition());
}
}
return new FileNode(includes, definitions);
return new ModuleNode(path, imports, definitions);
}
private DefinitionNode ParseDefinition()
@@ -450,14 +449,14 @@ public class Parser
private Optional<Token> Peek()
{
while (_index < _tokens.Length && _tokens[_index] is SymbolToken { Symbol: Symbol.Whitespace })
while (_index < _tokens.Count && _tokens.ElementAt(_index) is SymbolToken { Symbol: Symbol.Whitespace })
{
Next();
}
if (_index < _tokens.Length)
if (_index < _tokens.Count)
{
return _tokens[_index];
return _tokens.ElementAt(_index);
}
return Optional<Token>.Empty();

View File

@@ -1,39 +1,45 @@
using System.Diagnostics;
using Nub.Lang.Backend.Custom;
using Nub.Lang.Backend.Custom;
using Nub.Lang.Frontend.Lexing;
using Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Typing;
var rootPath = Path.GetDirectoryName(args[0]);
var rootFileName = Path.GetFileName(args[0]);
Debug.Assert(rootPath != null && rootFileName != null);
List<ModuleNode> modules = [];
Dictionary<string, FileNode> files = [];
var lexer = new Lexer();
var parser = new Parser();
Queue<string> queue = [];
queue.Enqueue(rootFileName);
Parse(args[0]);
while (queue.TryDequeue(out var path))
void Parse(string path)
{
var src = File.ReadAllText(Path.Combine(rootPath, path));
var lexer = new Lexer(src);
var tokens = lexer.Lex();
var parser = new Parser(tokens);
var file = parser.ParseFile(path);
files[path] = file;
var files = Directory.EnumerateFiles(path, "*.nub", SearchOption.TopDirectoryOnly);
foreach (var include in file.Includes)
List<Token> tokens = [];
foreach (var file in files)
{
if (!files.ContainsKey(include))
var src = File.ReadAllText(file);
tokens.AddRange(lexer.Lex(src));
}
var module = parser.ParseModule(tokens, path);
modules.Add(module);
foreach (var import in module.Imports)
{
var importPath = Path.GetFullPath(import, module.Path);
if (modules.All(m => m.Path != importPath))
{
queue.Enqueue(include);
Parse(importPath);
}
}
}
var definitions = files.Values.SelectMany(f => f.Definitions).ToArray();
foreach (var moduleNode in modules)
{
Console.WriteLine(moduleNode.Path);
}
var definitions = modules.SelectMany(f => f.Definitions).ToArray();
var typer = new ExpressionTyper(definitions);
typer.Populate();

View File

@@ -1,5 +1,3 @@
include "core/strlen.nub"
let SYS_WRITE = 1;
let STD_OUT = 1;
let STD_ERR = 2;

View File

@@ -1,4 +1,4 @@
include "core/print.nub";
import "core";
func main() {
println("test");

0
Nub.Lang/Nub.Lang/output/build.sh Normal file → Executable file
View File