diff --git a/.gitignore b/.gitignore index a2efa16..2a5a189 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/Nub.Lang/Nub.Lang/Frontend/Lexing/Lexer.cs b/Nub.Lang/Nub.Lang/Frontend/Lexing/Lexer.cs index 488e217..9295c63 100644 --- a/Nub.Lang/Nub.Lang/Frontend/Lexing/Lexer.cs +++ b/Nub.Lang/Nub.Lang/Frontend/Lexing/Lexer.cs @@ -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 Lex(string src) { _src = src; - } - - public IReadOnlyCollection Lex() - { _index = 0; + List tokens = []; while (Peek().HasValue) { diff --git a/Nub.Lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs b/Nub.Lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs index e19f67e..90a1b9f 100644 --- a/Nub.Lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs +++ b/Nub.Lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs @@ -8,7 +8,7 @@ public class SymbolToken(Symbol symbol) : Token public enum Symbol { Whitespace, - Include, + Import, Extern, Func, Return, diff --git a/Nub.Lang/Nub.Lang/Frontend/Parsing/FileNode.cs b/Nub.Lang/Nub.Lang/Frontend/Parsing/FileNode.cs deleted file mode 100644 index 57497b9..0000000 --- a/Nub.Lang/Nub.Lang/Frontend/Parsing/FileNode.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Nub.Lang.Frontend.Parsing; - -public class FileNode(IReadOnlyCollection includes, IReadOnlyCollection definitions) : Node -{ - public IReadOnlyCollection Includes { get; } = includes; - public IReadOnlyCollection Definitions { get; } = definitions; -} \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Frontend/Parsing/ModuleNode.cs b/Nub.Lang/Nub.Lang/Frontend/Parsing/ModuleNode.cs new file mode 100644 index 0000000..f5e6231 --- /dev/null +++ b/Nub.Lang/Nub.Lang/Frontend/Parsing/ModuleNode.cs @@ -0,0 +1,8 @@ +namespace Nub.Lang.Frontend.Parsing; + +public class ModuleNode(string path, IReadOnlyCollection imports, IReadOnlyCollection definitions) : Node +{ + public string Path { get; } = path; + public IReadOnlyCollection Imports { get; } = imports; + public IReadOnlyCollection Definitions { get; } = definitions; +} \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Frontend/Parsing/Parser.cs b/Nub.Lang/Nub.Lang/Frontend/Parsing/Parser.cs index 76addf0..f546899 100644 --- a/Nub.Lang/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/Nub.Lang/Nub.Lang/Frontend/Parsing/Parser.cs @@ -6,38 +6,37 @@ namespace Nub.Lang.Frontend.Parsing; public class Parser { - private readonly Token[] _tokens; + private IReadOnlyCollection _tokens = []; private int _index; - - public Parser(IReadOnlyCollection tokens) - { - _tokens = tokens.ToArray(); - } - - public FileNode ParseFile(string relativePath) + + public ModuleNode ParseModule(IReadOnlyCollection tokens, string path) { _index = 0; + _tokens = tokens; + List definitions = []; - List 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 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 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.Empty(); diff --git a/Nub.Lang/Nub.Lang/Program.cs b/Nub.Lang/Nub.Lang/Program.cs index 7df74ff..65f34b2 100644 --- a/Nub.Lang/Nub.Lang/Program.cs +++ b/Nub.Lang/Nub.Lang/Program.cs @@ -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 modules = []; -Dictionary files = []; +var lexer = new Lexer(); +var parser = new Parser(); -Queue 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 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(); diff --git a/Nub.Lang/Nub.Lang/input/core/print.nub b/Nub.Lang/Nub.Lang/input/core/print.nub index 4f978ea..0fea73a 100644 --- a/Nub.Lang/Nub.Lang/input/core/print.nub +++ b/Nub.Lang/Nub.Lang/input/core/print.nub @@ -1,5 +1,3 @@ -include "core/strlen.nub" - let SYS_WRITE = 1; let STD_OUT = 1; let STD_ERR = 2; diff --git a/Nub.Lang/Nub.Lang/input/program.nub b/Nub.Lang/Nub.Lang/input/program.nub index cb94a8e..2404d99 100644 --- a/Nub.Lang/Nub.Lang/input/program.nub +++ b/Nub.Lang/Nub.Lang/input/program.nub @@ -1,4 +1,4 @@ -include "core/print.nub"; +import "core"; func main() { println("test"); diff --git a/Nub.Lang/Nub.Lang/output/build.sh b/Nub.Lang/Nub.Lang/output/build.sh old mode 100644 new mode 100755