diff --git a/Nub.Lang/Nub.Lang/Backend/Custom/CustomGenerator.cs b/Nub.Lang/Nub.Lang/Backend/Custom/ExpressionGenerator.cs similarity index 55% rename from Nub.Lang/Nub.Lang/Backend/Custom/CustomGenerator.cs rename to Nub.Lang/Nub.Lang/Backend/Custom/ExpressionGenerator.cs index fb0b37c..0be4de6 100644 --- a/Nub.Lang/Nub.Lang/Backend/Custom/CustomGenerator.cs +++ b/Nub.Lang/Nub.Lang/Backend/Custom/ExpressionGenerator.cs @@ -3,220 +3,18 @@ using Nub.Lang.Frontend.Parsing; namespace Nub.Lang.Backend.Custom; -public class CustomGenerator +public class ExpressionGenerator { - private const string Entrypoint = "main"; - - private readonly IReadOnlyCollection _definitions; - private readonly SymbolTable _symbolTable; private readonly StringBuilder _builder; - private readonly Dictionary _strings; - private readonly HashSet _externFuncDefinitions; - private int _stringIndex; - private int _labelIndex; - - public CustomGenerator(IReadOnlyCollection definitions) + private readonly SymbolTable _symbolTable; + + public ExpressionGenerator(SymbolTable symbolTable, StringBuilder builder) { - _strings = []; - _definitions = definitions; - _builder = new StringBuilder(); - _externFuncDefinitions = ["strcmp"]; - _symbolTable = new SymbolTable(definitions.OfType().ToList()); - - foreach (var funcDefinitionNode in definitions.OfType()) - { - _symbolTable.DefineFunc(funcDefinitionNode); - _externFuncDefinitions.Add(_symbolTable.ResolveExternFunc(funcDefinitionNode.Name, funcDefinitionNode.Parameters.Select(p => p.Type).ToList()).StartLabel); - } - foreach (var funcDefinitionNode in definitions.OfType()) - { - _symbolTable.DefineFunc(funcDefinitionNode); - } - } - - public string Generate() - { - _builder.AppendLine("global _start"); - - foreach (var externFuncDefinition in _externFuncDefinitions) - { - _builder.AppendLine($"extern {externFuncDefinition}"); - } - - _builder.AppendLine(); - _builder.AppendLine("section .bss"); - foreach (var globalVariable in _definitions.OfType()) - { - var symbol = _symbolTable.ResolveGlobalVariable(globalVariable.Name); - _builder.AppendLine($" {symbol.Identifier}: resq 1 ; {globalVariable.Name}"); - } - - _builder.AppendLine(); - _builder.AppendLine("section .text"); - _builder.AppendLine("_start:"); - - var main = _symbolTable.ResolveLocalFunc(Entrypoint, []); - - _builder.AppendLine(" ; Initialize global variables"); - foreach (var globalVariable in _definitions.OfType()) - { - var symbol = _symbolTable.ResolveGlobalVariable(globalVariable.Name); - GenerateExpression(globalVariable.Value, main); - _builder.AppendLine($" mov [{symbol.Identifier}], rax"); - } - - _builder.AppendLine(); - _builder.AppendLine($" ; Call entrypoint {Entrypoint}"); - _builder.AppendLine($" call {main.StartLabel}"); - - _builder.AppendLine(); - _builder.AppendLine(main.ReturnType.HasValue - ? " mov rdi, rax ; Exit with return value of entrypoint" - : " mov rdi, 0 ; Exit with default status code 0"); - _builder.AppendLine(" mov rax, 60"); - _builder.AppendLine(" syscall"); - - foreach (var funcDefinition in _definitions.OfType()) - { - _builder.AppendLine(); - GenerateFuncDefinition(funcDefinition); - } - - _builder.AppendLine(); - _builder.AppendLine("section .data"); - foreach (var str in _strings) - { - _builder.AppendLine($"{str.Key}: db `{str.Value}`, 0"); - } - - return _builder.ToString(); - } - - private void GenerateFuncDefinition(LocalFuncDefinitionNode node) - { - var func = _symbolTable.ResolveLocalFunc(node.Name, node.Parameters.Select(p => p.Type).ToList()); - - _builder.AppendLine($"; {node.ToString()}"); - _builder.AppendLine($"{func.StartLabel}:"); - _builder.AppendLine(" ; Set up stack frame"); - _builder.AppendLine(" push rbp"); - _builder.AppendLine(" mov rbp, rsp"); - _builder.AppendLine($" sub rsp, {func.StackAllocation}"); - - string[] registers = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"]; - - _builder.AppendLine(" ; Body"); - for (var i = 0; i < func.Parameters.Count; i++) - { - var parameter = func.ResolveLocalVariable(func.Parameters.ElementAt(i).Name); - if (i < registers.Length) - { - _builder.AppendLine($" mov [rbp - {parameter.Offset}], {registers[i]}"); - } - else - { - var stackOffset = 16 + (i - registers.Length) * 8; - _builder.AppendLine($" mov rax, [rbp + {stackOffset}]"); - _builder.AppendLine($" mov [rbp - {parameter.Offset}], rax"); - } - } - - GenerateBlock(node.Body, func); - - _builder.AppendLine($"{func.EndLabel}:"); - _builder.AppendLine(" ; Clean up stack frame"); - _builder.AppendLine(" mov rsp, rbp"); - _builder.AppendLine(" pop rbp"); - _builder.AppendLine(" ret"); + _symbolTable = symbolTable; + _builder = builder; } - private void GenerateBlock(BlockNode block, LocalFunc func) - { - foreach (var statement in block.Statements) - { - GenerateStatement(statement, func); - } - } - - private void GenerateStatement(StatementNode statement, LocalFunc func) - { - switch (statement) - { - case FuncCallStatementNode funcCallStatement: - GenerateFuncCall(funcCallStatement.FuncCall, func); - break; - case IfNode ifStatement: - GenerateIf(ifStatement, func); - break; - case ReturnNode @return: - GenerateReturn(@return, func); - break; - case SyscallStatementNode syscallStatement: - GenerateSyscall(syscallStatement.Syscall, func); - break; - case VariableAssignmentNode variableAssignment: - GenerateVariableAssignment(variableAssignment, func); - break; - case VariableReassignmentNode variableReassignment: - GenerateVariableReassignment(variableReassignment, func); - break; - default: - throw new ArgumentOutOfRangeException(nameof(statement)); - } - } - - private void GenerateIf(IfNode ifStatement, LocalFunc func) - { - var endLabel = CreateLabel(); - GenerateIf(ifStatement, endLabel, func); - _builder.AppendLine($"{endLabel}:"); - } - - private void GenerateIf(IfNode ifStatement, string endLabel, LocalFunc func) - { - var nextLabel = CreateLabel(); - GenerateExpression(ifStatement.Condition, func); - _builder.AppendLine(" cmp rax, 0"); - _builder.AppendLine($" je {nextLabel}"); - GenerateBlock(ifStatement.Body, func); - _builder.AppendLine($" jmp {endLabel}"); - _builder.AppendLine($"{nextLabel}:"); - - if (ifStatement.Else.HasValue) - { - ifStatement.Else.Value.Match - ( - elseIfStatement => GenerateIf(elseIfStatement, endLabel, func), - elseStatement => GenerateBlock(elseStatement, func) - ); - } - } - - private void GenerateReturn(ReturnNode @return, LocalFunc func) - { - if (@return.Value.HasValue) - { - GenerateExpression(@return.Value.Value, func); - } - - _builder.AppendLine($" jmp {func.EndLabel}"); - } - - private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment, LocalFunc func) - { - var variable = func.ResolveLocalVariable(variableAssignment.Name); - GenerateExpression(variableAssignment.Value, func); - _builder.AppendLine($" mov [rbp - {variable.Offset}], rax"); - } - - private void GenerateVariableReassignment(VariableReassignmentNode variableReassignment, LocalFunc func) - { - var variable = func.ResolveLocalVariable(variableReassignment.Name); - GenerateExpression(variableReassignment.Value, func); - _builder.AppendLine($" mov [rbp - {variable.Offset}], rax"); - } - - private void GenerateExpression(ExpressionNode expression, LocalFunc func) + public void GenerateExpression(ExpressionNode expression, LocalFunc func) { switch (expression) { @@ -431,8 +229,8 @@ public class CustomGenerator } case StringType: { - var ident = $"string{++_stringIndex}"; - _strings.Add(ident, literal.Literal); + var ident = _symbolTable.LabelFactory.Create(); + _symbolTable.DefineString(ident, literal.Literal); _builder.AppendLine($" mov rax, {ident}"); break; } @@ -462,7 +260,7 @@ public class CustomGenerator } } - private void GenerateFuncCall(FuncCall funcCall, LocalFunc func) + public void GenerateFuncCall(FuncCall funcCall, LocalFunc func) { var symbol = _symbolTable.ResolveFunc(funcCall.Name, funcCall.Parameters.Select(p => p.Type).ToList()); string[] registers = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"]; @@ -487,8 +285,8 @@ public class CustomGenerator _builder.AppendLine($" add rsp, {stackParameters}"); } } - - private void GenerateSyscall(Syscall syscall, LocalFunc func) + + public void GenerateSyscall(Syscall syscall, LocalFunc func) { string[] registers = ["rax", "rdi", "rsi", "rdx", "r10", "r8", "r9"]; @@ -505,9 +303,4 @@ public class CustomGenerator _builder.AppendLine(" syscall"); } - - private string CreateLabel() - { - return $"label{++_labelIndex}"; - } } \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Backend/Custom/FuncGenerator.cs b/Nub.Lang/Nub.Lang/Backend/Custom/FuncGenerator.cs new file mode 100644 index 0000000..8acdfa4 --- /dev/null +++ b/Nub.Lang/Nub.Lang/Backend/Custom/FuncGenerator.cs @@ -0,0 +1,146 @@ +using System.Text; +using Nub.Lang.Frontend.Parsing; + +namespace Nub.Lang.Backend.Custom; + +public class FuncGenerator +{ + private readonly SymbolTable _symbolTable; + private readonly ExpressionGenerator _expressionGenerator; + private readonly StringBuilder _builder; + + public FuncGenerator(SymbolTable symbolTable, StringBuilder builder, ExpressionGenerator expressionGenerator) + { + _symbolTable = symbolTable; + _builder = builder; + _expressionGenerator = expressionGenerator; + } + + public string GenerateFuncDefinition(LocalFuncDefinitionNode node) + { + var func = _symbolTable.ResolveLocalFunc(node.Name, node.Parameters.Select(p => p.Type).ToList()); + + _builder.AppendLine($"; {node.ToString()}"); + _builder.AppendLine($"{func.StartLabel}:"); + _builder.AppendLine(" ; Set up stack frame"); + _builder.AppendLine(" push rbp"); + _builder.AppendLine(" mov rbp, rsp"); + _builder.AppendLine($" sub rsp, {func.StackAllocation}"); + + string[] registers = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"]; + + _builder.AppendLine(" ; Body"); + for (var i = 0; i < func.Parameters.Count; i++) + { + var parameter = func.ResolveLocalVariable(func.Parameters.ElementAt(i).Name); + if (i < registers.Length) + { + _builder.AppendLine($" mov [rbp - {parameter.Offset}], {registers[i]}"); + } + else + { + var stackOffset = 16 + (i - registers.Length) * 8; + _builder.AppendLine($" mov rax, [rbp + {stackOffset}]"); + _builder.AppendLine($" mov [rbp - {parameter.Offset}], rax"); + } + } + + GenerateBlock(node.Body, func); + + _builder.AppendLine($"{func.EndLabel}:"); + _builder.AppendLine(" ; Clean up stack frame"); + _builder.AppendLine(" mov rsp, rbp"); + _builder.AppendLine(" pop rbp"); + _builder.AppendLine(" ret"); + + var result = _builder.ToString(); + _builder.Clear(); + return result; + } + + private void GenerateBlock(BlockNode block, LocalFunc func) + { + foreach (var statement in block.Statements) + { + GenerateStatement(statement, func); + } + } + + private void GenerateStatement(StatementNode statement, LocalFunc func) + { + switch (statement) + { + case FuncCallStatementNode funcCallStatement: + _expressionGenerator.GenerateFuncCall(funcCallStatement.FuncCall, func); + break; + case IfNode ifStatement: + GenerateIf(ifStatement, func); + break; + case ReturnNode @return: + GenerateReturn(@return, func); + break; + case SyscallStatementNode syscallStatement: + _expressionGenerator.GenerateSyscall(syscallStatement.Syscall, func); + break; + case VariableAssignmentNode variableAssignment: + GenerateVariableAssignment(variableAssignment, func); + break; + case VariableReassignmentNode variableReassignment: + GenerateVariableReassignment(variableReassignment, func); + break; + default: + throw new ArgumentOutOfRangeException(nameof(statement)); + } + } + + private void GenerateIf(IfNode ifStatement, LocalFunc func) + { + var endLabel = _symbolTable.LabelFactory.Create(); + GenerateIf(ifStatement, endLabel, func); + _builder.AppendLine($"{endLabel}:"); + } + + private void GenerateIf(IfNode ifStatement, string endLabel, LocalFunc func) + { + var nextLabel = _symbolTable.LabelFactory.Create(); + _expressionGenerator.GenerateExpression(ifStatement.Condition, func); + _builder.AppendLine(" cmp rax, 0"); + _builder.AppendLine($" je {nextLabel}"); + GenerateBlock(ifStatement.Body, func); + _builder.AppendLine($" jmp {endLabel}"); + _builder.AppendLine($"{nextLabel}:"); + + if (ifStatement.Else.HasValue) + { + ifStatement.Else.Value.Match + ( + elseIfStatement => GenerateIf(elseIfStatement, endLabel, func), + elseStatement => GenerateBlock(elseStatement, func) + ); + } + } + + private void GenerateReturn(ReturnNode @return, LocalFunc func) + { + if (@return.Value.HasValue) + { + _expressionGenerator.GenerateExpression(@return.Value.Value, func); + } + + _builder.AppendLine($" jmp {func.EndLabel}"); + } + + private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment, LocalFunc func) + { + var variable = func.ResolveLocalVariable(variableAssignment.Name); + _expressionGenerator.GenerateExpression(variableAssignment.Value, func); + _builder.AppendLine($" mov [rbp - {variable.Offset}], rax"); + } + + private void GenerateVariableReassignment(VariableReassignmentNode variableReassignment, LocalFunc func) + { + var variable = func.ResolveLocalVariable(variableReassignment.Name); + _expressionGenerator.GenerateExpression(variableReassignment.Value, func); + _builder.AppendLine($" mov [rbp - {variable.Offset}], rax"); + } +} \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Backend/Custom/Generator.cs b/Nub.Lang/Nub.Lang/Backend/Custom/Generator.cs new file mode 100644 index 0000000..4789649 --- /dev/null +++ b/Nub.Lang/Nub.Lang/Backend/Custom/Generator.cs @@ -0,0 +1,123 @@ +using System.Text; +using Nub.Lang.Frontend.Parsing; + +namespace Nub.Lang.Backend.Custom; + +public class Generator +{ + private const string Entrypoint = "main"; + + private readonly List _definitions; + private readonly SymbolTable _symbolTable; + private readonly StringBuilder _builder; + private readonly HashSet _externFuncDefinitions; + private readonly ExpressionGenerator _expressionGenerator; + private readonly FuncGenerator _funcGenerator; + + public Generator(FileNode file, Dictionary deps) + { + _definitions = []; + _builder = new StringBuilder(); + _externFuncDefinitions = ["strcmp"]; + _symbolTable = new SymbolTable(); + _expressionGenerator = new ExpressionGenerator(_symbolTable, _builder); + _funcGenerator = new FuncGenerator(_symbolTable, _builder, _expressionGenerator); + + ResolveGlobalVariables(file, deps); + ResolveDefinitions(file, deps); + } + + private void ResolveGlobalVariables(FileNode file, Dictionary deps) + { + + foreach (var globalVariableDefinition in file.Definitions.OfType()) + { + _symbolTable.DefineGlobalVariable(globalVariableDefinition); + _definitions.Add(globalVariableDefinition); + } + + foreach (var include in file.Includes) + { + ResolveGlobalVariables(deps[include], deps); + } + } + + private void ResolveDefinitions(FileNode file, Dictionary deps) + { + foreach (var funcDefinitionNode in file.Definitions.OfType()) + { + _symbolTable.DefineFunc(funcDefinitionNode); + _definitions.Add(funcDefinitionNode); + _externFuncDefinitions.Add(_symbolTable.ResolveExternFunc(funcDefinitionNode.Name, funcDefinitionNode.Parameters.Select(p => p.Type).ToList()).StartLabel); + } + + foreach (var funcDefinitionNode in file.Definitions.OfType()) + { + _symbolTable.DefineFunc(funcDefinitionNode); + _definitions.Add(funcDefinitionNode); + } + + foreach (var include in file.Includes) + { + ResolveDefinitions(deps[include], deps); + } + } + + public string Generate() + { + _builder.AppendLine("global _start"); + + foreach (var externFuncDefinition in _externFuncDefinitions) + { + _builder.AppendLine($"extern {externFuncDefinition}"); + } + + _builder.AppendLine(); + _builder.AppendLine("section .bss"); + foreach (var globalVariable in _definitions.OfType()) + { + var symbol = _symbolTable.ResolveGlobalVariable(globalVariable.Name); + _builder.AppendLine($" {symbol.Identifier}: resq 1 ; {globalVariable.Name}"); + } + + _builder.AppendLine(); + _builder.AppendLine("section .text"); + _builder.AppendLine("_start:"); + + var main = _symbolTable.ResolveLocalFunc(Entrypoint, []); + + _builder.AppendLine(" ; Initialize global variables"); + foreach (var globalVariable in _definitions.OfType()) + { + var symbol = _symbolTable.ResolveGlobalVariable(globalVariable.Name); + _expressionGenerator.GenerateExpression(globalVariable.Value, main); + _builder.AppendLine($" mov [{symbol.Identifier}], rax"); + } + + _builder.AppendLine(); + _builder.AppendLine($" ; Call entrypoint {Entrypoint}"); + _builder.AppendLine($" call {main.StartLabel}"); + + _builder.AppendLine(); + _builder.AppendLine(main.ReturnType.HasValue + ? " mov rdi, rax ; Exit with return value of entrypoint" + : " mov rdi, 0 ; Exit with default status code 0"); + _builder.AppendLine(" mov rax, 60"); + _builder.AppendLine(" syscall"); + + foreach (var funcDefinition in _definitions.OfType()) + { + _builder.AppendLine(); + _builder.AppendLine(_funcGenerator.GenerateFuncDefinition(funcDefinition)); + } + + _builder.AppendLine(); + _builder.AppendLine("section .data"); + foreach (var str in _symbolTable.Strings) + { + _builder.AppendLine($"{str.Key}: db `{str.Value}`, 0"); + } + + return _builder.ToString(); + } +} \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Backend/Custom/SymbolTable.cs b/Nub.Lang/Nub.Lang/Backend/Custom/SymbolTable.cs index d1dd1e8..90ddc92 100644 --- a/Nub.Lang/Nub.Lang/Backend/Custom/SymbolTable.cs +++ b/Nub.Lang/Nub.Lang/Backend/Custom/SymbolTable.cs @@ -7,16 +7,20 @@ public class SymbolTable { private readonly List _funcDefinitions = []; private readonly List _globalVariables = []; - private int _labelIndex; + private int _globalVariableIndex; + public LabelFactory LabelFactory { get; } = new(); + + public readonly Dictionary Strings = []; - public SymbolTable(IReadOnlyCollection globalVariableDefinitions) + public void DefineString(string label, string value) { - var globalVariableIndex = 0; - foreach (var globalVariable in globalVariableDefinitions) - { - var identifier = $"variable{++globalVariableIndex}"; - _globalVariables.Add(new GlobalVariable(globalVariable.Name, globalVariable.Value.Type, identifier)); - } + Strings.Add(label, value); + } + + public void DefineGlobalVariable(GlobalVariableDefinitionNode globalVariableDefinition) + { + var identifier = $"variable{++_globalVariableIndex}"; + _globalVariables.Add(new GlobalVariable(globalVariableDefinition.Name, globalVariableDefinition.Value.Type, identifier)); } public void DefineFunc(ExternFuncDefinitionNode externFuncDefinition) @@ -52,8 +56,8 @@ public class SymbolTable throw new Exception($"Func {existing} is already defined"); } - var startLabel = $"func{++_labelIndex}"; - var endLabel = $"func_end{_labelIndex}"; + var startLabel = LabelFactory.Create(); + var endLabel = LabelFactory.Create(); _funcDefinitions.Add(new LocalFunc(localFuncDefinition.Name, startLabel, endLabel, localFuncDefinition.Parameters, localFuncDefinition.ReturnType, _globalVariables.Concat(ResolveFuncVariables(localFuncDefinition)).ToList())); } @@ -209,4 +213,11 @@ public class LocalFunc : Func } public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}"; +} + +public class LabelFactory +{ + private int _index; + + public string Create() => $"label{++_index}"; } \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Frontend/Lexing/Lexer.cs b/Nub.Lang/Nub.Lang/Frontend/Lexing/Lexer.cs index 8645751..488e217 100644 --- a/Nub.Lang/Nub.Lang/Frontend/Lexing/Lexer.cs +++ b/Nub.Lang/Nub.Lang/Frontend/Lexing/Lexer.cs @@ -9,6 +9,7 @@ public class Lexer ["func"] = Symbol.Func, ["extern"] = Symbol.Extern, ["return"] = Symbol.Return, + ["include"] = Symbol.Include, ["let"] = Symbol.Let, ["if"] = Symbol.If, ["else"] = Symbol.Else, diff --git a/Nub.Lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs b/Nub.Lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs index 4f8595f..e19f67e 100644 --- a/Nub.Lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs +++ b/Nub.Lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs @@ -8,6 +8,7 @@ public class SymbolToken(Symbol symbol) : Token public enum Symbol { Whitespace, + Include, Extern, Func, Return, diff --git a/Nub.Lang/Nub.Lang/Frontend/Parsing/FileNode.cs b/Nub.Lang/Nub.Lang/Frontend/Parsing/FileNode.cs new file mode 100644 index 0000000..57497b9 --- /dev/null +++ b/Nub.Lang/Nub.Lang/Frontend/Parsing/FileNode.cs @@ -0,0 +1,7 @@ +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/Parser.cs b/Nub.Lang/Nub.Lang/Frontend/Parsing/Parser.cs index 79456f6..76addf0 100644 --- a/Nub.Lang/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/Nub.Lang/Nub.Lang/Frontend/Parsing/Parser.cs @@ -14,16 +14,30 @@ public class Parser _tokens = tokens.ToArray(); } - public IReadOnlyCollection Parse() + public FileNode ParseFile(string relativePath) { _index = 0; 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); + } + while (Peek().HasValue) { definitions.Add(ParseDefinition()); } - return definitions; + return new FileNode(includes, definitions); } private DefinitionNode ParseDefinition() diff --git a/Nub.Lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs b/Nub.Lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs index 151ca39..acc41ce 100644 --- a/Nub.Lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs +++ b/Nub.Lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs @@ -13,25 +13,39 @@ public class Func(string name, IReadOnlyCollection parameters, Op public class ExpressionTyper { - private readonly IReadOnlyCollection _functions; - private readonly IReadOnlyCollection _variableDefinitions; + private readonly List _functions; + private readonly List _variableDefinitions; private readonly Stack _variables; - public ExpressionTyper(IReadOnlyCollection definitions) + public ExpressionTyper(FileNode file, Dictionary deps) { - var functions = definitions + _variables = new Stack(); + _functions = []; + _variableDefinitions = []; + + ResolveFunctions(file, deps); + } + + private void ResolveFunctions(FileNode file, Dictionary deps) + { + var functions = file.Definitions .OfType() .Select(f => new Func(f.Name, f.Parameters, f.Body, f.ReturnType)) .ToList(); - var externFunctions = definitions + var externFunctions = file.Definitions .OfType() .Select(f => new Func(f.Name, f.Parameters, Optional.Empty(), f.ReturnType)) .ToList(); - _functions = functions.Concat(externFunctions).ToList(); - _variableDefinitions = definitions.OfType().ToList(); - _variables = new Stack(); + _functions.AddRange(functions); + _functions.AddRange(externFunctions); + _variableDefinitions.AddRange(file.Definitions.OfType()); + + foreach (var include in file.Includes) + { + ResolveFunctions(deps[include], deps); + } } public void Populate() diff --git a/Nub.Lang/Nub.Lang/Program.cs b/Nub.Lang/Nub.Lang/Program.cs index 269d5b0..9517963 100644 --- a/Nub.Lang/Nub.Lang/Program.cs +++ b/Nub.Lang/Nub.Lang/Program.cs @@ -1,20 +1,46 @@ -using Nub.Lang.Backend.Custom; +using System.Diagnostics; +using Nub.Lang.Backend.Custom; using Nub.Lang.Frontend.Lexing; using Nub.Lang.Frontend.Parsing; using Nub.Lang.Frontend.Typing; -var src = File.ReadAllText(args[0]); +var rootPath = Path.GetDirectoryName(args[0]); +var rootFileName = Path.GetFileName(args[0]); +Debug.Assert(rootPath != null && rootFileName != null); -var lexer = new Lexer(src); -var tokens = lexer.Lex(); +Dictionary files = []; -var parser = new Parser(tokens); -var definitions = parser.Parse(); +Queue queue = []; +queue.Enqueue(rootFileName); -var typer = new ExpressionTyper(definitions); -typer.Populate(); +while (queue.TryDequeue(out var 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 generator = new CustomGenerator(definitions); + foreach (var include in file.Includes) + { + if (!files.ContainsKey(include)) + { + queue.Enqueue(include); + } + } +} + +foreach (var file in files) +{ + var typer = new ExpressionTyper(file.Value, files); + typer.Populate(); +} + + +var generator = new Generator(files[rootFileName], files); var asm = generator.Generate(); Console.WriteLine(asm); diff --git a/Nub.Lang/Nub.Lang/input/core/print.nub b/Nub.Lang/Nub.Lang/input/core/print.nub index da920a2..a5784ff 100644 --- a/Nub.Lang/Nub.Lang/input/core/print.nub +++ b/Nub.Lang/Nub.Lang/input/core/print.nub @@ -1,4 +1,4 @@ -namespace core; +include "core/string.nub" let SYS_WRITE = 1; let STD_IN = 0; diff --git a/Nub.Lang/Nub.Lang/input/core/string/strcmp.asm b/Nub.Lang/Nub.Lang/input/core/string.asm similarity index 64% rename from Nub.Lang/Nub.Lang/input/core/string/strcmp.asm rename to Nub.Lang/Nub.Lang/input/core/string.asm index b5035e6..5b815d4 100644 --- a/Nub.Lang/Nub.Lang/input/core/string/strcmp.asm +++ b/Nub.Lang/Nub.Lang/input/core/string.asm @@ -1,4 +1,5 @@ global strlen +global strcmp section .text strcmp: @@ -17,4 +18,15 @@ strcmp: ret .equal: mov rax, 1 + ret + +strlen: + xor rax, rax +.loop: + cmp byte [rdi], 0 + jz .done + inc rax + inc rdi + jmp .loop +.done: ret \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/input/core/string/strcmp.nub b/Nub.Lang/Nub.Lang/input/core/string.nub similarity index 52% rename from Nub.Lang/Nub.Lang/input/core/string/strcmp.nub rename to Nub.Lang/Nub.Lang/input/core/string.nub index aa81777..32f47ea 100644 --- a/Nub.Lang/Nub.Lang/input/core/string/strcmp.nub +++ b/Nub.Lang/Nub.Lang/input/core/string.nub @@ -1,3 +1,2 @@ -namespace core; - +extern func strlen(msg: String): int64; extern func strcmp(a: String, b: String): bool; diff --git a/Nub.Lang/Nub.Lang/input/core/string/strlen.asm b/Nub.Lang/Nub.Lang/input/core/string/strlen.asm deleted file mode 100644 index dcd0a3c..0000000 --- a/Nub.Lang/Nub.Lang/input/core/string/strlen.asm +++ /dev/null @@ -1,13 +0,0 @@ -global strlen -section .text - -strlen: - xor rax, rax -.loop: - cmp byte [rdi], 0 - jz .done - inc rax - inc rdi - jmp .loop -.done: - ret \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/input/core/string/strlen.nub b/Nub.Lang/Nub.Lang/input/core/string/strlen.nub deleted file mode 100644 index eb59ffa..0000000 --- a/Nub.Lang/Nub.Lang/input/core/string/strlen.nub +++ /dev/null @@ -1,3 +0,0 @@ -namespace core; - -extern func strlen(msg: String): int64; diff --git a/Nub.Lang/Nub.Lang/input/program.nub b/Nub.Lang/Nub.Lang/input/program.nub index 7e41667..cb94a8e 100644 --- a/Nub.Lang/Nub.Lang/input/program.nub +++ b/Nub.Lang/Nub.Lang/input/program.nub @@ -1,17 +1,16 @@ -namespace program; -using core; +include "core/print.nub"; func main() { - core::println("test"); - core::println(true); + println("test"); + println(true); - if true { - core::println("1"); + if strlen("1") == 1 { + println("1"); } else if false { - core::println("2"); + println("2"); } else if true { - core::println("3"); + println("3"); } else { - core::println("4"); + println("4"); } } diff --git a/Nub.Lang/Nub.Lang/output/build.sh b/Nub.Lang/Nub.Lang/output/build.sh index 4acf5dc..bc12275 100644 --- a/Nub.Lang/Nub.Lang/output/build.sh +++ b/Nub.Lang/Nub.Lang/output/build.sh @@ -1,6 +1,5 @@ #!/bin/sh nasm -g -felf64 out.asm -o out.o -nasm -g -felf64 ../input/core/string/strlen.asm -o strlen.o -nasm -g -felf64 ../input/core/string/strcmp.asm -o strcmp.o +nasm -g -felf64 ../input/core/string.asm -o string.o -ld -o out out.o strlen.o strcmp.o +ld -o out out.o string.o