directory scoped imports
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ public class SymbolToken(Symbol symbol) : Token
|
||||
public enum Symbol
|
||||
{
|
||||
Whitespace,
|
||||
Include,
|
||||
Import,
|
||||
Extern,
|
||||
Func,
|
||||
Return,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
8
Nub.Lang/Nub.Lang/Frontend/Parsing/ModuleNode.cs
Normal file
8
Nub.Lang/Nub.Lang/Frontend/Parsing/ModuleNode.cs
Normal 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;
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
include "core/strlen.nub"
|
||||
|
||||
let SYS_WRITE = 1;
|
||||
let STD_OUT = 1;
|
||||
let STD_ERR = 2;
|
||||
|
||||
@@ -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
0
Nub.Lang/Nub.Lang/output/build.sh
Normal file → Executable file
Reference in New Issue
Block a user