From e919d817379e371ce243a3e61615d84e366c7c24 Mon Sep 17 00:00:00 2001 From: nub31 Date: Thu, 12 Jun 2025 22:49:58 +0200 Subject: [PATCH] Cleanup --- build.sh | 9 +- src/lang/Nub.Lang.CLI/Program.cs | 36 ++- .../Nub.Lang/Diagnostics/ConsoleColors.cs | 2 - src/lang/Nub.Lang/Frontend/DefinitionTable.cs | 43 +++ .../Frontend/Generation/QBEGenerator.cs | 233 +++++++++------- .../Frontend/Generation/SymbolTable.cs | 109 -------- src/lang/Nub.Lang/Frontend/Lexing/Lexer.cs | 20 +- .../{SourceFile.cs => CompilationUnit.cs} | 2 +- .../Parsing/Definitions/DefinitionNode.cs | 5 +- .../Parsing/Definitions/FuncDefinitionNode.cs | 7 +- .../Definitions/StructDefinitionNode.cs | 3 +- .../Parsing/Expressions/IdentifierNode.cs | 6 +- src/lang/Nub.Lang/Frontend/Parsing/Parser.cs | 121 ++++---- .../Nub.Lang/Frontend/Typing/TypeChecker.cs | 260 ++++++++---------- src/lang/Nub.Lang/Optional.cs | 15 + 15 files changed, 413 insertions(+), 458 deletions(-) create mode 100644 src/lang/Nub.Lang/Frontend/DefinitionTable.cs delete mode 100644 src/lang/Nub.Lang/Frontend/Generation/SymbolTable.cs rename src/lang/Nub.Lang/Frontend/Parsing/{SourceFile.cs => CompilationUnit.cs} (71%) diff --git a/build.sh b/build.sh index 1b5d5c6..5d7c77b 100755 --- a/build.sh +++ b/build.sh @@ -6,9 +6,12 @@ dotnet build src/lang/Nub.Lang.CLI mkdir -p bin-int bin rm -rf bin-int/* bin/* -nub example > bin-int/out.ssa -qbe bin-int/out.ssa > bin-int/out.s -as -o bin-int/out.o bin-int/out.s +nub example + +find bin-int -name '*.ssa' | while read -r file; do + qbe "$file" > "bin-int/$(basename "${file}" .ssa).s" + as "bin-int/$(basename "${file}" .ssa).s" -o "bin-int/$(basename "${file}" .ssa).o" +done find src/runtime -name '*.s' | while read -r file; do as "$file" -o "bin-int/$(basename "${file}" .s).o" diff --git a/src/lang/Nub.Lang.CLI/Program.cs b/src/lang/Nub.Lang.CLI/Program.cs index 98d76fd..4987893 100644 --- a/src/lang/Nub.Lang.CLI/Program.cs +++ b/src/lang/Nub.Lang.CLI/Program.cs @@ -1,4 +1,5 @@ using Nub.Lang; +using Nub.Lang.Frontend; using Nub.Lang.Frontend.Generation; using Nub.Lang.Frontend.Lexing; using Nub.Lang.Frontend.Parsing; @@ -20,38 +21,43 @@ if (!Directory.Exists(srcDir)) } var error = false; -var lexer = new Lexer(); -var parser = new Parser(); -var typeChecker = new TypeChecker(); -List files = []; +List compilationUnits = []; + foreach (var file in Directory.EnumerateFiles(srcDir, "*.nub", SearchOption.AllDirectories)) { var content = File.ReadAllText(file); - var tokenizeResult = lexer.Tokenize(new SourceText(file, content)); + var tokenizeResult = Lexer.Tokenize(new SourceText(file, content)); tokenizeResult.PrintAllDiagnostics(); error = error || tokenizeResult.HasErrors; - var parseResult = parser.ParseModule(tokenizeResult.Value); + var parseResult = Parser.ParseFile(tokenizeResult.Value); parseResult.PrintAllDiagnostics(); error = error || parseResult.HasErrors; if (parseResult.Value != null) { - files.Add(parseResult.Value); + compilationUnits.Add(parseResult.Value); } } -var typeCheckResult = typeChecker.TypeCheck(files); -typeCheckResult.PrintAllDiagnostics(); -error = error || typeCheckResult.HasErrors; +if (error) +{ + return 1; +} -if (error) return 1; +var definitionTable = new DefinitionTable(compilationUnits); -var generator = new QBEGenerator(); -var result = generator.Generate(files); +foreach (var compilationUnit in compilationUnits) +{ + var typeCheckResult = TypeChecker.Check(compilationUnit, definitionTable); + typeCheckResult.PrintAllDiagnostics(); + error = error || typeCheckResult.HasErrors; -Console.Out.Write(result); + var result = QBEGenerator.Generate(compilationUnit, definitionTable); -return 0; \ No newline at end of file + File.WriteAllText($"bin-int/{Guid.NewGuid():N}.ssa", result); +} + +return error ? 1 : 0; \ No newline at end of file diff --git a/src/lang/Nub.Lang/Diagnostics/ConsoleColors.cs b/src/lang/Nub.Lang/Diagnostics/ConsoleColors.cs index 24f0d25..c1d05a0 100644 --- a/src/lang/Nub.Lang/Diagnostics/ConsoleColors.cs +++ b/src/lang/Nub.Lang/Diagnostics/ConsoleColors.cs @@ -46,8 +46,6 @@ public static class ConsoleColors public const string BrightCyan = "\e[96m"; public const string BrightWhite = "\e[97m"; - private static readonly Lexer Lexer = new(); - private static bool IsColorSupported() { var term = Environment.GetEnvironmentVariable("TERM"); diff --git a/src/lang/Nub.Lang/Frontend/DefinitionTable.cs b/src/lang/Nub.Lang/Frontend/DefinitionTable.cs new file mode 100644 index 0000000..edb0615 --- /dev/null +++ b/src/lang/Nub.Lang/Frontend/DefinitionTable.cs @@ -0,0 +1,43 @@ +using Nub.Lang.Frontend.Parsing; +using Nub.Lang.Frontend.Parsing.Definitions; + +namespace Nub.Lang.Frontend; + +public class DefinitionTable +{ + private readonly IEnumerable _compilationUnits; + + public DefinitionTable(IEnumerable compilationUnits) + { + _compilationUnits = compilationUnits; + } + + public Optional LookupFunc(string @namespace, string name) + { + var definition = _compilationUnits + .Where(c => c.Namespace == @namespace) + .SelectMany(c => c.Definitions) + .OfType() + .SingleOrDefault(f => f.Name == name); + + return Optional.OfNullable(definition); + } + + public Optional LookupStruct(string @namespace, string name) + { + var definition = _compilationUnits + .Where(c => c.Namespace == @namespace) + .SelectMany(c => c.Definitions) + .OfType() + .SingleOrDefault(f => f.Name == name); + + return Optional.OfNullable(definition); + } + + public IEnumerable GetStructs() + { + return _compilationUnits + .SelectMany(c => c.Definitions) + .OfType(); + } +} \ No newline at end of file diff --git a/src/lang/Nub.Lang/Frontend/Generation/QBEGenerator.cs b/src/lang/Nub.Lang/Frontend/Generation/QBEGenerator.cs index d2a9206..ab6111b 100644 --- a/src/lang/Nub.Lang/Frontend/Generation/QBEGenerator.cs +++ b/src/lang/Nub.Lang/Frontend/Generation/QBEGenerator.cs @@ -10,52 +10,57 @@ using Nub.Lang.Frontend.Typing; namespace Nub.Lang.Frontend.Generation; -public class QBEGenerator +public static class QBEGenerator { private const string OutOfBoundsMessage = "Index is out of bounds\\n"; - private List _sourceFiles = []; - private StringBuilder _builder = new(); - private List _strings = []; - private Stack _breakLabels = []; - private Stack _continueLabels = []; - private int _variableIndex; - private int _labelIndex; - private bool _codeIsReachable = true; - private Dictionary _anonymousFunctions = []; - private int _anonymousFuncIndex; - private SymbolTable _symbolTable = new([]); + private static CompilationUnit _compilationUnit = null!; + private static DefinitionTable _definitionTable = null!; - public string Generate(List sourceFiles) + private static StringBuilder _builder = new(); + private static List _strings = []; + private static Stack _breakLabels = []; + private static Stack _continueLabels = []; + private static Queue<(AnonymousFuncNode Func, string Name)> _anonymousFunctions = []; + private static Stack<(string Name, string Pointer)> _variables = []; + private static Stack _variableScopes = []; + private static int _variableIndex; + private static int _labelIndex; + private static int _anonymousFuncIndex; + private static bool _codeIsReachable = true; + + public static string Generate(CompilationUnit compilationUnit, DefinitionTable definitionTable) { - _sourceFiles = sourceFiles; + _compilationUnit = compilationUnit; + _definitionTable = definitionTable; + _builder = new StringBuilder(); _strings = []; _breakLabels = []; _continueLabels = []; _anonymousFunctions = []; + _variables = []; + _variableScopes = []; _variableIndex = 0; _labelIndex = 0; _anonymousFuncIndex = 0; _codeIsReachable = true; - _symbolTable = new SymbolTable(_sourceFiles.SelectMany(f => f.Definitions.OfType())); - foreach (var structDef in _sourceFiles.SelectMany(f => f.Definitions).OfType()) + foreach (var structDef in _definitionTable.GetStructs()) { GenerateStructDefinition(structDef); _builder.AppendLine(); } - foreach (var funcDef in _sourceFiles.SelectMany(f => f.Definitions).OfType()) + foreach (var funcDef in _compilationUnit.Definitions.OfType()) { - var symbol = _symbolTable.LookupFunc(funcDef.Namespace, funcDef.Name); - GenerateFuncDefinition(symbol.GeneratedName, funcDef.Parameters, funcDef.ReturnType, funcDef.Body, funcDef.Exported); + GenerateFuncDefinition(FuncName(funcDef), funcDef.Parameters, funcDef.ReturnType, funcDef.Body, funcDef.Exported); _builder.AppendLine(); } - - foreach (var (func, name) in _anonymousFunctions) + + while (_anonymousFunctions.TryDequeue(out var anon)) { - GenerateFuncDefinition(name, func.Parameters, func.ReturnType, func.Body, false); + GenerateFuncDefinition(anon.Name, anon.Func.Parameters, anon.Func.ReturnType, anon.Func.Body, false); _builder.AppendLine(); } @@ -70,6 +75,18 @@ public class QBEGenerator return _builder.ToString(); } + private static string FuncName(IFuncSignature func) + { + return func switch + { + ExternFuncDefinitionNode externFuncDefinition => $"${externFuncDefinition.CallName}", + LocalFuncDefinitionNode localFuncDefinition => localFuncDefinition.Exported + ? $"${localFuncDefinition.Name}" + : $"${localFuncDefinition.Namespace}_{localFuncDefinition.Name}", + _ => throw new ArgumentOutOfRangeException(nameof(func)) + }; + } + private static string QBEStore(NubType type) { return $"store{type switch @@ -154,7 +171,7 @@ public class QBEGenerator }}"; } - private int AlignmentOf(NubType type) + private static int AlignmentOf(NubType type) { switch (type) { @@ -183,7 +200,7 @@ public class QBEGenerator } case NubStructType nubStructType: { - var definition = LookupStructDefinition(nubStructType.Namespace, nubStructType.Name); + var definition = _definitionTable.LookupStruct(nubStructType.Namespace, nubStructType.Name).GetValue(); return definition.Fields.Max(f => AlignmentOf(f.Type)); } case NubPointerType: @@ -203,12 +220,12 @@ public class QBEGenerator } } - private int AlignTo(int offset, int alignment) + private static int AlignTo(int offset, int alignment) { return (offset + alignment - 1) & ~(alignment - 1); } - private int SizeOf(NubType type) + private static int SizeOf(NubType type) { switch (type) { @@ -239,7 +256,7 @@ public class QBEGenerator } case NubStructType nubStructType: { - var definition = LookupStructDefinition(nubStructType.Namespace, nubStructType.Name); + var definition = _definitionTable.LookupStruct(nubStructType.Namespace, nubStructType.Name).GetValue(); var size = 0; var maxAlignment = 1; @@ -275,7 +292,7 @@ public class QBEGenerator } } - private bool IsLargeType(NubType type) + private static bool IsLargeType(NubType type) { return type switch { @@ -289,10 +306,11 @@ public class QBEGenerator }; } - private void GenerateFuncDefinition(string name, List parameters, NubType returnType, BlockNode body, bool exported) + private static void GenerateFuncDefinition(string name, List parameters, NubType returnType, BlockNode body, bool exported) { - _symbolTable.Reset(); - + _variables.Clear(); + _variableScopes.Clear(); + if (exported) { _builder.Append("export "); @@ -358,6 +376,8 @@ public class QBEGenerator _builder.AppendLine($"({string.Join(", ", parameterStrings)}) {{"); _builder.AppendLine("@start"); + List<(string Name, string Pointer)> parameterVars = []; + foreach (var parameter in parameters) { var parameterName = "%" + parameter.Name; @@ -385,10 +405,10 @@ public class QBEGenerator } } - _symbolTable.DeclareVariable(parameter.Name, parameterName); + parameterVars.Add((parameter.Name, parameterName)); } - GenerateBlock(body); + GenerateBlock(body, parameterVars); if (body.Statements.LastOrDefault() is not ReturnNode) { @@ -401,7 +421,7 @@ public class QBEGenerator _builder.AppendLine("}"); } - private void GenerateStructDefinition(StructDefinitionNode structDefinition) + private static void GenerateStructDefinition(StructDefinitionNode structDefinition) { _builder.Append($"type :{structDefinition.Namespace}_{structDefinition.Name} = {{ "); foreach (var structDefinitionField in structDefinition.Fields) @@ -435,7 +455,7 @@ public class QBEGenerator _builder.AppendLine("}"); } - private void GenerateStatement(StatementNode statement) + private static void GenerateStatement(StatementNode statement) { switch (statement) { @@ -477,7 +497,7 @@ public class QBEGenerator } } - private void GenerateArrayIndexAssignment(ArrayIndexAssignmentNode arrayIndexAssignment) + private static void GenerateArrayIndexAssignment(ArrayIndexAssignmentNode arrayIndexAssignment) { var array = GenerateExpression(arrayIndexAssignment.ArrayIndexAccess.Array); var index = GenerateExpression(arrayIndexAssignment.ArrayIndexAccess.Index); @@ -521,38 +541,51 @@ public class QBEGenerator } } - private void GenerateBlock(BlockNode block) + private static void GenerateBlock(BlockNode block, List<(string Name, string Pointer)>? variables = null) { - _symbolTable.StartScope(); + _variableScopes.Push(_variables.Count); + if (variables != null) + { + foreach (var variable in variables) + { + _variables.Push(variable); + } + } + foreach (var statement in block.Statements.Where(_ => _codeIsReachable)) { GenerateStatement(statement); } - _symbolTable.EndScope(); + + var count = _variableScopes.Pop(); + while (_variableScopes.Count > count) + { + _variableScopes.Pop(); + } _codeIsReachable = true; } - private void GenerateBreak() + private static void GenerateBreak() { _builder.AppendLine($" jmp {_breakLabels.Peek()}"); _codeIsReachable = false; } - private void GenerateContinue() + private static void GenerateContinue() { _builder.AppendLine($" jmp {_continueLabels.Peek()}"); _codeIsReachable = false; } - private void GenerateDereferenceAssignment(DereferenceAssignmentNode dereferenceAssignment) + private static void GenerateDereferenceAssignment(DereferenceAssignmentNode dereferenceAssignment) { var pointer = GenerateExpression(dereferenceAssignment.Dereference.Expression); var value = GenerateExpression(dereferenceAssignment.Value); GenerateCopy(dereferenceAssignment.Value.Type, value, pointer); } - private void GenerateIf(IfNode ifStatement) + private static void GenerateIf(IfNode ifStatement) { var trueLabel = GenLabelName(); var falseLabel = GenLabelName(); @@ -568,20 +601,20 @@ public class QBEGenerator { ifStatement.Else.Value.Match ( - GenerateIf, - GenerateBlock + elseIfNode => GenerateIf(elseIfNode), + elseNode => GenerateBlock(elseNode) ); } _builder.AppendLine(endLabel); } - private void GenerateMemberAssignment(MemberAssignmentNode memberAssignment) + private static void GenerateMemberAssignment(MemberAssignmentNode memberAssignment) { var structType = memberAssignment.MemberAccess.Expression.Type as NubStructType; Debug.Assert(structType != null); - var structDefinition = LookupStructDefinition(structType.Namespace, structType.Name); + var structDefinition = _definitionTable.LookupStruct(structType.Namespace, structType.Name).GetValue(); var offset = LookupMemberOffset(structDefinition, memberAssignment.MemberAccess.Member); var item = GenerateExpression(memberAssignment.MemberAccess.Expression); @@ -594,7 +627,7 @@ public class QBEGenerator GenerateCopy(memberAssignment.Value.Type, value, pointer); } - private void GenerateReturn(ReturnNode @return) + private static void GenerateReturn(ReturnNode @return) { if (@return.Value.HasValue) { @@ -607,7 +640,7 @@ public class QBEGenerator } } - private void GenerateVariableDeclaration(VariableDeclarationNode variableDeclaration) + private static void GenerateVariableDeclaration(VariableDeclarationNode variableDeclaration) { var type = variableDeclaration.ExplicitType.Value ?? variableDeclaration.Value.Value!.Type; var pointer = GenVarName(); @@ -621,20 +654,20 @@ public class QBEGenerator else { var pointerName = GenVarName(); - _symbolTable.DeclareVariable(variableDeclaration.Name, pointerName); + _variables.Push((variableDeclaration.Name, pointerName)); } - _symbolTable.DeclareVariable(variableDeclaration.Name, pointer); + _variables.Push((variableDeclaration.Name, pointer)); } - private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment) + private static void GenerateVariableAssignment(VariableAssignmentNode variableAssignment) { var value = GenerateExpression(variableAssignment.Value); - var variable = _symbolTable.LookupVariable(variableAssignment.Identifier.Name); + var variable = _variables.Single(x => x.Name == variableAssignment.Identifier.Name); GenerateCopy(variableAssignment.Value.Type, value, variable.Pointer); } - private void GenerateWhile(WhileNode whileStatement) + private static void GenerateWhile(WhileNode whileStatement) { var conditionLabel = GenLabelName(); var iterationLabel = GenLabelName(); @@ -655,7 +688,7 @@ public class QBEGenerator _breakLabels.Pop(); } - private string GenerateExpression(ExpressionNode expression) + private static string GenerateExpression(ExpressionNode expression) { return expression switch { @@ -676,17 +709,17 @@ public class QBEGenerator }; } - private string GenerateAnonymousFunc(AnonymousFuncNode anonymousFunc) + private static string GenerateAnonymousFunc(AnonymousFuncNode anonymousFunc) { var name = $"$anon_func{++_anonymousFuncIndex}"; - _anonymousFunctions[anonymousFunc] = name; + _anonymousFunctions.Enqueue((anonymousFunc, name)); var pointer = GenVarName(); _builder.AppendLine($" {pointer} =l alloc8 8"); _builder.AppendLine($" storel {name}, {pointer}"); return pointer; } - private string GenerateArrayIndexPointer(ArrayIndexAccessNode arrayIndexAccess) + private static string GenerateArrayIndexPointer(ArrayIndexAccessNode arrayIndexAccess) { var array = GenerateExpression(arrayIndexAccess.Array); var index = GenerateExpression(arrayIndexAccess.Index); @@ -721,7 +754,7 @@ public class QBEGenerator } } - private string GenerateArrayAccessIndex(ArrayIndexAccessNode arrayIndexAccess) + private static string GenerateArrayAccessIndex(ArrayIndexAccessNode arrayIndexAccess) { var pointerName = GenerateArrayIndexPointer(arrayIndexAccess); @@ -737,7 +770,7 @@ public class QBEGenerator } } - private void GenerateArrayBoundsCheck(string array, string index) + private static void GenerateArrayBoundsCheck(string array, string index) { var countName = GenVarName(); _builder.AppendLine($" {countName} =l loadl {array}"); @@ -761,7 +794,7 @@ public class QBEGenerator _builder.AppendLine(notOobLabel); } - private string GenerateArrayInitializer(ArrayInitializerNode arrayInitializer) + private static string GenerateArrayInitializer(ArrayInitializerNode arrayInitializer) { var capacity = GenerateExpression(arrayInitializer.Capacity); var elementSize = SizeOf(arrayInitializer.ElementType); @@ -780,7 +813,7 @@ public class QBEGenerator return outputName; } - private string GenerateDereference(DereferenceNode dereference) + private static string GenerateDereference(DereferenceNode dereference) { var result = GenerateExpression(dereference.Expression); var outputName = GenVarName(); @@ -788,7 +821,7 @@ public class QBEGenerator return outputName; } - private string GenerateAddressOf(AddressOfNode addressOf) + private static string GenerateAddressOf(AddressOfNode addressOf) { switch (addressOf.Expression) { @@ -797,11 +830,11 @@ public class QBEGenerator case DereferenceNode dereference: return GenerateExpression(dereference.Expression); case IdentifierNode identifier: - if (identifier.Namespace != null) + if (identifier.Namespace.HasValue) { throw new NotSupportedException("There is nothing to address in another namespace"); } - return _symbolTable.LookupVariable(identifier.Name).Pointer; + return _variables.Single(x => x.Name == identifier.Name).Pointer; case MemberAccessNode memberAccess: return GenerateMemberAccessPointer(memberAccess); default: @@ -809,7 +842,7 @@ public class QBEGenerator } } - private string GenerateBinaryExpression(BinaryExpressionNode binaryExpression) + private static string GenerateBinaryExpression(BinaryExpressionNode binaryExpression) { var left = GenerateExpression(binaryExpression.Left); var right = GenerateExpression(binaryExpression.Right); @@ -1035,25 +1068,26 @@ public class QBEGenerator throw new NotSupportedException($"Binary operator {binaryExpression.Operator} for types {binaryExpression.Left.Type} and {binaryExpression.Right.Type} not supported"); } - private string GenerateIdentifier(IdentifierNode identifier) + private static string GenerateIdentifier(IdentifierNode identifier) { - var symbol = _symbolTable.Lookup(identifier.Namespace, identifier.Name); - - switch (symbol) + if (_definitionTable.LookupFunc(identifier.Namespace.Or(_compilationUnit.Namespace), identifier.Name).TryGetValue(out var func)) { - case SymbolTable.Func func: - var pointer = GenVarName(); - _builder.AppendLine($" {pointer} =l alloc8 8"); - _builder.AppendLine($" storel {func.GeneratedName}, {pointer}"); - return pointer; - case SymbolTable.Variable variable: - return GenerateDereference(identifier.Type, variable.Pointer); - default: - throw new ArgumentOutOfRangeException(nameof(symbol)); + var pointer = GenVarName(); + _builder.AppendLine($" {pointer} =l alloc8 8"); + _builder.AppendLine($" storel {FuncName(func)}, {pointer}"); + return pointer; } + + if (!identifier.Namespace.HasValue) + { + var variable = _variables.Single(v => v.Name == identifier.Name); + return GenerateDereference(identifier.Type, variable.Pointer); + } + + throw new UnreachableException(); } - private string GenerateLiteral(LiteralNode literal) + private static string GenerateLiteral(LiteralNode literal) { if (literal.Type.IsInteger) { @@ -1095,9 +1129,9 @@ public class QBEGenerator } - private string GenerateStructInitializer(StructInitializerNode structInitializer) + private static string GenerateStructInitializer(StructInitializerNode structInitializer) { - var structDefinition = LookupStructDefinition(structInitializer.StructType.Namespace, structInitializer.StructType.Name); + var structDefinition = _definitionTable.LookupStruct(structInitializer.StructType.Namespace, structInitializer.StructType.Name).GetValue(); var structVar = GenVarName(); var size = SizeOf(structInitializer.StructType); @@ -1130,7 +1164,7 @@ public class QBEGenerator return structVar; } - private string GenerateUnaryExpression(UnaryExpressionNode unaryExpression) + private static string GenerateUnaryExpression(UnaryExpressionNode unaryExpression) { var operand = GenerateExpression(unaryExpression.Operand); var outputName = GenVarName(); @@ -1177,7 +1211,7 @@ public class QBEGenerator throw new NotSupportedException($"Unary operator {unaryExpression.Operator} for type {unaryExpression.Operand.Type} not supported"); } - private string GenerateMemberAccessPointer(MemberAccessNode memberAccess) + private static string GenerateMemberAccessPointer(MemberAccessNode memberAccess) { var item = GenerateExpression(memberAccess.Expression); switch (memberAccess.Expression.Type) @@ -1193,7 +1227,7 @@ public class QBEGenerator } case NubStructType structType: { - var structDefinition = LookupStructDefinition(structType.Namespace, structType.Name); + var structDefinition = _definitionTable.LookupStruct(structType.Namespace, structType.Name).GetValue(); var offset = LookupMemberOffset(structDefinition, memberAccess.Member); var offsetName = GenVarName(); @@ -1207,7 +1241,7 @@ public class QBEGenerator } } - private string GenerateMemberAccess(MemberAccessNode memberAccess) + private static string GenerateMemberAccess(MemberAccessNode memberAccess) { var pointer = GenerateMemberAccessPointer(memberAccess); @@ -1223,7 +1257,7 @@ public class QBEGenerator } } - private string GenerateFixedArrayInitializer(FixedArrayInitializerNode fixedArrayInitializer) + private static string GenerateFixedArrayInitializer(FixedArrayInitializerNode fixedArrayInitializer) { var totalSize = SizeOf(fixedArrayInitializer.Type); var outputName = GenVarName(); @@ -1240,7 +1274,7 @@ public class QBEGenerator return outputName; } - private string GenerateFuncCall(FuncCallNode funcCall) + private static string GenerateFuncCall(FuncCallNode funcCall) { var funcType = (NubFuncType)funcCall.Expression.Type; @@ -1279,9 +1313,9 @@ public class QBEGenerator } string funcTarget; - if (funcCall.Expression is IdentifierNode identifier && _symbolTable.Lookup(identifier.Namespace, identifier.Name) is SymbolTable.Func func) + if (funcCall.Expression is IdentifierNode identifier && _definitionTable.LookupFunc(identifier.Namespace.Or(_compilationUnit.Namespace), identifier.Name).TryGetValue(out var func)) { - funcTarget = func.GeneratedName; + funcTarget = FuncName(func); } else { @@ -1304,7 +1338,7 @@ public class QBEGenerator } } - private void GenerateCopy(NubType type, string value, string destinationPointer) + private static void GenerateCopy(NubType type, string value, string destinationPointer) { if (IsLargeType(type)) { @@ -1316,7 +1350,7 @@ public class QBEGenerator } } - private string GenerateDereference(NubType type, string pointer) + private static string GenerateDereference(NubType type, string pointer) { if (IsLargeType(type)) { @@ -1330,26 +1364,17 @@ public class QBEGenerator } } - private string GenVarName() + private static string GenVarName() { return $"%v{++_variableIndex}"; } - private string GenLabelName() + private static string GenLabelName() { return $"@l{++_labelIndex}"; } - private StructDefinitionNode LookupStructDefinition(string @namespace, string name) - { - return _sourceFiles - .Where(f => f.Namespace == @namespace) - .SelectMany(f => f.Definitions) - .OfType() - .Single(s => s.Name == name); - } - - private int LookupMemberOffset(StructDefinitionNode structDefinition, string member) + private static int LookupMemberOffset(StructDefinitionNode structDefinition, string member) { var offset = 0; diff --git a/src/lang/Nub.Lang/Frontend/Generation/SymbolTable.cs b/src/lang/Nub.Lang/Frontend/Generation/SymbolTable.cs deleted file mode 100644 index 7889008..0000000 --- a/src/lang/Nub.Lang/Frontend/Generation/SymbolTable.cs +++ /dev/null @@ -1,109 +0,0 @@ -using Nub.Lang.Frontend.Parsing.Definitions; - -namespace Nub.Lang.Frontend.Generation; - -public class SymbolTable -{ - private readonly List _functions = []; - private readonly Stack _variables = []; - private readonly Stack _scopes = []; - - public SymbolTable(IEnumerable functions) - { - foreach (var func in functions) - { - string name; - switch (func) - { - case ExternFuncDefinitionNode externFuncDefinitionNode: - { - name = "$" + externFuncDefinitionNode.CallName; - break; - } - case LocalFuncDefinitionNode localFuncDefinitionNode: - { - if (localFuncDefinitionNode.Exported) - { - name = "$" + localFuncDefinitionNode.Name; - } - else - { - name = "$" + localFuncDefinitionNode.Namespace + "_" + localFuncDefinitionNode.Name; - } - break; - } - default: - { - throw new ArgumentOutOfRangeException(nameof(func)); - } - } - _functions.Add(new Func(func.Namespace, func.Name, name)); - } - } - - public void Reset() - { - _variables.Clear(); - } - - public void StartScope() - { - _scopes.Push(_variables.Count); - } - - public void EndScope() - { - var count = _scopes.Pop(); - while (count > _variables.Count) - { - _variables.Pop(); - } - } - - public Symbol Lookup(string? @namespace, string name) - { - if (@namespace == null) - { - return LookupVariable(name); - } - - return LookupFunc(@namespace, name); - } - - public Func LookupFunc(string @namespace, string name) - { - return _functions.Single(x => x.Name == name && x.Namespace == @namespace); - } - - public Variable LookupVariable(string name) - { - return _variables.Single(x => x.Name == name); - } - - public void DeclareVariable(string name, string pointer) - { - _variables.Push(new Variable(name, pointer)); - } - - public abstract class Symbol(string name) - { - public string Name { get; } = name; - } - - public class Variable(string name, string pointer) : Symbol(name) - { - public string Pointer { get; set; } = pointer; - } - - public class Func(string @namespace, string name, string generatedName) : Symbol(name) - { - public string Namespace { get; } = @namespace; - public string GeneratedName { get; } = generatedName; - } - - public class Struct(string @namespace, string name) : Symbol(name) - { - public string Namespace { get; } = @namespace; - public string GeneratedName => $"{Namespace}_{Name}"; - } -} \ No newline at end of file diff --git a/src/lang/Nub.Lang/Frontend/Lexing/Lexer.cs b/src/lang/Nub.Lang/Frontend/Lexing/Lexer.cs index a02551b..c5f9473 100644 --- a/src/lang/Nub.Lang/Frontend/Lexing/Lexer.cs +++ b/src/lang/Nub.Lang/Frontend/Lexing/Lexer.cs @@ -2,7 +2,7 @@ namespace Nub.Lang.Frontend.Lexing; -public class Lexer +public static class Lexer { private static readonly Dictionary Keywords = new() { @@ -59,10 +59,10 @@ public class Lexer ['&'] = Symbol.Ampersand, }; - private SourceText _sourceText; - private int _index; + private static SourceText _sourceText; + private static int _index; - public DiagnosticsResult> Tokenize(SourceText sourceText) + public static DiagnosticsResult> Tokenize(SourceText sourceText) { _sourceText = sourceText; _index = 0; @@ -76,7 +76,7 @@ public class Lexer return new DiagnosticsResult>([], tokens); } - private void ConsumeWhitespace() + private static void ConsumeWhitespace() { while (Peek().TryGetValue(out var character) && char.IsWhiteSpace(character)) { @@ -84,7 +84,7 @@ public class Lexer } } - private Optional ParseToken() + private static Optional ParseToken() { ConsumeWhitespace(); var startIndex = _index; @@ -236,7 +236,7 @@ public class Lexer throw new Exception($"Unknown character {current}"); } - private SourceLocation CreateLocation(int index) + private static SourceLocation CreateLocation(int index) { var line = 1; var column = 1; @@ -256,12 +256,12 @@ public class Lexer return new SourceLocation(line, column); } - private SourceSpan CreateSpan(int startIndex) + private static SourceSpan CreateSpan(int startIndex) { return new SourceSpan(_sourceText, CreateLocation(startIndex), CreateLocation(_index)); } - private Optional Peek(int offset = 0) + private static Optional Peek(int offset = 0) { if (_index + offset < _sourceText.Content.Length) { @@ -271,7 +271,7 @@ public class Lexer return Optional.Empty(); } - private void Next() + private static void Next() { _index++; } diff --git a/src/lang/Nub.Lang/Frontend/Parsing/SourceFile.cs b/src/lang/Nub.Lang/Frontend/Parsing/CompilationUnit.cs similarity index 71% rename from src/lang/Nub.Lang/Frontend/Parsing/SourceFile.cs rename to src/lang/Nub.Lang/Frontend/Parsing/CompilationUnit.cs index dbf3624..52247f0 100644 --- a/src/lang/Nub.Lang/Frontend/Parsing/SourceFile.cs +++ b/src/lang/Nub.Lang/Frontend/Parsing/CompilationUnit.cs @@ -2,7 +2,7 @@ namespace Nub.Lang.Frontend.Parsing; -public class SourceFile(string @namespace, List definitions) +public class CompilationUnit(string @namespace, List definitions) { public string Namespace { get; } = @namespace; public List Definitions { get; } = definitions; diff --git a/src/lang/Nub.Lang/Frontend/Parsing/Definitions/DefinitionNode.cs b/src/lang/Nub.Lang/Frontend/Parsing/Definitions/DefinitionNode.cs index 494cbb9..13a47f9 100644 --- a/src/lang/Nub.Lang/Frontend/Parsing/Definitions/DefinitionNode.cs +++ b/src/lang/Nub.Lang/Frontend/Parsing/Definitions/DefinitionNode.cs @@ -2,7 +2,8 @@ namespace Nub.Lang.Frontend.Parsing.Definitions; -public abstract class DefinitionNode(IReadOnlyList tokens, Optional documentation) : Node(tokens) +public abstract class DefinitionNode(IReadOnlyList tokens, Optional documentation, string @namespace) : Node(tokens) { - public Optional Documentation { get; set; } = documentation; + public Optional Documentation { get; } = documentation; + public string Namespace { get; set; } = @namespace; } diff --git a/src/lang/Nub.Lang/Frontend/Parsing/Definitions/FuncDefinitionNode.cs b/src/lang/Nub.Lang/Frontend/Parsing/Definitions/FuncDefinitionNode.cs index fdb50db..bd83b98 100644 --- a/src/lang/Nub.Lang/Frontend/Parsing/Definitions/FuncDefinitionNode.cs +++ b/src/lang/Nub.Lang/Frontend/Parsing/Definitions/FuncDefinitionNode.cs @@ -15,17 +15,15 @@ public class FuncParameter(string name, NubType type) public interface IFuncSignature { public string Name { get; } - public string Namespace { get; } public List Parameters { get; } public NubType ReturnType { get; } public string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){": " + ReturnType}"; } -public class LocalFuncDefinitionNode(IReadOnlyList tokens, Optional documentation, string name, string @namespace, List parameters, BlockNode body, NubType returnType, bool exported) : DefinitionNode(tokens, documentation), IFuncSignature +public class LocalFuncDefinitionNode(IReadOnlyList tokens, Optional documentation, string @namespace, string name, List parameters, BlockNode body, NubType returnType, bool exported) : DefinitionNode(tokens, documentation, @namespace), IFuncSignature { public string Name { get; } = name; - public string Namespace { get; } = @namespace; public List Parameters { get; } = parameters; public BlockNode Body { get; } = body; public NubType ReturnType { get; } = returnType; @@ -34,10 +32,9 @@ public class LocalFuncDefinitionNode(IReadOnlyList tokens, Optional $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){": " + ReturnType}"; } -public class ExternFuncDefinitionNode(IReadOnlyList tokens, Optional documentation, string name, string @namespace, string callName, List parameters, NubType returnType) : DefinitionNode(tokens, documentation), IFuncSignature +public class ExternFuncDefinitionNode(IReadOnlyList tokens, Optional documentation, string @namespace, string name, string callName, List parameters, NubType returnType) : DefinitionNode(tokens, documentation, @namespace), IFuncSignature { public string Name { get; } = name; - public string Namespace { get; } = @namespace; public string CallName { get; } = callName; public List Parameters { get; } = parameters; public NubType ReturnType { get; } = returnType; diff --git a/src/lang/Nub.Lang/Frontend/Parsing/Definitions/StructDefinitionNode.cs b/src/lang/Nub.Lang/Frontend/Parsing/Definitions/StructDefinitionNode.cs index 1a1975b..09bb11a 100644 --- a/src/lang/Nub.Lang/Frontend/Parsing/Definitions/StructDefinitionNode.cs +++ b/src/lang/Nub.Lang/Frontend/Parsing/Definitions/StructDefinitionNode.cs @@ -11,9 +11,8 @@ public class StructField(string name, NubType type, Optional val public Optional Value { get; } = value; } -public class StructDefinitionNode(IReadOnlyList tokens, Optional documentation, string name, string @namespace, List fields) : DefinitionNode(tokens, documentation) +public class StructDefinitionNode(IReadOnlyList tokens, Optional documentation, string @namespace, string name, List fields) : DefinitionNode(tokens, documentation, @namespace) { public string Name { get; } = name; - public string Namespace { get; } = @namespace; public List Fields { get; } = fields; } \ No newline at end of file diff --git a/src/lang/Nub.Lang/Frontend/Parsing/Expressions/IdentifierNode.cs b/src/lang/Nub.Lang/Frontend/Parsing/Expressions/IdentifierNode.cs index cb51da0..e1086f8 100644 --- a/src/lang/Nub.Lang/Frontend/Parsing/Expressions/IdentifierNode.cs +++ b/src/lang/Nub.Lang/Frontend/Parsing/Expressions/IdentifierNode.cs @@ -2,10 +2,10 @@ namespace Nub.Lang.Frontend.Parsing.Expressions; -public class IdentifierNode(IReadOnlyList tokens, string? @namespace, string name) : LValueNode(tokens) +public class IdentifierNode(IReadOnlyList tokens, Optional @namespace, string name) : LValueNode(tokens) { - public string? Namespace { get; } = @namespace; + public Optional Namespace { get; } = @namespace; public string Name { get; } = name; - public override string ToString() => Name; + public override string ToString() => Namespace.HasValue ? $"{Namespace.Value}::{Name}" : Name; } \ No newline at end of file diff --git a/src/lang/Nub.Lang/Frontend/Parsing/Parser.cs b/src/lang/Nub.Lang/Frontend/Parsing/Parser.cs index c7e4d91..9514d59 100644 --- a/src/lang/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/src/lang/Nub.Lang/Frontend/Parsing/Parser.cs @@ -9,33 +9,43 @@ using Nub.Lang.Frontend.Typing; namespace Nub.Lang.Frontend.Parsing; -public class Parser +public static class Parser { - private List _diagnostics = []; - private List _tokens = []; - private int _index; - private string _namespace = string.Empty; + private static string _namespace = null!; + private static List _diagnostics = []; + private static List _tokens = []; + private static int _index; - public DiagnosticsResult ParseModule(List tokens) + public static DiagnosticsResult ParseFile(List tokens) { - _diagnostics = []; _tokens = tokens; + _namespace = null!; + _diagnostics = []; _index = 0; - _namespace = string.Empty; try { ExpectSymbol(Symbol.Namespace); - _namespace = ExpectIdentifier().Value; + var @namespace = ExpectIdentifier(); + _namespace = @namespace.Value; + } + catch (ParseException ex) + { + _diagnostics.Add(ex.Diagnostic); + return new DiagnosticsResult(_diagnostics, null); + } + try + { List definitions = []; while (Peek().HasValue) { - definitions.Add(ParseDefinition()); + var definition = ParseDefinition(); + definitions.Add(definition); } - return new DiagnosticsResult(_diagnostics, new SourceFile(_namespace, definitions)); + return new DiagnosticsResult(_diagnostics, new CompilationUnit(_namespace, definitions)); } catch (ParseException ex) { @@ -43,10 +53,10 @@ public class Parser RecoverToNextDefinition(); } - return new DiagnosticsResult(_diagnostics, null); + return new DiagnosticsResult(_diagnostics, null); } - private DefinitionNode ParseDefinition() + private static DefinitionNode ParseDefinition() { var startIndex = _index; List modifiers = []; @@ -78,7 +88,7 @@ public class Parser }; } - private DefinitionNode ParseFuncDefinition(int startIndex, List modifiers, Optional documentation) + private static DefinitionNode ParseFuncDefinition(int startIndex, List modifiers, Optional documentation) { var name = ExpectIdentifier(); List parameters = []; @@ -120,7 +130,7 @@ public class Parser callName = ExpectIdentifier().Value; } - return new ExternFuncDefinitionNode(GetTokensForNode(startIndex), documentation, name.Value, _namespace, callName, parameters, returnType); + return new ExternFuncDefinitionNode(GetTokensForNode(startIndex), documentation, _namespace, name.Value, callName, parameters, returnType); } var body = ParseBlock(); @@ -135,10 +145,10 @@ public class Parser .Build()); } - return new LocalFuncDefinitionNode(GetTokensForNode(startIndex), documentation, name.Value, _namespace, parameters, body, returnType, exported); + return new LocalFuncDefinitionNode(GetTokensForNode(startIndex), documentation, _namespace, name.Value, parameters, body, returnType, exported); } - private StructDefinitionNode ParseStruct(int startIndex, List _, Optional documentation) + private static StructDefinitionNode ParseStruct(int startIndex, List _, Optional documentation) { var name = ExpectIdentifier().Value; @@ -162,10 +172,10 @@ public class Parser variables.Add(new StructField(variableName, variableType, variableValue)); } - return new StructDefinitionNode(GetTokensForNode(startIndex), documentation, name, _namespace, variables); + return new StructDefinitionNode(GetTokensForNode(startIndex), documentation, _namespace, name, variables); } - private FuncParameter ParseFuncParameter() + private static FuncParameter ParseFuncParameter() { var name = ExpectIdentifier(); ExpectSymbol(Symbol.Colon); @@ -174,7 +184,7 @@ public class Parser return new FuncParameter(name.Value, type); } - private StatementNode ParseStatement() + private static StatementNode ParseStatement() { var startIndex = _index; if (!Peek().TryGetValue(out var token)) @@ -207,7 +217,7 @@ public class Parser return ParseStatementExpression(startIndex); } - private StatementNode ParseStatementExpression(int startIndex) + private static StatementNode ParseStatementExpression(int startIndex) { var expr = ParseExpression(); @@ -256,7 +266,7 @@ public class Parser return new StatementExpressionNode(GetTokensForNode(startIndex), expr); } - private VariableDeclarationNode ParseVariableDeclaration(int startIndex) + private static VariableDeclarationNode ParseVariableDeclaration(int startIndex) { ExpectSymbol(Symbol.Let); var name = ExpectIdentifier().Value; @@ -275,20 +285,20 @@ public class Parser return new VariableDeclarationNode(GetTokensForNode(startIndex), name, type, value); } - private StatementNode ParseBreak(int startIndex) + private static StatementNode ParseBreak(int startIndex) { ExpectSymbol(Symbol.Break); Next(); return new BreakNode(GetTokensForNode(startIndex)); } - private StatementNode ParseContinue(int startIndex) + private static StatementNode ParseContinue(int startIndex) { ExpectSymbol(Symbol.Continue); return new ContinueNode(GetTokensForNode(startIndex)); } - private ReturnNode ParseReturn(int startIndex) + private static ReturnNode ParseReturn(int startIndex) { ExpectSymbol(Symbol.Return); var value = Optional.Empty(); @@ -300,7 +310,7 @@ public class Parser return new ReturnNode(GetTokensForNode(startIndex), value); } - private IfNode ParseIf(int startIndex) + private static IfNode ParseIf(int startIndex) { ExpectSymbol(Symbol.If); var condition = ParseExpression(); @@ -318,7 +328,7 @@ public class Parser return new IfNode(GetTokensForNode(startIndex), condition, body, elseStatement); } - private WhileNode ParseWhile(int startIndex) + private static WhileNode ParseWhile(int startIndex) { ExpectSymbol(Symbol.While); var condition = ParseExpression(); @@ -326,7 +336,7 @@ public class Parser return new WhileNode(GetTokensForNode(startIndex), condition, body); } - private ExpressionNode ParseExpression(int precedence = 0) + private static ExpressionNode ParseExpression(int precedence = 0) { var startIndex = _index; var left = ParsePrimaryExpression(); @@ -407,7 +417,7 @@ public class Parser } } - private ExpressionNode ParsePrimaryExpression() + private static ExpressionNode ParsePrimaryExpression() { var startIndex = _index; ExpressionNode expr; @@ -422,7 +432,7 @@ public class Parser } case IdentifierToken identifier: { - string? @namespace = null; + var @namespace = Optional.Empty(); var name = identifier.Value; if (TryExpectSymbol(Symbol.DoubleColon)) { @@ -577,7 +587,7 @@ public class Parser return ParsePostfixOperators(startIndex, expr); } - private ExpressionNode ParsePostfixOperators(int startIndex, ExpressionNode expr) + private static ExpressionNode ParsePostfixOperators(int startIndex, ExpressionNode expr) { while (true) { @@ -628,7 +638,7 @@ public class Parser return expr; } - private BlockNode ParseBlock() + private static BlockNode ParseBlock() { var startIndex = _index; ExpectSymbol(Symbol.OpenBrace); @@ -649,21 +659,21 @@ public class Parser return new BlockNode(GetTokensForNode(startIndex), statements); } - private NubType ParseType() + private static NubType ParseType() { if (TryExpectIdentifier(out var name)) { - if (name == "any") + if (name.Value == "any") { return new NubAnyType(); } - if (name == "void") + if (name.Value == "void") { return new NubVoidType(); } - if (NubPrimitiveType.TryParse(name, out var primitiveTypeKind)) + if (NubPrimitiveType.TryParse(name.Value, out var primitiveTypeKind)) { return new NubPrimitiveType(primitiveTypeKind.Value); } @@ -674,7 +684,16 @@ public class Parser @namespace = ExpectIdentifier().Value; } - return new NubStructType(@namespace, name); + if (@namespace == null) + { + throw new ParseException(Diagnostic + .Error($"Struct '{name.Value}' does not belong to a namespace") + .WithHelp("Make sure you have specified a namespace at the top of the file") + .At(name) + .Build()); + } + + return new NubStructType(@namespace , name.Value); } if (TryExpectSymbol(Symbol.Caret)) @@ -749,7 +768,7 @@ public class Parser .Build()); } - private Token ExpectToken() + private static Token ExpectToken() { if (!Peek().TryGetValue(out var token)) { @@ -764,7 +783,7 @@ public class Parser return token; } - private SymbolToken ExpectSymbol() + private static SymbolToken ExpectSymbol() { var token = ExpectToken(); if (token is not SymbolToken symbol) @@ -779,7 +798,7 @@ public class Parser return symbol; } - private void ExpectSymbol(Symbol expectedSymbol) + private static void ExpectSymbol(Symbol expectedSymbol) { var token = ExpectSymbol(); if (token.Symbol != expectedSymbol) @@ -792,7 +811,7 @@ public class Parser } } - private bool TryExpectSymbol(Symbol symbol) + private static bool TryExpectSymbol(Symbol symbol) { if (Peek() is { Value: SymbolToken symbolToken } && symbolToken.Symbol == symbol) { @@ -803,7 +822,7 @@ public class Parser return false; } - private bool TryExpectModifier([NotNullWhen(true)] out ModifierToken? modifier) + private static bool TryExpectModifier([NotNullWhen(true)] out ModifierToken? modifier) { if (Peek() is { Value: ModifierToken modifierToken }) { @@ -816,11 +835,11 @@ public class Parser return false; } - private bool TryExpectIdentifier([NotNullWhen(true)] out string? identifier) + private static bool TryExpectIdentifier([NotNullWhen(true)] out IdentifierToken? identifier) { if (Peek() is { Value: IdentifierToken identifierToken }) { - identifier = identifierToken.Value; + identifier = identifierToken; Next(); return true; } @@ -829,7 +848,7 @@ public class Parser return false; } - private IdentifierToken ExpectIdentifier() + private static IdentifierToken ExpectIdentifier() { var token = ExpectToken(); if (token is not IdentifierToken identifier) @@ -844,12 +863,12 @@ public class Parser return identifier; } - private void RecoverToNextDefinition() + private static void RecoverToNextDefinition() { while (Peek().HasValue) { var token = Peek().Value; - if (token is SymbolToken { Symbol: Symbol.Func or Symbol.Struct }) + if (token is SymbolToken { Symbol: Symbol.Func or Symbol.Struct } or ModifierToken) { break; } @@ -858,7 +877,7 @@ public class Parser } } - private void RecoverToNextStatement() + private static void RecoverToNextStatement() { while (Peek().TryGetValue(out var token)) { @@ -874,7 +893,7 @@ public class Parser } } - private Optional Peek(int offset = 0) + private static Optional Peek(int offset = 0) { var peekIndex = _index + offset; while (peekIndex < _tokens.Count && _tokens[peekIndex] is DocumentationToken) @@ -890,7 +909,7 @@ public class Parser return Optional.Empty(); } - private void Next() + private static void Next() { while (_index < _tokens.Count && _tokens[_index] is DocumentationToken) { @@ -900,7 +919,7 @@ public class Parser _index++; } - private IReadOnlyList GetTokensForNode(int startIndex) + private static IReadOnlyList GetTokensForNode(int startIndex) { return _tokens[startIndex..Math.Min(_index, _tokens.Count - 1)]; } diff --git a/src/lang/Nub.Lang/Frontend/Typing/TypeChecker.cs b/src/lang/Nub.Lang/Frontend/Typing/TypeChecker.cs index b30f8a4..9623e2f 100644 --- a/src/lang/Nub.Lang/Frontend/Typing/TypeChecker.cs +++ b/src/lang/Nub.Lang/Frontend/Typing/TypeChecker.cs @@ -8,63 +8,46 @@ using Nub.Lang.Frontend.Parsing.Statements; namespace Nub.Lang.Frontend.Typing; -public class TypeChecker +public static class TypeChecker { - private Dictionary _variables = new(); - private List _sourceFiles = []; - private List _diagnostics = []; - private NubType? _currentFunctionReturnType; - private Queue _anonymousFunctions = []; + private static CompilationUnit _compilationUnit = null!; + private static DefinitionTable _definitionTable = null!; - public DiagnosticsResult TypeCheck(List sourceFiles) + private static Dictionary _variables = new(); + private static List _diagnostics = []; + private static NubType? _currentFunctionReturnType; + private static Queue _anonymousFunctions = []; + + public static DiagnosticsResult Check(CompilationUnit compilationUnit, DefinitionTable definitionTable) { + _compilationUnit = compilationUnit; + _definitionTable = definitionTable; + _variables = new Dictionary(); _diagnostics = []; _currentFunctionReturnType = null; - _sourceFiles = sourceFiles; _anonymousFunctions = []; - var externFuncDefinitions = _sourceFiles - .SelectMany(f => f.Definitions) - .OfType() - .ToArray(); - - foreach (var funcName in externFuncDefinitions.Where(x => externFuncDefinitions.Count(y => x.Name == y.Name) > 1)) + foreach (var structDef in compilationUnit.Definitions.OfType()) { - ReportError($"Extern function '{funcName}' has been declared more than once", funcName); + CheckStructDef(structDef); } - var exportedLocalFuncDefinitions = _sourceFiles - .SelectMany(f => f.Definitions) - .OfType() - .Where(f => f.Exported) - .ToArray(); - - foreach (var funcName in exportedLocalFuncDefinitions.Where(x => exportedLocalFuncDefinitions.Count(y => x.Name == y.Name) > 1)) + foreach (var funcDef in compilationUnit.Definitions.OfType()) { - ReportError($"Exported function '{funcName}' has been declared more than once", funcName); - } - - foreach (var structDef in _sourceFiles.SelectMany(f => f.Definitions).OfType()) - { - TypeCheckStructDef(structDef); - } - - foreach (var funcDef in _sourceFiles.SelectMany(f => f.Definitions).OfType()) - { - TypeCheckFuncDef(funcDef.Parameters, funcDef.Body, funcDef.ReturnType); + CheckFuncDef(funcDef.Parameters, funcDef.Body, funcDef.ReturnType); } while (_anonymousFunctions.TryDequeue(out var func)) { - TypeCheckFuncDef(func.Parameters, func.Body, func.ReturnType); + CheckFuncDef(func.Parameters, func.Body, func.ReturnType); } return new DiagnosticsResult(_diagnostics); } - private void TypeCheckStructDef(StructDefinitionNode structDef) + private static void CheckStructDef(StructDefinitionNode structDef) { var fields = new Dictionary(); foreach (var field in structDef.Fields) @@ -77,7 +60,7 @@ public class TypeChecker if (field.Value.HasValue) { - var fieldType = TypeCheckExpression(field.Value.Value, field.Type); + var fieldType = CheckExpression(field.Value.Value, field.Type); if (fieldType != null && !fieldType.Equals(field.Type)) { ReportError("Default field initializer does not match the defined type", field.Value.Value); @@ -88,7 +71,7 @@ public class TypeChecker } } - private void TypeCheckFuncDef(List parameters, BlockNode body, NubType returnType) + private static void CheckFuncDef(List parameters, BlockNode body, NubType returnType) { _variables.Clear(); _currentFunctionReturnType = returnType; @@ -98,50 +81,50 @@ public class TypeChecker _variables[param.Name] = param.Type; } - TypeCheckBlock(body); + CheckBlock(body); } - private void TypeCheckBlock(BlockNode block) + private static void CheckBlock(BlockNode block) { foreach (var statement in block.Statements) { - TypeCheckStatement(statement); + CheckStatement(statement); } } - private void TypeCheckStatement(StatementNode statement) + private static void CheckStatement(StatementNode statement) { switch (statement) { case ArrayIndexAssignmentNode arrayIndexAssignment: - TypeCheckArrayIndexAssignment(arrayIndexAssignment); + CheckArrayIndexAssignment(arrayIndexAssignment); break; case VariableAssignmentNode variableAssignment: - TypeCheckVariableAssignment(variableAssignment); + CheckVariableAssignment(variableAssignment); break; case VariableDeclarationNode variableDeclaration: - TypeCheckVariableVariableDeclaration(variableDeclaration); + CheckVariableVariableDeclaration(variableDeclaration); break; case IfNode ifNode: - TypeCheckIf(ifNode); + CheckIf(ifNode); break; case MemberAssignmentNode memberAssignment: - TypeCheckMemberAssignment(memberAssignment); + CheckMemberAssignment(memberAssignment); break; case WhileNode whileNode: - TypeCheckWhile(whileNode); + CheckWhile(whileNode); break; case ReturnNode returnNode: - TypeCheckReturn(returnNode); + CheckReturn(returnNode); break; case StatementExpressionNode statementExpression: - TypeCheckExpression(statementExpression.Expression); + CheckExpression(statementExpression.Expression); break; case BreakNode: case ContinueNode: break; case DereferenceAssignmentNode dereferenceAssignment: - TypeCheckDereferenceAssignment(dereferenceAssignment); + CheckDereferenceAssignment(dereferenceAssignment); break; default: ReportError($"Unsupported statement type: {statement.GetType().Name}", statement); @@ -149,11 +132,11 @@ public class TypeChecker } } - private void TypeCheckMemberAssignment(MemberAssignmentNode memberAssignment) + private static void CheckMemberAssignment(MemberAssignmentNode memberAssignment) { - var memberType = TypeCheckExpression(memberAssignment.MemberAccess); + var memberType = CheckExpression(memberAssignment.MemberAccess); if (memberType == null) return; - var valueType = TypeCheckExpression(memberAssignment.Value, memberType); + var valueType = CheckExpression(memberAssignment.Value, memberType); if (valueType == null) return; if (!NubType.IsCompatibleWith(memberType, valueType)) @@ -162,11 +145,11 @@ public class TypeChecker } } - private void TypeCheckArrayIndexAssignment(ArrayIndexAssignmentNode arrayIndexAssignment) + private static void CheckArrayIndexAssignment(ArrayIndexAssignmentNode arrayIndexAssignment) { - var itemType = TypeCheckExpression(arrayIndexAssignment.ArrayIndexAccess); + var itemType = CheckExpression(arrayIndexAssignment.ArrayIndexAccess); if (itemType == null) return; - var valueType = TypeCheckExpression(arrayIndexAssignment.Value, itemType); + var valueType = CheckExpression(arrayIndexAssignment.Value, itemType); if (valueType == null) return; if (!NubType.IsCompatibleWith(itemType, valueType)) @@ -175,7 +158,7 @@ public class TypeChecker } } - private void TypeCheckVariableAssignment(VariableAssignmentNode variableAssignment) + private static void CheckVariableAssignment(VariableAssignmentNode variableAssignment) { if (!_variables.TryGetValue(variableAssignment.Identifier.Name, out var variable)) { @@ -183,7 +166,7 @@ public class TypeChecker return; } - var valueType = TypeCheckExpression(variableAssignment.Value, variable); + var valueType = CheckExpression(variableAssignment.Value, variable); if (valueType == null) return; if (!NubType.IsCompatibleWith(variableAssignment.Value.Type, variable)) @@ -192,7 +175,7 @@ public class TypeChecker } } - private void TypeCheckVariableVariableDeclaration(VariableDeclarationNode variableDeclaration) + private static void CheckVariableVariableDeclaration(VariableDeclarationNode variableDeclaration) { NubType? type = null; @@ -203,7 +186,7 @@ public class TypeChecker if (variableDeclaration.Value.HasValue) { - var valueType = TypeCheckExpression(variableDeclaration.Value.Value, variableDeclaration.ExplicitType.Value); + var valueType = CheckExpression(variableDeclaration.Value.Value, variableDeclaration.ExplicitType.Value); if (valueType == null) return; type = valueType; } @@ -232,9 +215,9 @@ public class TypeChecker _variables[variableDeclaration.Name] = type; } - private NubType? TypeCheckDereference(DereferenceNode dereference) + private static NubType? CheckDereference(DereferenceNode dereference) { - var exprType = TypeCheckExpression(dereference.Expression); + var exprType = CheckExpression(dereference.Expression); if (exprType == null) return null; if (exprType is not NubPointerType nubPointerType) @@ -246,14 +229,14 @@ public class TypeChecker return nubPointerType.BaseType; } - private NubType TypeCheckFixedInitializerArray(FixedArrayInitializerNode fixedArrayInitializer) + private static NubType CheckFixedInitializerArray(FixedArrayInitializerNode fixedArrayInitializer) { return new NubFixedArrayType(fixedArrayInitializer.ElementType, fixedArrayInitializer.Capacity); } - private NubType? TypeCheckFuncCall(FuncCallNode funcCall) + private static NubType? CheckFuncCall(FuncCallNode funcCall) { - var identType = TypeCheckExpression(funcCall.Expression); + var identType = CheckExpression(funcCall.Expression); if (identType is not NubFuncType funcType) { ReportError("Cannot call function on non-function type", funcCall); @@ -268,7 +251,7 @@ public class TypeChecker for (var i = 0; i < funcCall.Parameters.Count; i++) { var parameter = funcCall.Parameters[i]; - var parameterType = TypeCheckExpression(parameter); + var parameterType = CheckExpression(parameter); if (parameterType == null) return null; if (!NubType.IsCompatibleWith(parameterType, funcType.Parameters[i])) @@ -281,39 +264,39 @@ public class TypeChecker return funcType.ReturnType; } - private void TypeCheckIf(IfNode ifNode) + private static void CheckIf(IfNode ifNode) { - var conditionType = TypeCheckExpression(ifNode.Condition, NubPrimitiveType.Bool); + var conditionType = CheckExpression(ifNode.Condition, NubPrimitiveType.Bool); if (conditionType != null && !conditionType.Equals(NubPrimitiveType.Bool)) { ReportError($"If condition must be a boolean expression, got '{conditionType}'", ifNode.Condition); } - TypeCheckBlock(ifNode.Body); + CheckBlock(ifNode.Body); if (ifNode.Else.HasValue) { var elseValue = ifNode.Else.Value; - elseValue.Match(TypeCheckIf, TypeCheckBlock); + elseValue.Match(CheckIf, CheckBlock); } } - private void TypeCheckWhile(WhileNode whileNode) + private static void CheckWhile(WhileNode whileNode) { - var conditionType = TypeCheckExpression(whileNode.Condition, NubPrimitiveType.Bool); + var conditionType = CheckExpression(whileNode.Condition, NubPrimitiveType.Bool); if (conditionType != null && !conditionType.Equals(NubPrimitiveType.Bool)) { ReportError($"While condition must be a boolean expression, got '{conditionType}'", whileNode.Condition); } - TypeCheckBlock(whileNode.Body); + CheckBlock(whileNode.Body); } - private void TypeCheckReturn(ReturnNode returnNode) + private static void CheckReturn(ReturnNode returnNode) { if (returnNode.Value.HasValue) { - var returnType = TypeCheckExpression(returnNode.Value.Value, _currentFunctionReturnType); + var returnType = CheckExpression(returnNode.Value.Value, _currentFunctionReturnType); if (returnType == null) return; if (_currentFunctionReturnType == null) @@ -333,11 +316,11 @@ public class TypeChecker } } - private void TypeCheckDereferenceAssignment(DereferenceAssignmentNode dereferenceAssignment) + private static void CheckDereferenceAssignment(DereferenceAssignmentNode dereferenceAssignment) { - var dereferenceType = TypeCheckExpression(dereferenceAssignment.Dereference); + var dereferenceType = CheckExpression(dereferenceAssignment.Dereference); if (dereferenceType == null) return; - var valueType = TypeCheckExpression(dereferenceAssignment.Value, dereferenceType); + var valueType = CheckExpression(dereferenceAssignment.Value, dereferenceType); if (valueType == null) return; if (!NubType.IsCompatibleWith(dereferenceType, valueType)) @@ -346,23 +329,23 @@ public class TypeChecker } } - private NubType? TypeCheckExpression(ExpressionNode expression, NubType? expectedType = null) + private static NubType? CheckExpression(ExpressionNode expression, NubType? expectedType = null) { var resultType = expression switch { - AddressOfNode addressOf => TypeCheckAddressOf(addressOf), - AnonymousFuncNode anonymousFunc => TypeCheckAnonymousFunc(anonymousFunc), - ArrayIndexAccessNode arrayIndex => TypeCheckArrayIndex(arrayIndex), - ArrayInitializerNode arrayInitializer => TypeCheckArrayInitializer(arrayInitializer), - LiteralNode literal => TypeCheckLiteral(literal, expectedType), - IdentifierNode identifier => TypeCheckIdentifier(identifier), - BinaryExpressionNode binaryExpr => TypeCheckBinaryExpression(binaryExpr), - DereferenceNode dereference => TypeCheckDereference(dereference), - FixedArrayInitializerNode fixedArray => TypeCheckFixedInitializerArray(fixedArray), - FuncCallNode funcCallExpr => TypeCheckFuncCall(funcCallExpr), - StructInitializerNode structInit => TypeCheckStructInitializer(structInit), - UnaryExpressionNode unaryExpression => TypeCheckUnaryExpression(unaryExpression), - MemberAccessNode memberAccess => TypeCheckMemberAccess(memberAccess), + AddressOfNode addressOf => CheckAddressOf(addressOf), + AnonymousFuncNode anonymousFunc => CheckAnonymousFunc(anonymousFunc), + ArrayIndexAccessNode arrayIndex => CheckArrayIndex(arrayIndex), + ArrayInitializerNode arrayInitializer => CheckArrayInitializer(arrayInitializer), + LiteralNode literal => CheckLiteral(literal, expectedType), + IdentifierNode identifier => CheckIdentifier(identifier), + BinaryExpressionNode binaryExpr => CheckBinaryExpression(binaryExpr), + DereferenceNode dereference => CheckDereference(dereference), + FixedArrayInitializerNode fixedArray => CheckFixedInitializerArray(fixedArray), + FuncCallNode funcCallExpr => CheckFuncCall(funcCallExpr), + StructInitializerNode structInit => CheckStructInitializer(structInit), + UnaryExpressionNode unaryExpression => CheckUnaryExpression(unaryExpression), + MemberAccessNode memberAccess => CheckMemberAccess(memberAccess), _ => throw new UnreachableException() }; @@ -374,13 +357,13 @@ public class TypeChecker return resultType; } - private NubType TypeCheckAnonymousFunc(AnonymousFuncNode anonymousFunc) + private static NubType CheckAnonymousFunc(AnonymousFuncNode anonymousFunc) { _anonymousFunctions.Enqueue(anonymousFunc); return new NubFuncType(anonymousFunc.ReturnType, anonymousFunc.Parameters.Select(p => p.Type).ToList()); } - private NubType? TypeCheckLiteral(LiteralNode literal, NubType? expectedType = null) + private static NubType? CheckLiteral(LiteralNode literal, NubType? expectedType = null) { if (expectedType != null) { @@ -411,11 +394,11 @@ public class TypeChecker }; } - private NubType? TypeCheckArrayIndex(ArrayIndexAccessNode arrayIndexAccess) + private static NubType? CheckArrayIndex(ArrayIndexAccessNode arrayIndexAccess) { - var expressionType = TypeCheckExpression(arrayIndexAccess.Array); + var expressionType = CheckExpression(arrayIndexAccess.Array); if (expressionType == null) return null; - var indexType = TypeCheckExpression(arrayIndexAccess.Index, NubPrimitiveType.U64); + var indexType = CheckExpression(arrayIndexAccess.Index, NubPrimitiveType.U64); if (indexType is { IsInteger: false }) { ReportError("Array index type must be a number", arrayIndexAccess.Index); @@ -435,9 +418,9 @@ public class TypeChecker return null; } - private NubType TypeCheckArrayInitializer(ArrayInitializerNode arrayInitializer) + private static NubType CheckArrayInitializer(ArrayInitializerNode arrayInitializer) { - var capacityType = TypeCheckExpression(arrayInitializer.Capacity, NubPrimitiveType.U64); + var capacityType = CheckExpression(arrayInitializer.Capacity, NubPrimitiveType.U64); if (capacityType is { IsInteger: false }) { ReportError("Array capacity type must be an integer", arrayInitializer.Capacity); @@ -446,33 +429,26 @@ public class TypeChecker return new NubArrayType(arrayInitializer.ElementType); } - private NubType? TypeCheckIdentifier(IdentifierNode identifier) + private static NubType? CheckIdentifier(IdentifierNode identifier) { - if (identifier.Namespace == null) + var definition = _definitionTable.LookupFunc(identifier.Namespace.Or(_compilationUnit.Namespace), identifier.Name); + if (definition.HasValue) { - var result = _variables.GetValueOrDefault(identifier.Name); - if (result == null) - { - ReportError($"Variable '{identifier.Name}' is not defined", identifier); - return null; - } + return new NubFuncType(definition.Value.ReturnType, definition.Value.Parameters.Select(p => p.Type).ToList()); + } - return result; - } - - var func = LookupFuncSignature(identifier.Namespace, identifier.Name); - if (func == null) + if (!identifier.Namespace.HasValue) { - ReportError($"Identifier '{identifier.Name}' is not defined", identifier); - return null; + return _variables[identifier.Name]; } - - return new NubFuncType(func.ReturnType, func.Parameters.Select(p => p.Type).ToList()); + + ReportError($"Identifier '{identifier}' not found", identifier); + return null; } - private NubType? TypeCheckAddressOf(AddressOfNode addressOf) + private static NubType? CheckAddressOf(AddressOfNode addressOf) { - var exprType = TypeCheckExpression(addressOf.Expression); + var exprType = CheckExpression(addressOf.Expression); if (exprType == null) return null; if (addressOf.Expression is not (IdentifierNode or MemberAccessNode)) @@ -484,10 +460,10 @@ public class TypeChecker return new NubPointerType(exprType); } - private NubType? TypeCheckBinaryExpression(BinaryExpressionNode binaryExpr) + private static NubType? CheckBinaryExpression(BinaryExpressionNode binaryExpr) { - var leftType = TypeCheckExpression(binaryExpr.Left); - var rightType = TypeCheckExpression(binaryExpr.Right); + var leftType = CheckExpression(binaryExpr.Left); + var rightType = CheckExpression(binaryExpr.Right); if (leftType == null || rightType == null) return null; @@ -530,12 +506,12 @@ public class TypeChecker } } - private NubType? TypeCheckStructInitializer(StructInitializerNode structInit) + private static NubType? CheckStructInitializer(StructInitializerNode structInit) { var initialized = new HashSet(); - var definition = LookupStructDefinition(structInit.StructType.Namespace, structInit.StructType.Name); - if (definition == null) + var defOpt = _definitionTable.LookupStruct(structInit.StructType.Namespace, structInit.StructType.Name); + if (!defOpt.TryGetValue(out var definition)) { ReportError($"Struct type '{structInit.StructType.Name}' is not defined", structInit); return null; @@ -550,7 +526,7 @@ public class TypeChecker continue; } - var initializerType = TypeCheckExpression(initializer.Value, definitionField.Type); + var initializerType = CheckExpression(initializer.Value, definitionField.Type); if (initializerType != null && !NubType.IsCompatibleWith(initializerType, definitionField.Type)) { ReportError($"Cannot initialize field '{initializer.Key}' of type '{definitionField.Type}' with expression of type '{initializerType}'", initializer.Value); @@ -575,9 +551,9 @@ public class TypeChecker return structInit.StructType; } - private NubType? TypeCheckUnaryExpression(UnaryExpressionNode unaryExpression) + private static NubType? CheckUnaryExpression(UnaryExpressionNode unaryExpression) { - var operandType = TypeCheckExpression(unaryExpression.Operand); + var operandType = CheckExpression(unaryExpression.Operand); if (operandType == null) return null; switch (unaryExpression.Operator) @@ -615,9 +591,9 @@ public class TypeChecker } } - private NubType? TypeCheckMemberAccess(MemberAccessNode memberAccess) + private static NubType? CheckMemberAccess(MemberAccessNode memberAccess) { - var expressionType = TypeCheckExpression(memberAccess.Expression); + var expressionType = CheckExpression(memberAccess.Expression); if (expressionType == null) return null; switch (expressionType) @@ -633,8 +609,8 @@ public class TypeChecker } case NubStructType structType: { - var definition = LookupStructDefinition(structType.Namespace, structType.Name); - if (definition == null) + var defOpt = _definitionTable.LookupStruct(structType.Namespace, structType.Name); + if (!defOpt.TryGetValue(out var definition)) { ReportError($"Struct type '{structType.Name}' is not defined", memberAccess); return null; @@ -655,13 +631,13 @@ public class TypeChecker return null; } - private void ReportError(string message, Node node) + private static void ReportError(string message, Node node) { var diagnostic = Diagnostic.Error(message).At(node).Build(); _diagnostics.Add(diagnostic); } - private void ReportWarning(string message, Node node) + private static void ReportWarning(string message, Node node) { var diagnostic = Diagnostic.Warning(message).At(node).Build(); _diagnostics.Add(diagnostic); @@ -691,22 +667,4 @@ public class TypeChecker return false; } } - - private IFuncSignature? LookupFuncSignature(string @namespace, string name) - { - return _sourceFiles - .Where(f => f.Namespace == @namespace) - .SelectMany(f => f.Definitions) - .OfType() - .FirstOrDefault(f => f.Name == name); - } - - private StructDefinitionNode? LookupStructDefinition(string @namespace, string name) - { - return _sourceFiles - .Where(f => f.Namespace == @namespace) - .SelectMany(f => f.Definitions) - .OfType() - .SingleOrDefault(d => d.Name == name); - } } \ No newline at end of file diff --git a/src/lang/Nub.Lang/Optional.cs b/src/lang/Nub.Lang/Optional.cs index b60f923..0824254 100644 --- a/src/lang/Nub.Lang/Optional.cs +++ b/src/lang/Nub.Lang/Optional.cs @@ -58,5 +58,20 @@ public readonly struct Optional return false; } + public TValue GetValue() + { + return Value ?? throw new InvalidOperationException("Value is not set"); + } + public static implicit operator Optional(TValue value) => new(value); + + public TValue Or(TValue other) + { + if (HasValue) + { + return Value; + } + + return other; + } } \ No newline at end of file