From cc0bbc81a6f3d1a63d4a7ad7799c731345f26902 Mon Sep 17 00:00:00 2001 From: nub31 Date: Sun, 22 Jun 2025 20:00:07 +0200 Subject: [PATCH] not working --- src/CLI/Program.cs | 28 +- src/Generation/QBE/QBEGenerator.cs | 142 +- src/Syntax/DefinitionTable.cs | 53 +- src/Syntax/Parsing/CompilationUnit.cs | 9 - .../Node/{DefinitionNode.cs => Definition.cs} | 0 .../Node/{ExpressionNode.cs => Expression.cs} | 14 +- .../Node/{StatementNode.cs => Statement.cs} | 0 src/Syntax/Parsing/Parser.cs | 4 +- src/Syntax/Parsing/SyntaxTree.cs | 5 + src/Syntax/Typing/Binder.cs | 400 ++++++ src/Syntax/Typing/BoundNode/Definition.cs | 33 + src/Syntax/Typing/BoundNode/Expression.cs | 21 + src/Syntax/Typing/BoundNode/Node.cs | 6 + src/Syntax/Typing/BoundNode/Statement.cs | 17 + src/Syntax/Typing/BoundSyntaxTree.cs | 5 + src/Syntax/Typing/TypeChecker.cs | 1266 ++++++++--------- 16 files changed, 1257 insertions(+), 746 deletions(-) delete mode 100644 src/Syntax/Parsing/CompilationUnit.cs rename src/Syntax/Parsing/Node/{DefinitionNode.cs => Definition.cs} (100%) rename src/Syntax/Parsing/Node/{ExpressionNode.cs => Expression.cs} (90%) rename src/Syntax/Parsing/Node/{StatementNode.cs => Statement.cs} (100%) create mode 100644 src/Syntax/Parsing/SyntaxTree.cs create mode 100644 src/Syntax/Typing/Binder.cs create mode 100644 src/Syntax/Typing/BoundNode/Definition.cs create mode 100644 src/Syntax/Typing/BoundNode/Expression.cs create mode 100644 src/Syntax/Typing/BoundNode/Node.cs create mode 100644 src/Syntax/Typing/BoundNode/Statement.cs create mode 100644 src/Syntax/Typing/BoundSyntaxTree.cs diff --git a/src/CLI/Program.cs b/src/CLI/Program.cs index 25ad51b..f62895f 100644 --- a/src/CLI/Program.cs +++ b/src/CLI/Program.cs @@ -7,6 +7,7 @@ using Syntax.Diagnostics; using Syntax.Parsing; using Syntax.Tokenization; using Syntax.Typing; +using Binder = Syntax.Typing.Binder; const string BIN_DIR = "bin"; const string BIN_INT_DIR = "bin-int"; @@ -25,8 +26,7 @@ Directory.CreateDirectory(BIN_DIR); Directory.CreateDirectory(BIN_INT_DIR); var diagnostics = new List(); -var compilationUnits = new List(); -var sourceTexts = new Dictionary(); +var syntaxTrees = new List(); foreach (var file in args) { @@ -45,13 +45,12 @@ foreach (var file in args) var tokenizeResult = Tokenizer.Tokenize(sourceText, out var tokenizerDiagnostics); diagnostics.AddRange(tokenizerDiagnostics); - var compilationUnit = Parser.ParseFile(tokenizeResult, out var parseDiagnostics); + var syntaxTree = Parser.ParseFile(tokenizeResult, file, out var parseDiagnostics); diagnostics.AddRange(parseDiagnostics); - if (compilationUnit != null) + if (syntaxTree != null) { - compilationUnits.Add(compilationUnit); - sourceTexts[compilationUnit] = sourceText; + syntaxTrees.Add(syntaxTree); } else { @@ -59,14 +58,17 @@ foreach (var file in args) } } -var definitionTable = new DefinitionTable(compilationUnits); +var definitionTable = new DefinitionTable(syntaxTrees); -foreach (var compilationUnit in compilationUnits) +var boundSyntaxTrees = new List(); + +foreach (var syntaxTree in syntaxTrees) { - TypeChecker.Check(compilationUnit, definitionTable, out var typeCheckerDiagnostics); - diagnostics.AddRange(typeCheckerDiagnostics); + boundSyntaxTrees.Add(Binder.Bind(syntaxTree, definitionTable)); } +var boundDefinitionTable = new BoundDefinitionTable(boundSyntaxTrees); + foreach (var diagnostic in diagnostics) { Console.Error.WriteLine(diagnostic.FormatANSI()); @@ -79,16 +81,16 @@ if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Erro var objectFiles = new List(); -foreach (var compilationUnit in compilationUnits) +foreach (var boundSyntaxTree in boundSyntaxTrees) { - var relativeFilePath = Path.GetRelativePath(Environment.CurrentDirectory, sourceTexts[compilationUnit].Path); + var relativeFilePath = Path.GetRelativePath(Environment.CurrentDirectory, boundSyntaxTree.FilePath); var outputPath = Path.Combine(BIN_INT_DIR, "program", relativeFilePath); var outputDirectory = Path.GetDirectoryName(outputPath); Debug.Assert(!string.IsNullOrWhiteSpace(outputDirectory)); Directory.CreateDirectory(outputDirectory); - var ssa = QBEGenerator.Emit(compilationUnit, definitionTable); + var ssa = QBEGenerator.Emit(boundSyntaxTree, boundDefinitionTable); var ssaPath = Path.ChangeExtension(outputPath, "ssa"); File.WriteAllText(ssaPath, ssa); diff --git a/src/Generation/QBE/QBEGenerator.cs b/src/Generation/QBE/QBEGenerator.cs index b02856b..03648b4 100644 --- a/src/Generation/QBE/QBEGenerator.cs +++ b/src/Generation/QBE/QBEGenerator.cs @@ -2,23 +2,23 @@ using System.Globalization; using System.Text; using Syntax; -using Syntax.Parsing; using Syntax.Parsing.Node; using Syntax.Tokenization; using Syntax.Typing; +using Syntax.Typing.BoundNode; namespace Generation.QBE; public static class QBEGenerator { - private static CompilationUnit _compilationUnit = null!; - private static DefinitionTable _definitionTable = null!; + private static BoundSyntaxTree _syntaxTree = null!; + private static BoundDefinitionTable _definitionTable = null!; private static StringBuilder _builder = new(); private static List _cStringLiterals = []; private static Stack _breakLabels = []; private static Stack _continueLabels = []; - private static Queue<(AnonymousFuncNode Func, string Name)> _anonymousFunctions = []; + private static Queue<(BoundAnonymousFuncNode Func, string Name)> _anonymousFunctions = []; private static Stack _variables = []; private static Stack _variableScopes = []; private static int _variableIndex; @@ -27,9 +27,9 @@ public static class QBEGenerator private static int _cStringLiteralIndex; private static bool _codeIsReachable = true; - public static string Emit(CompilationUnit compilationUnit, DefinitionTable definitionTable) + public static string Emit(BoundSyntaxTree syntaxTree, BoundDefinitionTable definitionTable) { - _compilationUnit = compilationUnit; + _syntaxTree = syntaxTree; _definitionTable = definitionTable; _builder = new StringBuilder(); @@ -51,8 +51,8 @@ public static class QBEGenerator _builder.AppendLine(); } - foreach (var funcDef in _compilationUnit.Definitions.OfType()) - { + foreach (var funcDef in _syntaxTree.Definitions.OfType()) + { EmitFuncDefinition(FuncName(funcDef), funcDef.Parameters, funcDef.ReturnType, funcDef.Body, funcDef.Exported); _builder.AppendLine(); } @@ -86,19 +86,19 @@ public static class QBEGenerator return $"$cstring{++_cStringLiteralIndex}"; } - private static string FuncName(IFuncSignature funcDef) + private static string FuncName(IBoundFuncSignature funcDef) { return funcDef switch { - ExternFuncDefinitionNode externFuncDefinition => $"${externFuncDefinition.CallName}", - LocalFuncDefinitionNode localFuncDefinition => localFuncDefinition.Exported + BoundExternFuncDefinitionNode externFuncDefinition => $"${externFuncDefinition.CallName}", + BoundLocalFuncDefinitionNode localFuncDefinition => localFuncDefinition.Exported ? $"${localFuncDefinition.Name}" : $"${localFuncDefinition.Namespace}_{localFuncDefinition.Name}", _ => throw new ArgumentOutOfRangeException(nameof(funcDef)) }; } - private static string StructName(StructDefinitionNode structDef) + private static string StructName(BoundStructDefinitionNode structDef) { return $":{structDef.Namespace}_{structDef.Name}"; } @@ -288,7 +288,7 @@ public static class QBEGenerator } } - private static int OffsetOf(StructDefinitionNode structDefinition, string member) + private static int OffsetOf(BoundStructDefinitionNode structDefinition, string member) { var offset = 0; @@ -318,7 +318,7 @@ public static class QBEGenerator return type is NubStructType or NubFixedArrayType; } - private static void EmitFuncDefinition(string name, List parameters, NubType returnType, BlockNode body, bool exported) + private static void EmitFuncDefinition(string name, List parameters, NubType returnType, BoundBlockNode body, bool exported) { _variables.Clear(); _variableScopes.Clear(); @@ -429,7 +429,7 @@ public static class QBEGenerator EmitBlock(body, parameterVars); - if (body.Statements.LastOrDefault() is not ReturnNode) + if (body.Statements.LastOrDefault() is not BoundReturnNode) { if (returnType is NubVoidType) { @@ -440,7 +440,7 @@ public static class QBEGenerator _builder.AppendLine("}"); } - private static void EmitStructDefinition(StructDefinitionNode structDefinition) + private static void EmitStructDefinition(BoundStructDefinitionNode structDefinition) { _builder.Append($"type {StructName(structDefinition)} = {{ "); foreach (var structDefinitionField in structDefinition.Fields) @@ -476,41 +476,41 @@ public static class QBEGenerator _builder.AppendLine("}"); } - private static void EmitStatement(StatementNode statement) + private static void EmitStatement(BoundStatementNode statement) { switch (statement) { - case ArrayIndexAssignmentNode arrayIndexAssignment: + case BoundArrayIndexAssignmentNode arrayIndexAssignment: EmitArrayIndexAssignment(arrayIndexAssignment); break; - case BreakNode: + case BoundBreakNode: EmitBreak(); break; - case ContinueNode: + case BoundContinueNode: EmitContinue(); break; - case DereferenceAssignmentNode dereferenceAssignment: + case BoundDereferenceAssignmentNode dereferenceAssignment: EmitDereferenceAssignment(dereferenceAssignment); break; - case IfNode ifStatement: + case BoundIfNode ifStatement: EmitIf(ifStatement); break; - case MemberAssignmentNode memberAssignment: + case BoundMemberAssignmentNode memberAssignment: EmitMemberAssignment(memberAssignment); break; - case ReturnNode @return: + case BoundReturnNode @return: EmitReturn(@return); break; - case StatementExpressionNode statementExpression: + case BoundStatementExpressionNode statementExpression: EmitExpression(statementExpression.Expression); break; - case VariableDeclarationNode variableDeclaration: + case BoundVariableDeclarationNode variableDeclaration: EmitVariableDeclaration(variableDeclaration); break; - case VariableAssignmentNode variableAssignment: + case BoundVariableAssignmentNode variableAssignment: EmitVariableAssignment(variableAssignment); break; - case WhileNode whileStatement: + case BoundWhileNode whileStatement: EmitWhile(whileStatement); break; default: @@ -518,7 +518,7 @@ public static class QBEGenerator } } - private static void EmitArrayIndexAssignment(ArrayIndexAssignmentNode arrayIndexAssignment) + private static void EmitArrayIndexAssignment(BoundArrayIndexAssignmentNode arrayIndexAssignment) { var array = EmitUnwrap(EmitExpression(arrayIndexAssignment.ArrayIndexAccess.Array)); var index = EmitUnwrap(EmitExpression(arrayIndexAssignment.ArrayIndexAccess.Index)); @@ -554,7 +554,7 @@ public static class QBEGenerator } } - private static void EmitBlock(BlockNode block, List? variables = null) + private static void EmitBlock(BoundBlockNode block, List? variables = null) { _variableScopes.Push(_variables.Count); if (variables != null) @@ -591,14 +591,14 @@ public static class QBEGenerator _codeIsReachable = false; } - private static void EmitDereferenceAssignment(DereferenceAssignmentNode dereferenceAssignment) + private static void EmitDereferenceAssignment(BoundDereferenceAssignmentNode dereferenceAssignment) { var pointer = EmitUnwrap(EmitExpression(dereferenceAssignment.Dereference.Expression)); var value = EmitUnwrap(EmitExpression(dereferenceAssignment.Value)); EmitCopy(dereferenceAssignment.Value.Type, value, pointer); } - private static void EmitIf(IfNode ifStatement) + private static void EmitIf(BoundIfNode ifStatement) { var trueLabel = LabelName(); var falseLabel = LabelName(); @@ -622,7 +622,7 @@ public static class QBEGenerator _builder.AppendLine(endLabel); } - private static void EmitMemberAssignment(MemberAssignmentNode memberAssignment) + private static void EmitMemberAssignment(BoundMemberAssignmentNode memberAssignment) { var structType = memberAssignment.MemberAccess.Expression.Type as NubStructType; Debug.Assert(structType != null); @@ -640,7 +640,7 @@ public static class QBEGenerator EmitCopy(memberAssignment.Value.Type, value, pointer); } - private static void EmitReturn(ReturnNode @return) + private static void EmitReturn(BoundReturnNode @return) { if (@return.Value.HasValue) { @@ -653,21 +653,21 @@ public static class QBEGenerator } } - private static void EmitVariableDeclaration(VariableDeclarationNode variableDeclaration) + private static void EmitVariableDeclaration(BoundVariableDeclarationNode variableDeclaration) { var tmp = VarName(); _builder.AppendLine($" {tmp} =l alloc8 {SizeOf(variableDeclaration.Type)}"); _variables.Push(new Variable(variableDeclaration.Name, new Val(tmp, variableDeclaration.Type))); } - private static void EmitVariableAssignment(VariableAssignmentNode variableAssignment) + private static void EmitVariableAssignment(BoundVariableAssignmentNode variableAssignment) { var value = EmitUnwrap(EmitExpression(variableAssignment.Value)); var variable = _variables.Single(x => x.Name == variableAssignment.Identifier.Name); EmitCopy(variableAssignment.Value.Type, value, variable.Val.Name); } - private static void EmitWhile(WhileNode whileStatement) + private static void EmitWhile(BoundWhileNode whileStatement) { var conditionLabel = LabelName(); var iterationLabel = LabelName(); @@ -688,35 +688,35 @@ public static class QBEGenerator _breakLabels.Pop(); } - private static Val EmitExpression(ExpressionNode expression) + private static Val EmitExpression(BoundExpressionNode expression) { return expression switch { - AddressOfNode addressOf => EmitAddressOf(addressOf), - AnonymousFuncNode anonymousFunc => EmitAnonymousFunc(anonymousFunc), - ArrayIndexAccessNode arrayIndex => EmitArrayIndexAccess(arrayIndex), - ArrayInitializerNode arrayInitializer => EmitArrayInitializer(arrayInitializer), - BinaryExpressionNode binaryExpression => EmitBinaryExpression(binaryExpression), - DereferenceNode dereference => EmitDereference(dereference), - FixedArrayInitializerNode fixedArrayInitializer => EmitFixedArrayInitializer(fixedArrayInitializer), - FuncCallNode funcCallExpression => EmitFuncCall(funcCallExpression), - IdentifierNode identifier => EmitIdentifier(identifier), - LiteralNode literal => EmitLiteral(literal), - StructInitializerNode structInitializer => EmitStructInitializer(structInitializer), - UnaryExpressionNode unaryExpression => EmitUnaryExpression(unaryExpression), - MemberAccessNode memberAccess => EmitMemberAccess(memberAccess), + BoundAddressOfNode addressOf => EmitAddressOf(addressOf), + BoundAnonymousFuncNode anonymousFunc => EmitAnonymousFunc(anonymousFunc), + BoundArrayIndexAccessNode arrayIndex => EmitArrayIndexAccess(arrayIndex), + BoundArrayInitializerNode arrayInitializer => EmitArrayInitializer(arrayInitializer), + BoundBinaryExpressionNode binaryExpression => EmitBinaryExpression(binaryExpression), + BoundDereferenceNode dereference => EmitDereference(dereference), + BoundFixedArrayInitializerNode fixedArrayInitializer => EmitFixedArrayInitializer(fixedArrayInitializer), + BoundFuncCallNode funcCallExpression => EmitFuncCall(funcCallExpression), + BoundIdentifierNode identifier => EmitIdentifier(identifier), + BoundLiteralNode literal => EmitLiteral(literal), + BoundStructInitializerNode structInitializer => EmitStructInitializer(structInitializer), + BoundUnaryExpressionNode unaryExpression => EmitUnaryExpression(unaryExpression), + BoundMemberAccessNode memberAccess => EmitMemberAccess(memberAccess), _ => throw new ArgumentOutOfRangeException(nameof(expression)) }; } - private static Val EmitAnonymousFunc(AnonymousFuncNode anonymousFunc) + private static Val EmitAnonymousFunc(BoundAnonymousFuncNode anonymousFunc) { var name = $"$anon_func{++_anonymousFuncIndex}"; _anonymousFunctions.Enqueue((anonymousFunc, name)); return new Val(name, anonymousFunc.Type, ValKind.Func); } - private static string EmitArrayIndexPointer(ArrayIndexAccessNode arrayIndexAccess) + private static string EmitArrayIndexPointer(BoundArrayIndexAccessNode arrayIndexAccess) { var array = EmitUnwrap(EmitExpression(arrayIndexAccess.Array)); var index = EmitUnwrap(EmitExpression(arrayIndexAccess.Index)); @@ -736,7 +736,7 @@ public static class QBEGenerator return pointer; } - private static Val EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess) + private static Val EmitArrayIndexAccess(BoundArrayIndexAccessNode arrayIndexAccess) { var pointer = EmitArrayIndexPointer(arrayIndexAccess); var outputName = VarName(); @@ -768,7 +768,7 @@ public static class QBEGenerator _builder.AppendLine(notOobLabel); } - private static Val EmitArrayInitializer(ArrayInitializerNode arrayInitializer) + private static Val EmitArrayInitializer(BoundArrayInitializerNode arrayInitializer) { var capacity = EmitUnwrap(EmitExpression(arrayInitializer.Capacity)); var elementSize = SizeOf(arrayInitializer.ElementType); @@ -789,7 +789,7 @@ public static class QBEGenerator return new Val(arrayPointer, arrayInitializer.Type); } - private static Val EmitDereference(DereferenceNode dereference) + private static Val EmitDereference(BoundDereferenceNode dereference) { var result = EmitUnwrap(EmitExpression(dereference.Expression)); var outputName = VarName(); @@ -797,23 +797,23 @@ public static class QBEGenerator return new Val(outputName, dereference.Type); } - private static Val EmitAddressOf(AddressOfNode addressOf) + private static Val EmitAddressOf(BoundAddressOfNode addressOf) { switch (addressOf.Expression) { - case ArrayIndexAccessNode arrayIndexAccess: + case BoundArrayIndexAccessNode arrayIndexAccess: var pointer = EmitArrayIndexPointer(arrayIndexAccess); return new Val(pointer, addressOf.Type); - case DereferenceNode dereference: + case BoundDereferenceNode dereference: return EmitExpression(dereference.Expression); - case IdentifierNode identifier: + case BoundIdentifierNode identifier: if (identifier.Namespace.HasValue) { throw new NotSupportedException("There is nothing to address in another namespace"); } return _variables.Single(x => x.Name == identifier.Name).Val; - case MemberAccessNode memberAccess: + case BoundMemberAccessNode memberAccess: var ptr = EmitMemberAccessPointer(memberAccess); return new Val(ptr, addressOf.Type); default: @@ -821,7 +821,7 @@ public static class QBEGenerator } } - private static Val EmitBinaryExpression(BinaryExpressionNode binaryExpression) + private static Val EmitBinaryExpression(BoundBinaryExpressionNode binaryExpression) { var left = EmitUnwrap(EmitExpression(binaryExpression.Left)); var right = EmitUnwrap(EmitExpression(binaryExpression.Right)); @@ -1035,9 +1035,9 @@ public static class QBEGenerator throw new NotSupportedException($"Binary operator {binaryExpression.Operator} for types {binaryExpression.Left.Type} and {binaryExpression.Right.Type} not supported"); } - private static Val EmitIdentifier(IdentifierNode identifier) + private static Val EmitIdentifier(BoundIdentifierNode identifier) { - if (_definitionTable.LookupFunc(identifier.Namespace.Or(_compilationUnit.Namespace), identifier.Name).TryGetValue(out var func)) + if (_definitionTable.LookupFunc(identifier.Namespace.Or(_syntaxTree.Namespace), identifier.Name).TryGetValue(out var func)) { return new Val(FuncName(func), identifier.Type, ValKind.Func); } @@ -1050,7 +1050,7 @@ public static class QBEGenerator throw new UnreachableException(); } - private static Val EmitLiteral(LiteralNode literal) + private static Val EmitLiteral(BoundLiteralNode literal) { if (literal.Type.IsInteger) { @@ -1092,7 +1092,7 @@ public static class QBEGenerator } } - private static Val EmitStructInitializer(StructInitializerNode structInitializer) + private static Val EmitStructInitializer(BoundStructInitializerNode structInitializer) { var structDefinition = _definitionTable.LookupStruct(structInitializer.StructType.Namespace, structInitializer.StructType.Name).GetValue(); @@ -1127,7 +1127,7 @@ public static class QBEGenerator return new Val(output, structInitializer.StructType); } - private static Val EmitUnaryExpression(UnaryExpressionNode unaryExpression) + private static Val EmitUnaryExpression(BoundUnaryExpressionNode unaryExpression) { var operand = EmitUnwrap(EmitExpression(unaryExpression.Operand)); var outputName = VarName(); @@ -1174,7 +1174,7 @@ public static class QBEGenerator throw new NotSupportedException($"Unary operator {unaryExpression.Operator} for type {unaryExpression.Operand.Type} not supported"); } - private static string EmitMemberAccessPointer(MemberAccessNode memberAccess) + private static string EmitMemberAccessPointer(BoundMemberAccessNode memberAccess) { var item = EmitUnwrap(EmitExpression(memberAccess.Expression)); switch (memberAccess.Expression.Type) @@ -1204,7 +1204,7 @@ public static class QBEGenerator } } - private static Val EmitMemberAccess(MemberAccessNode memberAccess) + private static Val EmitMemberAccess(BoundMemberAccessNode memberAccess) { var pointer = EmitMemberAccessPointer(memberAccess); @@ -1213,7 +1213,7 @@ public static class QBEGenerator return new Val(output, memberAccess.Type); } - private static Val EmitFixedArrayInitializer(FixedArrayInitializerNode fixedArrayInitializer) + private static Val EmitFixedArrayInitializer(BoundFixedArrayInitializerNode fixedArrayInitializer) { var totalSize = SizeOf(fixedArrayInitializer.Type); var outputName = VarName(); @@ -1230,7 +1230,7 @@ public static class QBEGenerator return new Val(outputName, fixedArrayInitializer.Type); } - private static Val EmitFuncCall(FuncCallNode funcCall) + private static Val EmitFuncCall(BoundFuncCallNode funcCall) { var funcType = (NubFuncType)funcCall.Expression.Type; diff --git a/src/Syntax/DefinitionTable.cs b/src/Syntax/DefinitionTable.cs index 5d19fd5..2651dd6 100644 --- a/src/Syntax/DefinitionTable.cs +++ b/src/Syntax/DefinitionTable.cs @@ -1,21 +1,23 @@ using Common; using Syntax.Parsing; using Syntax.Parsing.Node; +using Syntax.Typing; +using Syntax.Typing.BoundNode; namespace Syntax; public class DefinitionTable { - private readonly IEnumerable _compilationUnits; + private readonly IEnumerable _syntaxTrees; - public DefinitionTable(IEnumerable compilationUnits) + public DefinitionTable(IEnumerable syntaxTrees) { - _compilationUnits = compilationUnits; + _syntaxTrees = syntaxTrees; } public Optional LookupFunc(string @namespace, string name) { - var definition = _compilationUnits + var definition = _syntaxTrees .Where(c => c.Namespace == @namespace) .SelectMany(c => c.Definitions) .OfType() @@ -26,7 +28,7 @@ public class DefinitionTable public Optional LookupStruct(string @namespace, string name) { - var definition = _compilationUnits + var definition = _syntaxTrees .Where(c => c.Namespace == @namespace) .SelectMany(c => c.Definitions) .OfType() @@ -37,8 +39,47 @@ public class DefinitionTable public IEnumerable GetStructs() { - return _compilationUnits + return _syntaxTrees .SelectMany(c => c.Definitions) .OfType(); } +} + +public class BoundDefinitionTable +{ + private readonly IEnumerable _syntaxTrees; + + public BoundDefinitionTable(IEnumerable syntaxTrees) + { + _syntaxTrees = syntaxTrees; + } + + public Optional LookupFunc(string @namespace, string name) + { + var definition = _syntaxTrees + .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 = _syntaxTrees + .Where(c => c.Namespace == @namespace) + .SelectMany(c => c.Definitions) + .OfType() + .SingleOrDefault(f => f.Name == name); + + return Optional.OfNullable(definition); + } + + public IEnumerable GetStructs() + { + return _syntaxTrees + .SelectMany(c => c.Definitions) + .OfType(); + } } \ No newline at end of file diff --git a/src/Syntax/Parsing/CompilationUnit.cs b/src/Syntax/Parsing/CompilationUnit.cs deleted file mode 100644 index 4b994cf..0000000 --- a/src/Syntax/Parsing/CompilationUnit.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Syntax.Parsing.Node; - -namespace Syntax.Parsing; - -public class CompilationUnit(string @namespace, List definitions) -{ - public string Namespace { get; } = @namespace; - public List Definitions { get; } = definitions; -} \ No newline at end of file diff --git a/src/Syntax/Parsing/Node/DefinitionNode.cs b/src/Syntax/Parsing/Node/Definition.cs similarity index 100% rename from src/Syntax/Parsing/Node/DefinitionNode.cs rename to src/Syntax/Parsing/Node/Definition.cs diff --git a/src/Syntax/Parsing/Node/ExpressionNode.cs b/src/Syntax/Parsing/Node/Expression.cs similarity index 90% rename from src/Syntax/Parsing/Node/ExpressionNode.cs rename to src/Syntax/Parsing/Node/Expression.cs index 09c7a89..d77e12a 100644 --- a/src/Syntax/Parsing/Node/ExpressionNode.cs +++ b/src/Syntax/Parsing/Node/Expression.cs @@ -4,20 +4,9 @@ using Syntax.Typing; namespace Syntax.Parsing.Node; -public abstract record ExpressionNode(IEnumerable Tokens) : Node(Tokens) -{ - private NubType? _type; - public NubType Type - { - get => _type ?? throw new Exception("Tried to access expression type before type was populated"); - set => _type = value; - } -} - +public abstract record ExpressionNode(IEnumerable Tokens) : Node(Tokens); public abstract record LValueNode(IEnumerable Tokens) : ExpressionNode(Tokens); -public record DereferenceNode(IEnumerable Tokens, ExpressionNode Expression) : LValueNode(Tokens); - public record BinaryExpressionNode(IEnumerable Tokens, ExpressionNode Left, BinaryExpressionOperator Operator, ExpressionNode Right) : ExpressionNode(Tokens); public enum BinaryExpressionOperator @@ -52,3 +41,4 @@ public record FixedArrayInitializerNode(IEnumerable Tokens, NubType Eleme public record LiteralNode(IEnumerable Tokens, string Literal, LiteralKind Kind) : ExpressionNode(Tokens); public record MemberAccessNode(IEnumerable Tokens, ExpressionNode Expression, string Member) : LValueNode(Tokens); public record StructInitializerNode(IEnumerable Tokens, NubStructType StructType, Dictionary Initializers) : ExpressionNode(Tokens); +public record DereferenceNode(IEnumerable Tokens, ExpressionNode Expression) : LValueNode(Tokens); diff --git a/src/Syntax/Parsing/Node/StatementNode.cs b/src/Syntax/Parsing/Node/Statement.cs similarity index 100% rename from src/Syntax/Parsing/Node/StatementNode.cs rename to src/Syntax/Parsing/Node/Statement.cs diff --git a/src/Syntax/Parsing/Parser.cs b/src/Syntax/Parsing/Parser.cs index 952866a..75cf666 100644 --- a/src/Syntax/Parsing/Parser.cs +++ b/src/Syntax/Parsing/Parser.cs @@ -15,7 +15,7 @@ public static class Parser private static IEnumerable _tokens = []; private static int _index; - public static CompilationUnit? ParseFile(IEnumerable tokens, out IEnumerable diagnostics) + public static SyntaxTree? ParseFile(IEnumerable tokens, string filePath, out IEnumerable diagnostics) { _tokens = tokens; _namespace = null!; @@ -46,7 +46,7 @@ public static class Parser } diagnostics = _diagnostics; - return new CompilationUnit(_namespace, definitions); + return new SyntaxTree(filePath, _namespace, definitions); } catch (ParseException ex) { diff --git a/src/Syntax/Parsing/SyntaxTree.cs b/src/Syntax/Parsing/SyntaxTree.cs new file mode 100644 index 0000000..914a9ed --- /dev/null +++ b/src/Syntax/Parsing/SyntaxTree.cs @@ -0,0 +1,5 @@ +using Syntax.Parsing.Node; + +namespace Syntax.Parsing; + +public record SyntaxTree(string FilePath, string Namespace, List Definitions); diff --git a/src/Syntax/Typing/Binder.cs b/src/Syntax/Typing/Binder.cs new file mode 100644 index 0000000..df72194 --- /dev/null +++ b/src/Syntax/Typing/Binder.cs @@ -0,0 +1,400 @@ +using Common; +using Syntax.Parsing; +using Syntax.Parsing.Node; +using Syntax.Typing.BoundNode; +using UnaryExpressionNode = Syntax.Parsing.Node.UnaryExpressionNode; + +namespace Syntax.Typing; + +public static class Binder +{ + private static SyntaxTree _syntaxTree = null!; + private static DefinitionTable _definitionTable = null!; + + private static Dictionary _variables = new(); + + public static BoundSyntaxTree Bind(SyntaxTree syntaxTree, DefinitionTable definitionTable) + { + _syntaxTree = syntaxTree; + _definitionTable = definitionTable; + var definitions = new List(); + + foreach (var definition in syntaxTree.Definitions) + { + definitions.Add(BindDefinition(definition)); + } + + return new BoundSyntaxTree(syntaxTree.FilePath, syntaxTree.Namespace, definitions); + } + + private static BoundDefinitionNode BindDefinition(DefinitionNode node) + { + return node switch + { + ExternFuncDefinitionNode definition => BindExternFuncDefinition(definition), + LocalFuncDefinitionNode definition => BindLocalFuncDefinition(definition), + StructDefinitionNode definition => BindStruct(definition), + _ => throw new ArgumentOutOfRangeException(nameof(node)) + }; + } + + private static BoundStructDefinitionNode BindStruct(StructDefinitionNode node) + { + var structFields = new List(); + + foreach (var structField in node.Fields) + { + var value = Optional.Empty(); + + if (structField.Value.HasValue) + { + value = BindExpression(structField.Value.Value); + } + + structFields.Add(new BoundStructField(structField.Name, structField.Type, value)); + } + + return new BoundStructDefinitionNode(node.Tokens, node.Documentation, node.Namespace, node.Name, structFields); + } + + private static BoundExternFuncDefinitionNode BindExternFuncDefinition(ExternFuncDefinitionNode node) + { + var parameters = new List(); + + foreach (var parameter in node.Parameters) + { + parameters.Add(new BoundFuncParameter(parameter.Name, parameter.Type)); + } + + return new BoundExternFuncDefinitionNode(node.Tokens, node.Documentation, node.Namespace, node.Name, node.CallName, parameters, node.ReturnType); + } + + private static BoundLocalFuncDefinitionNode BindLocalFuncDefinition(LocalFuncDefinitionNode node) + { + _variables.Clear(); + + var parameters = new List(); + + foreach (var parameter in node.Parameters) + { + parameters.Add(new BoundFuncParameter(parameter.Name, parameter.Type)); + _variables[parameter.Name] = parameter.Type; + } + + var body = BindBlock(node.Body); + + return new BoundLocalFuncDefinitionNode(node.Tokens, node.Documentation, node.Namespace, node.Name, parameters, body, node.ReturnType, node.Exported); + } + + private static BoundBlockNode BindBlock(BlockNode node) + { + var statements = new List(); + + foreach (var statement in node.Statements) + { + statements.Add(BindStatement(statement)); + } + + return new BoundBlockNode(node.Tokens, statements); + } + + private static BoundStatementNode BindStatement(StatementNode node) + { + return node switch + { + ArrayIndexAssignmentNode statement => BindArrayIndex(statement), + BreakNode statement => BindBreak(statement), + ContinueNode statement => BindContinue(statement), + DereferenceAssignmentNode statement => BindDereferenceAssignment(statement), + IfNode statement => BindIf(statement), + MemberAssignmentNode statement => BindMemberAssignment(statement), + ReturnNode statement => BindReturn(statement), + StatementExpressionNode statement => BindStatementExpression(statement), + VariableAssignmentNode statement => BindVariableAssignment(statement), + VariableDeclarationNode statement => BindVariableDeclaration(statement), + WhileNode statement => BindWhile(statement), + _ => throw new ArgumentOutOfRangeException(nameof(node)) + }; + } + + private static BoundArrayIndexAssignmentNode BindArrayIndex(ArrayIndexAssignmentNode statement) + { + return new BoundArrayIndexAssignmentNode(statement.Tokens, BindArrayIndexAccess(statement.ArrayIndexAccess), BindExpression(statement.Value)); + } + + private static BoundBreakNode BindBreak(BreakNode statement) + { + return new BoundBreakNode(statement.Tokens); + } + + private static BoundContinueNode BindContinue(ContinueNode statement) + { + return new BoundContinueNode(statement.Tokens); + } + + private static BoundDereferenceAssignmentNode BindDereferenceAssignment(DereferenceAssignmentNode statement) + { + return new BoundDereferenceAssignmentNode(statement.Tokens, BindDereference(statement.Dereference), BindExpression(statement.Value)); + } + + private static BoundIfNode BindIf(IfNode statement) + { + var elseStatement = Optional.Empty>(); + + if (statement.Else.HasValue) + { + elseStatement = statement.Else.Value.Match> + ( + elseIf => BindIf(elseIf), + @else => BindBlock(@else) + ); + } + + return new BoundIfNode(statement.Tokens, BindExpression(statement.Condition), BindBlock(statement.Body), elseStatement); + } + + private static BoundMemberAssignmentNode BindMemberAssignment(MemberAssignmentNode statement) + { + return new BoundMemberAssignmentNode(statement.Tokens, BindMemberAccess(statement.MemberAccess), BindExpression(statement.Value)); + } + + private static BoundReturnNode BindReturn(ReturnNode statement) + { + var value = Optional.Empty(); + + if (statement.Value.HasValue) + { + value = BindExpression(statement.Value.Value); + } + + return new BoundReturnNode(statement.Tokens, value); + } + + private static BoundStatementExpressionNode BindStatementExpression(StatementExpressionNode statement) + { + return new BoundStatementExpressionNode(statement.Tokens, BindExpression(statement.Expression)); + } + + private static BoundVariableAssignmentNode BindVariableAssignment(VariableAssignmentNode statement) + { + return new BoundVariableAssignmentNode(statement.Tokens, BindIdentifier(statement.Identifier), BindExpression(statement.Value)); + } + + private static BoundVariableDeclarationNode BindVariableDeclaration(VariableDeclarationNode statement) + { + _variables[statement.Name] = statement.Type; + return new BoundVariableDeclarationNode(statement.Tokens, statement.Name, statement.Type); + } + + private static BoundWhileNode BindWhile(WhileNode statement) + { + return new BoundWhileNode(statement.Tokens, BindExpression(statement.Condition), BindBlock(statement.Body)); + } + + private static BoundExpressionNode BindExpression(ExpressionNode node) + { + return node switch + { + AddressOfNode expression => BindAddressOf(expression), + AnonymousFuncNode expression => BindAnonymousFunc(expression), + ArrayIndexAccessNode expression => BindArrayIndexAccess(expression), + ArrayInitializerNode expression => BindArrayInitializer(expression), + BinaryExpressionNode expression => BindBinaryExpression(expression), + DereferenceNode expression => BindDereference(expression), + FixedArrayInitializerNode expression => BindFixedArrayInitializer(expression), + FuncCallNode expression => BindFuncCall(expression), + IdentifierNode expression => BindIdentifier(expression), + LiteralNode expression => BindLiteral(expression), + MemberAccessNode expression => BindMemberAccess(expression), + StructInitializerNode expression => BindStructInitializer(expression), + UnaryExpressionNode expression => BindUnaryExpression(expression), + _ => throw new ArgumentOutOfRangeException(nameof(node)) + }; + } + + private static BoundAddressOfNode BindAddressOf(AddressOfNode expression) + { + var inner = (BoundLValueNode)BindExpression(expression.Expression); + return new BoundAddressOfNode(expression.Tokens, new NubPointerType(inner.Type), inner); + } + + private static BoundAnonymousFuncNode BindAnonymousFunc(AnonymousFuncNode expression) + { + var parameters = new List(); + var parameterTypes = new List(); + + foreach (var parameter in expression.Parameters) + { + var boundParameter = new BoundFuncParameter(parameter.Name, parameter.Type); + parameters.Add(boundParameter); + parameterTypes.Add(boundParameter.Type); + } + + var body = BindBlock(expression.Body); + + return new BoundAnonymousFuncNode(expression.Tokens, new NubFuncType(expression.ReturnType, parameterTypes), parameters, body, expression.ReturnType); + } + + private static BoundArrayIndexAccessNode BindArrayIndexAccess(ArrayIndexAccessNode expression) + { + var boundArray = BindExpression(expression.Array); + var elementType = ((NubArrayType)boundArray.Type).ElementType; + return new BoundArrayIndexAccessNode(expression.Tokens, elementType, boundArray, BindExpression(expression.Index)); + } + + private static BoundArrayInitializerNode BindArrayInitializer(ArrayInitializerNode expression) + { + return new BoundArrayInitializerNode(expression.Tokens, new NubArrayType(expression.ElementType), BindExpression(expression.Capacity), expression.ElementType); + } + + private static BoundBinaryExpressionNode BindBinaryExpression(BinaryExpressionNode expression) + { + var boundLeft = BindExpression(expression.Left); + var boundRight = BindExpression(expression.Right); + return new BoundBinaryExpressionNode(expression.Tokens, boundLeft.Type, boundLeft, expression.Operator, boundRight); + } + + private static BoundDereferenceNode BindDereference(DereferenceNode expression) + { + var boundExpression = BindExpression(expression.Expression); + var dereferencedType = ((NubPointerType)boundExpression.Type).BaseType; + return new BoundDereferenceNode(expression.Tokens, dereferencedType, boundExpression); + } + + private static BoundFixedArrayInitializerNode BindFixedArrayInitializer(FixedArrayInitializerNode expression) + { + return new BoundFixedArrayInitializerNode(expression.Tokens, new NubArrayType(expression.ElementType), expression.ElementType, expression.Capacity); + } + + private static BoundFuncCallNode BindFuncCall(FuncCallNode expression) + { + var boundExpression = BindExpression(expression.Expression); + + var returnType = ((NubFuncType)boundExpression.Type).ReturnType; + + var parameters = new List(); + + foreach (var parameter in expression.Parameters) + { + parameters.Add(BindExpression(parameter)); + } + + return new BoundFuncCallNode(expression.Tokens, returnType, boundExpression, parameters); + } + + private static BoundIdentifierNode BindIdentifier(IdentifierNode expression) + { + NubType? type = null; + + var definition = _definitionTable.LookupFunc(expression.Namespace.Or(_syntaxTree.Namespace), expression.Name); + if (definition.HasValue) + { + type = new NubFuncType(definition.Value.ReturnType, definition.Value.Parameters.Select(p => p.Type).ToList()); + } + + if (type == null && !expression.Namespace.HasValue) + { + type = _variables[expression.Name]; + } + + if (type == null) + { + throw new NotImplementedException("Diagnostics not implemented"); + } + + return new BoundIdentifierNode(expression.Tokens, type, expression.Namespace, expression.Name); + } + + private static BoundLiteralNode BindLiteral(LiteralNode expression) + { + throw new NotImplementedException("Literal requires context"); + } + + private static BoundMemberAccessNode BindMemberAccess(MemberAccessNode expression) + { + var boundExpression = BindExpression(expression.Expression); + + NubType? type = null; + + switch (boundExpression.Type) + { + case NubArrayType: + { + if (expression.Member == "count") + { + type = NubPrimitiveType.I64; + } + + break; + } + case NubStructType structType: + { + var defOpt = _definitionTable.LookupStruct(structType.Namespace, structType.Name); + if (!defOpt.TryGetValue(out var definition)) + { + throw new NotImplementedException("Diagnostics not implemented"); + } + + var field = definition.Fields.FirstOrDefault(f => f.Name == expression.Member); + if (field == null) + { + throw new NotImplementedException("Diagnostics not implemented"); + } + + type = field.Type; + break; + } + } + + if (type == null) + { + throw new NotImplementedException("Diagnostics not implemented"); + } + + return new BoundMemberAccessNode(expression.Tokens, type, boundExpression, expression.Member); + } + + private static BoundStructInitializerNode BindStructInitializer(StructInitializerNode expression) + { + var initializers = new Dictionary(); + + foreach (var (member, initializer) in expression.Initializers) + { + initializers[member] = BindExpression(initializer); + } + + return new BoundStructInitializerNode(expression.Tokens, expression.StructType, expression.StructType, initializers); + } + + private static BoundUnaryExpressionNode BindUnaryExpression(UnaryExpressionNode expression) + { + var boundOperand = BindExpression(expression.Operand); + + NubType? type = null; + + switch (expression.Operator) + { + case UnaryExpressionOperator.Negate: + { + if (boundOperand.Type.IsNumber) + { + type = boundOperand.Type; + } + + break; + } + case UnaryExpressionOperator.Invert: + { + type = new NubPrimitiveType(PrimitiveTypeKind.Bool); + break; + } + } + + if (type == null) + { + throw new NotImplementedException("Diagnostics not implemented"); + } + + return new BoundUnaryExpressionNode(expression.Tokens, type, expression.Operator, boundOperand); + } +} diff --git a/src/Syntax/Typing/BoundNode/Definition.cs b/src/Syntax/Typing/BoundNode/Definition.cs new file mode 100644 index 0000000..dcb2356 --- /dev/null +++ b/src/Syntax/Typing/BoundNode/Definition.cs @@ -0,0 +1,33 @@ +using Common; +using Syntax.Tokenization; + +namespace Syntax.Typing.BoundNode; + +public abstract record BoundDefinitionNode(IEnumerable Tokens, Optional Documentation, string Namespace) : BoundNode(Tokens); + +public record BoundFuncParameter(string Name, NubType Type) +{ + public override string ToString() => $"{Name}: {Type}"; +} + +public interface IBoundFuncSignature +{ + public string Name { get; } + public List Parameters { get; } + public NubType ReturnType { get; } + + public string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){": " + ReturnType}"; +} + +public record BoundLocalFuncDefinitionNode(IEnumerable Tokens, Optional Documentation, string Namespace, string Name, List Parameters, BoundBlockNode Body, NubType ReturnType, bool Exported) : BoundDefinitionNode(Tokens, Documentation, Namespace), IBoundFuncSignature +{ + public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){": " + ReturnType}"; +} + +public record BoundExternFuncDefinitionNode(IEnumerable Tokens, Optional Documentation, string Namespace, string Name, string CallName, List Parameters, NubType ReturnType) : BoundDefinitionNode(Tokens, Documentation, Namespace), IBoundFuncSignature +{ + public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){": " + ReturnType}"; +} + +public record BoundStructField(string Name, NubType Type, Optional Value); +public record BoundStructDefinitionNode(IEnumerable Tokens, Optional Documentation, string Namespace, string Name, List Fields) : BoundDefinitionNode(Tokens, Documentation, Namespace); diff --git a/src/Syntax/Typing/BoundNode/Expression.cs b/src/Syntax/Typing/BoundNode/Expression.cs new file mode 100644 index 0000000..5a23807 --- /dev/null +++ b/src/Syntax/Typing/BoundNode/Expression.cs @@ -0,0 +1,21 @@ +using Common; +using Syntax.Parsing.Node; +using Syntax.Tokenization; + +namespace Syntax.Typing.BoundNode; + +public abstract record BoundExpressionNode(IEnumerable Tokens, NubType Type) : BoundNode(Tokens); +public abstract record BoundLValueNode(IEnumerable Tokens, NubType Type) : BoundExpressionNode(Tokens, Type); +public record BoundBinaryExpressionNode(IEnumerable Tokens, NubType Type, BoundExpressionNode Left, BinaryExpressionOperator Operator, BoundExpressionNode Right) : BoundExpressionNode(Tokens, Type); +public record BoundUnaryExpressionNode(IEnumerable Tokens, NubType Type, UnaryExpressionOperator Operator, BoundExpressionNode Operand) : BoundExpressionNode(Tokens, Type); +public record BoundFuncCallNode(IEnumerable Tokens, NubType Type, BoundExpressionNode Expression, List Parameters) : BoundExpressionNode(Tokens, Type); +public record BoundIdentifierNode(IEnumerable Tokens, NubType Type, Optional Namespace, string Name) : BoundLValueNode(Tokens, Type); +public record BoundArrayInitializerNode(IEnumerable Tokens, NubType Type, BoundExpressionNode Capacity, NubType ElementType) : BoundExpressionNode(Tokens, Type); +public record BoundArrayIndexAccessNode(IEnumerable Tokens, NubType Type, BoundExpressionNode Array, BoundExpressionNode Index) : BoundLValueNode(Tokens, Type); +public record BoundAnonymousFuncNode(IEnumerable Tokens, NubType Type, List Parameters, BoundBlockNode Body, NubType ReturnType) : BoundExpressionNode(Tokens, Type); +public record BoundAddressOfNode(IEnumerable Tokens, NubType Type, BoundLValueNode Expression) : BoundExpressionNode(Tokens, Type); +public record BoundFixedArrayInitializerNode(IEnumerable Tokens, NubType Type, NubType ElementType, int Capacity) : BoundExpressionNode(Tokens, Type); +public record BoundLiteralNode(IEnumerable Tokens, NubType Type, string Literal, LiteralKind Kind) : BoundExpressionNode(Tokens, Type); +public record BoundMemberAccessNode(IEnumerable Tokens, NubType Type, BoundExpressionNode Expression, string Member) : BoundLValueNode(Tokens, Type); +public record BoundStructInitializerNode(IEnumerable Tokens, NubType Type, NubStructType StructType, Dictionary Initializers) : BoundExpressionNode(Tokens, Type); +public record BoundDereferenceNode(IEnumerable Tokens, NubType Type, BoundExpressionNode Expression) : BoundLValueNode(Tokens, Type); diff --git a/src/Syntax/Typing/BoundNode/Node.cs b/src/Syntax/Typing/BoundNode/Node.cs new file mode 100644 index 0000000..6030c31 --- /dev/null +++ b/src/Syntax/Typing/BoundNode/Node.cs @@ -0,0 +1,6 @@ +using Syntax.Tokenization; + +namespace Syntax.Typing.BoundNode; + +public abstract record BoundNode(IEnumerable Tokens); +public record BoundBlockNode(IEnumerable Tokens, List Statements) : BoundNode(Tokens); diff --git a/src/Syntax/Typing/BoundNode/Statement.cs b/src/Syntax/Typing/BoundNode/Statement.cs new file mode 100644 index 0000000..151626a --- /dev/null +++ b/src/Syntax/Typing/BoundNode/Statement.cs @@ -0,0 +1,17 @@ +using Common; +using Syntax.Tokenization; + +namespace Syntax.Typing.BoundNode; + +public record BoundStatementNode(IEnumerable Tokens) : BoundNode(Tokens); +public record BoundStatementExpressionNode(IEnumerable Tokens, BoundExpressionNode Expression) : BoundStatementNode(Tokens); +public record BoundReturnNode(IEnumerable Tokens, Optional Value) : BoundStatementNode(Tokens); +public record BoundMemberAssignmentNode(IEnumerable Tokens, BoundMemberAccessNode MemberAccess, BoundExpressionNode Value) : BoundStatementNode(Tokens); +public record BoundIfNode(IEnumerable Tokens, BoundExpressionNode Condition, BoundBlockNode Body, Optional> Else) : BoundStatementNode(Tokens); +public record BoundDereferenceAssignmentNode(IEnumerable Tokens, BoundDereferenceNode Dereference, BoundExpressionNode Value) : BoundStatementNode(Tokens); +public record BoundVariableAssignmentNode(IEnumerable Tokens, BoundIdentifierNode Identifier, BoundExpressionNode Value) : BoundStatementNode(Tokens); +public record BoundVariableDeclarationNode(IEnumerable Tokens, string Name, NubType Type) : BoundStatementNode(Tokens); +public record BoundContinueNode(IEnumerable Tokens) : BoundStatementNode(Tokens); +public record BoundBreakNode(IEnumerable Tokens) : BoundStatementNode(Tokens); +public record BoundArrayIndexAssignmentNode(IEnumerable Tokens, BoundArrayIndexAccessNode ArrayIndexAccess, BoundExpressionNode Value) : BoundStatementNode(Tokens); +public record BoundWhileNode(IEnumerable Tokens, BoundExpressionNode Condition, BoundBlockNode Body) : BoundStatementNode(Tokens); diff --git a/src/Syntax/Typing/BoundSyntaxTree.cs b/src/Syntax/Typing/BoundSyntaxTree.cs new file mode 100644 index 0000000..0dd20b5 --- /dev/null +++ b/src/Syntax/Typing/BoundSyntaxTree.cs @@ -0,0 +1,5 @@ +using Syntax.Typing.BoundNode; + +namespace Syntax.Typing; + +public record BoundSyntaxTree(string FilePath, string Namespace, List Definitions); diff --git a/src/Syntax/Typing/TypeChecker.cs b/src/Syntax/Typing/TypeChecker.cs index b0d111c..e935a2b 100644 --- a/src/Syntax/Typing/TypeChecker.cs +++ b/src/Syntax/Typing/TypeChecker.cs @@ -1,633 +1,633 @@ -using System.Diagnostics; -using Syntax.Diagnostics; -using Syntax.Parsing; -using Syntax.Parsing.Node; -using Syntax.Tokenization; - -namespace Syntax.Typing; - -public static class TypeChecker -{ - private static CompilationUnit _compilationUnit = null!; - private static DefinitionTable _definitionTable = null!; - - private static Dictionary _variables = new(); - private static List _diagnostics = []; - private static NubType? _currentFunctionReturnType; - private static Queue _anonymousFunctions = []; - - public static void Check(CompilationUnit compilationUnit, DefinitionTable definitionTable, out IEnumerable diagnostics) - { - _compilationUnit = compilationUnit; - _definitionTable = definitionTable; - - _variables = new Dictionary(); - _diagnostics = []; - _currentFunctionReturnType = null; - _anonymousFunctions = []; - - foreach (var structDef in compilationUnit.Definitions.OfType()) - { - CheckStructDef(structDef); - } - - foreach (var funcDef in compilationUnit.Definitions.OfType()) - { - CheckFuncDef(funcDef.Parameters, funcDef.Body, funcDef.ReturnType); - } - - while (_anonymousFunctions.TryDequeue(out var func)) - { - CheckFuncDef(func.Parameters, func.Body, func.ReturnType); - - } - - diagnostics = _diagnostics; - } - - private static void CheckStructDef(StructDefinitionNode structDef) - { - var fields = new Dictionary(); - foreach (var field in structDef.Fields) - { - if (fields.ContainsKey(field.Name)) - { - ReportError($"Duplicate field '{field.Name}' in struct '{structDef.Name}'", structDef); - continue; - } - - if (field.Value.HasValue) - { - 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); - } - } - - fields[field.Name] = field.Type; - } - } - - private static void CheckFuncDef(List parameters, BlockNode body, NubType returnType) - { - _variables.Clear(); - _currentFunctionReturnType = returnType; - - foreach (var param in parameters) - { - _variables[param.Name] = param.Type; - } - - CheckBlock(body); - } - - private static void CheckBlock(BlockNode block) - { - foreach (var statement in block.Statements) - { - CheckStatement(statement); - } - } - - private static void CheckStatement(StatementNode statement) - { - switch (statement) - { - case ArrayIndexAssignmentNode arrayIndexAssignment: - CheckArrayIndexAssignment(arrayIndexAssignment); - break; - case VariableAssignmentNode variableAssignment: - CheckVariableAssignment(variableAssignment); - break; - case VariableDeclarationNode variableDeclaration: - CheckVariableVariableDeclaration(variableDeclaration); - break; - case IfNode ifNode: - CheckIf(ifNode); - break; - case MemberAssignmentNode memberAssignment: - CheckMemberAssignment(memberAssignment); - break; - case WhileNode whileNode: - CheckWhile(whileNode); - break; - case ReturnNode returnNode: - CheckReturn(returnNode); - break; - case StatementExpressionNode statementExpression: - CheckExpression(statementExpression.Expression); - break; - case BreakNode: - case ContinueNode: - break; - case DereferenceAssignmentNode dereferenceAssignment: - CheckDereferenceAssignment(dereferenceAssignment); - break; - default: - ReportError($"Unsupported statement type: {statement.GetType().Name}", statement); - break; - } - } - - private static void CheckMemberAssignment(MemberAssignmentNode memberAssignment) - { - var memberType = CheckExpression(memberAssignment.MemberAccess); - if (memberType == null) return; - var valueType = CheckExpression(memberAssignment.Value, memberType); - if (valueType == null) return; - - if (!NubType.IsCompatibleWith(memberType, valueType)) - { - ReportError($"'{valueType}' is not assignable to member of type '{memberType}'", memberAssignment); - } - } - - private static void CheckArrayIndexAssignment(ArrayIndexAssignmentNode arrayIndexAssignment) - { - var itemType = CheckExpression(arrayIndexAssignment.ArrayIndexAccess); - if (itemType == null) return; - var valueType = CheckExpression(arrayIndexAssignment.Value, itemType); - if (valueType == null) return; - - if (!NubType.IsCompatibleWith(itemType, valueType)) - { - ReportError($"'{valueType}' is not assignable to array of type '{itemType}'", arrayIndexAssignment); - } - } - - private static void CheckVariableAssignment(VariableAssignmentNode variableAssignment) - { - if (!_variables.TryGetValue(variableAssignment.Identifier.Name, out var variable)) - { - ReportError($"Variable '{variableAssignment.Identifier}' is not declared", variableAssignment); - return; - } - - var valueType = CheckExpression(variableAssignment.Value, variable); - if (valueType == null) return; - - if (!NubType.IsCompatibleWith(variableAssignment.Value.Type, variable)) - { - ReportError($"Cannot assign expression of type '{variableAssignment.Value.Type}' to variable '{variableAssignment.Identifier}' with type '{variable}'", variableAssignment); - } - } - - private static void CheckVariableVariableDeclaration(VariableDeclarationNode variableDeclaration) - { - if (_variables.TryGetValue(variableDeclaration.Name, out var variable)) - { - ReportError($"Cannot redeclare variable '{variable}'", variableDeclaration); - } - - _variables[variableDeclaration.Name] = variableDeclaration.Type; - } - - private static NubType? CheckDereference(DereferenceNode dereference) - { - var exprType = CheckExpression(dereference.Expression); - if (exprType == null) return null; - - if (exprType is not NubPointerType nubPointerType) - { - ReportError($"Cannot dereference a non-pointer type {exprType}", dereference); - return null; - } - - return nubPointerType.BaseType; - } - - private static NubType CheckFixedInitializerArray(FixedArrayInitializerNode fixedArrayInitializer) - { - return new NubFixedArrayType(fixedArrayInitializer.ElementType, fixedArrayInitializer.Capacity); - } - - private static NubType? CheckFuncCall(FuncCallNode funcCall) - { - var identType = CheckExpression(funcCall.Expression); - if (identType is not NubFuncType funcType) - { - ReportError("Cannot call function on non-function type", funcCall); - return null; - } - - if (funcCall.Parameters.Count != funcType.Parameters.Count) - { - ReportError($"{funcType} expects {funcType.Parameters.Count} arguments, but was called with {funcType.Parameters.Count} arguments", funcCall); - return null; - } - - for (var i = 0; i < funcCall.Parameters.Count; i++) - { - var parameter = funcCall.Parameters[i]; - var parameterType = CheckExpression(parameter); - if (parameterType == null) return null; - - if (!NubType.IsCompatibleWith(parameterType, funcType.Parameters[i])) - { - ReportError($"'{parameterType}' does not match expected type {funcType.Parameters[i]}", funcCall); - return null; - } - } - - return funcType.ReturnType; - } - - private static void CheckIf(IfNode ifNode) - { - 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); - } - - CheckBlock(ifNode.Body); - - if (ifNode.Else.HasValue) - { - var elseValue = ifNode.Else.Value; - elseValue.Match(CheckIf, CheckBlock); - } - } - - private static void CheckWhile(WhileNode whileNode) - { - 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); - } - - CheckBlock(whileNode.Body); - } - - private static void CheckReturn(ReturnNode returnNode) - { - if (returnNode.Value.HasValue) - { - var returnType = CheckExpression(returnNode.Value.Value, _currentFunctionReturnType); - if (returnType == null) return; - - if (_currentFunctionReturnType == null) - { - ReportError("Cannot return a value from a function with no return type", returnNode.Value.Value); - return; - } - - if (!NubType.IsCompatibleWith(returnType, _currentFunctionReturnType)) - { - ReportError($"Return value of type '{returnType}' is not compatible with function return type '{_currentFunctionReturnType}'", returnNode.Value.Value); - } - } - else if (_currentFunctionReturnType != null) - { - ReportError($"Function must return a value of type '{_currentFunctionReturnType}'", returnNode); - } - } - - private static void CheckDereferenceAssignment(DereferenceAssignmentNode dereferenceAssignment) - { - var dereferenceType = CheckExpression(dereferenceAssignment.Dereference); - if (dereferenceType == null) return; - var valueType = CheckExpression(dereferenceAssignment.Value, dereferenceType); - if (valueType == null) return; - - if (!NubType.IsCompatibleWith(dereferenceType, valueType)) - { - ReportError($"'{valueType}' is not assignable to type '{dereferenceType}'", dereferenceAssignment); - } - } - - private static NubType? CheckExpression(ExpressionNode expression, NubType? expectedType = null) - { - var resultType = expression switch - { - 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() - }; - - if (resultType != null) - { - expression.Type = resultType; - } - - return resultType; - } - - private static NubType CheckAnonymousFunc(AnonymousFuncNode anonymousFunc) - { - _anonymousFunctions.Enqueue(anonymousFunc); - return new NubFuncType(anonymousFunc.ReturnType, anonymousFunc.Parameters.Select(p => p.Type).ToList()); - } - - private static NubType? CheckLiteral(LiteralNode literal, NubType? expectedType = null) - { - if (expectedType != null) - { - if (expectedType.IsNumber && literal.Kind is not LiteralKind.Integer and not LiteralKind.Float) - { - ReportError("Expression expects a numeric literal", literal); - return null; - } - - if (expectedType.IsInteger && literal.Kind == LiteralKind.Float) - { - if (literal.Kind == LiteralKind.Float) - { - ReportWarning("Float used in integer context. Everything after the '.' will be ignored", literal); - } - } - - return expectedType; - } - - return literal.Kind switch - { - LiteralKind.Integer => NubPrimitiveType.I64, - LiteralKind.Float => NubPrimitiveType.F64, - LiteralKind.String => new NubCStringType(), - LiteralKind.Bool => NubPrimitiveType.Bool, - _ => throw new ArgumentOutOfRangeException() - }; - } - - private static NubType? CheckArrayIndex(ArrayIndexAccessNode arrayIndexAccess) - { - var expressionType = CheckExpression(arrayIndexAccess.Array); - if (expressionType == null) return null; - var indexType = CheckExpression(arrayIndexAccess.Index, NubPrimitiveType.U64); - if (indexType is { IsInteger: false }) - { - ReportError("Array index type must be a number", arrayIndexAccess.Index); - } - - if (expressionType is NubArrayType arrayType) - { - return arrayType.ElementType; - } - - if (expressionType is NubFixedArrayType fixedArrayType) - { - return fixedArrayType.ElementType; - } - - ReportError($"Cannot access index of non-array type {expressionType}", arrayIndexAccess.Array); - return null; - } - - private static NubType CheckArrayInitializer(ArrayInitializerNode arrayInitializer) - { - var capacityType = CheckExpression(arrayInitializer.Capacity, NubPrimitiveType.U64); - if (capacityType is { IsInteger: false }) - { - ReportError("Array capacity type must be an integer", arrayInitializer.Capacity); - } - - return new NubArrayType(arrayInitializer.ElementType); - } - - private static NubType? CheckIdentifier(IdentifierNode identifier) - { - var definition = _definitionTable.LookupFunc(identifier.Namespace.Or(_compilationUnit.Namespace), identifier.Name); - if (definition.HasValue) - { - return new NubFuncType(definition.Value.ReturnType, definition.Value.Parameters.Select(p => p.Type).ToList()); - } - - if (!identifier.Namespace.HasValue) - { - return _variables[identifier.Name]; - } - - ReportError($"Identifier '{identifier}' not found", identifier); - return null; - } - - private static NubType? CheckAddressOf(AddressOfNode addressOf) - { - var exprType = CheckExpression(addressOf.Expression); - if (exprType == null) return null; - - return new NubPointerType(exprType); - } - - private static NubType? CheckBinaryExpression(BinaryExpressionNode binaryExpr) - { - var leftType = CheckExpression(binaryExpr.Left); - var rightType = CheckExpression(binaryExpr.Right); - - if (leftType == null || rightType == null) return null; - - if (!leftType.Equals(rightType)) - { - ReportError($"Left '{leftType}' and right '{rightType}' side of the binary expression must be the same type", binaryExpr); - return null; - } - - switch (binaryExpr.Operator) - { - case BinaryExpressionOperator.Equal: - case BinaryExpressionOperator.NotEqual: - return NubPrimitiveType.Bool; - case BinaryExpressionOperator.GreaterThan: - case BinaryExpressionOperator.GreaterThanOrEqual: - case BinaryExpressionOperator.LessThan: - case BinaryExpressionOperator.LessThanOrEqual: - if (!IsNumeric(leftType)) - { - ReportError($"Comparison operators require numeric operands, got '{leftType}' and '{rightType}'", binaryExpr); - return null; - } - - return NubPrimitiveType.Bool; - case BinaryExpressionOperator.Plus: - case BinaryExpressionOperator.Minus: - case BinaryExpressionOperator.Multiply: - case BinaryExpressionOperator.Divide: - if (!IsNumeric(leftType)) - { - ReportError($"Arithmetic operators require numeric operands, got '{leftType}' and '{rightType}'", binaryExpr); - return null; - } - - return leftType; - default: - ReportError($"Unsupported binary operator: {binaryExpr.Operator}", binaryExpr); - return null; - } - } - - private static NubType? CheckStructInitializer(StructInitializerNode structInit) - { - var initialized = new HashSet(); - - 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; - } - - foreach (var initializer in structInit.Initializers) - { - var definitionField = definition.Fields.FirstOrDefault(f => f.Name == initializer.Key); - if (definitionField == null) - { - ReportError($"Field '{initializer.Key}' does not exist in struct '{structInit.StructType.Name}'", initializer.Value); - continue; - } - - 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); - } - - initialized.Add(initializer.Key); - } - - foreach (var field in definition.Fields.Where(f => f.Value.HasValue)) - { - initialized.Add(field.Name); - } - - foreach (var field in definition.Fields) - { - if (!initialized.Contains(field.Name)) - { - ReportError($"Struct field '{field.Name}' is not initialized on type '{structInit.StructType.Name}'", structInit); - } - } - - return structInit.StructType; - } - - private static NubType? CheckUnaryExpression(UnaryExpressionNode unaryExpression) - { - var operandType = CheckExpression(unaryExpression.Operand); - if (operandType == null) return null; - - switch (unaryExpression.Operator) - { - case UnaryExpressionOperator.Negate: - { - if (operandType.Equals(NubPrimitiveType.I8) || - operandType.Equals(NubPrimitiveType.I16) || - operandType.Equals(NubPrimitiveType.I32) || - operandType.Equals(NubPrimitiveType.I64) || - operandType.Equals(NubPrimitiveType.F32) || - operandType.Equals(NubPrimitiveType.F64)) - { - return operandType; - } - - ReportError($"Cannot negate non-numeric type {operandType}", unaryExpression.Operand); - return null; - } - case UnaryExpressionOperator.Invert: - { - if (!operandType.Equals(NubPrimitiveType.Bool)) - { - ReportError($"Cannot invert non-boolean type {operandType}", unaryExpression.Operand); - return null; - } - - return operandType; - } - default: - { - ReportError($"Unsupported unary operator: {unaryExpression.Operator}", unaryExpression); - return null; - } - } - } - - private static NubType? CheckMemberAccess(MemberAccessNode memberAccess) - { - var expressionType = CheckExpression(memberAccess.Expression); - if (expressionType == null) return null; - - switch (expressionType) - { - case NubArrayType: - { - if (memberAccess.Member == "count") - { - return NubPrimitiveType.I64; - } - - break; - } - case NubStructType structType: - { - 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; - } - - var field = definition.Fields.FirstOrDefault(f => f.Name == memberAccess.Member); - if (field == null) - { - ReportError($"Field '{memberAccess.Member}' does not exist in struct '{structType.Name}'", memberAccess); - return null; - } - - return field.Type; - } - } - - ReportError($"Cannot access member '{memberAccess.Member}' on type '{expressionType}'", memberAccess); - return null; - } - - private static void ReportError(string message, Node node) - { - var diagnostic = Diagnostic.Error(message).At(node).Build(); - _diagnostics.Add(diagnostic); - } - - private static void ReportWarning(string message, Node node) - { - var diagnostic = Diagnostic.Warning(message).At(node).Build(); - _diagnostics.Add(diagnostic); - } - - private static bool IsNumeric(NubType type) - { - if (type is not NubPrimitiveType primitiveType) - { - return false; - } - - switch (primitiveType.Kind) - { - case PrimitiveTypeKind.I8: - case PrimitiveTypeKind.I16: - case PrimitiveTypeKind.I32: - case PrimitiveTypeKind.I64: - case PrimitiveTypeKind.U8: - case PrimitiveTypeKind.U16: - case PrimitiveTypeKind.U32: - case PrimitiveTypeKind.U64: - case PrimitiveTypeKind.F32: - case PrimitiveTypeKind.F64: - return true; - default: - return false; - } - } -} \ No newline at end of file +// using System.Diagnostics; +// using Syntax.Diagnostics; +// using Syntax.Parsing; +// using Syntax.Parsing.Node; +// using Syntax.Tokenization; +// +// namespace Syntax.Typing; +// +// public static class TypeChecker +// { +// private static SyntaxTree _syntaxTree = null!; +// private static DefinitionTable _definitionTable = null!; +// +// private static Dictionary _variables = new(); +// private static List _diagnostics = []; +// private static NubType? _currentFunctionReturnType; +// private static Queue _anonymousFunctions = []; +// +// public static void Check(SyntaxTree syntaxTree, DefinitionTable definitionTable, out IEnumerable diagnostics) +// { +// _syntaxTree = syntaxTree; +// _definitionTable = definitionTable; +// +// _variables = new Dictionary(); +// _diagnostics = []; +// _currentFunctionReturnType = null; +// _anonymousFunctions = []; +// +// foreach (var structDef in syntaxTree.Definitions.OfType()) +// { +// CheckStructDef(structDef); +// } +// +// foreach (var funcDef in syntaxTree.Definitions.OfType()) +// { +// CheckFuncDef(funcDef.Parameters, funcDef.Body, funcDef.ReturnType); +// } +// +// while (_anonymousFunctions.TryDequeue(out var func)) +// { +// CheckFuncDef(func.Parameters, func.Body, func.ReturnType); +// +// } +// +// diagnostics = _diagnostics; +// } +// +// private static void CheckStructDef(StructDefinitionNode structDef) +// { +// var fields = new Dictionary(); +// foreach (var field in structDef.Fields) +// { +// if (fields.ContainsKey(field.Name)) +// { +// ReportError($"Duplicate field '{field.Name}' in struct '{structDef.Name}'", structDef); +// continue; +// } +// +// if (field.Value.HasValue) +// { +// 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); +// } +// } +// +// fields[field.Name] = field.Type; +// } +// } +// +// private static void CheckFuncDef(List parameters, BlockNode body, NubType returnType) +// { +// _variables.Clear(); +// _currentFunctionReturnType = returnType; +// +// foreach (var param in parameters) +// { +// _variables[param.Name] = param.Type; +// } +// +// CheckBlock(body); +// } +// +// private static void CheckBlock(BlockNode block) +// { +// foreach (var statement in block.Statements) +// { +// CheckStatement(statement); +// } +// } +// +// private static void CheckStatement(StatementNode statement) +// { +// switch (statement) +// { +// case ArrayIndexAssignmentNode arrayIndexAssignment: +// CheckArrayIndexAssignment(arrayIndexAssignment); +// break; +// case VariableAssignmentNode variableAssignment: +// CheckVariableAssignment(variableAssignment); +// break; +// case VariableDeclarationNode variableDeclaration: +// CheckVariableVariableDeclaration(variableDeclaration); +// break; +// case IfNode ifNode: +// CheckIf(ifNode); +// break; +// case MemberAssignmentNode memberAssignment: +// CheckMemberAssignment(memberAssignment); +// break; +// case WhileNode whileNode: +// CheckWhile(whileNode); +// break; +// case ReturnNode returnNode: +// CheckReturn(returnNode); +// break; +// case StatementExpressionNode statementExpression: +// CheckExpression(statementExpression.Expression); +// break; +// case BreakNode: +// case ContinueNode: +// break; +// case DereferenceAssignmentNode dereferenceAssignment: +// CheckDereferenceAssignment(dereferenceAssignment); +// break; +// default: +// ReportError($"Unsupported statement type: {statement.GetType().Name}", statement); +// break; +// } +// } +// +// private static void CheckMemberAssignment(MemberAssignmentNode memberAssignment) +// { +// var memberType = CheckExpression(memberAssignment.MemberAccess); +// if (memberType == null) return; +// var valueType = CheckExpression(memberAssignment.Value, memberType); +// if (valueType == null) return; +// +// if (!NubType.IsCompatibleWith(memberType, valueType)) +// { +// ReportError($"'{valueType}' is not assignable to member of type '{memberType}'", memberAssignment); +// } +// } +// +// private static void CheckArrayIndexAssignment(ArrayIndexAssignmentNode arrayIndexAssignment) +// { +// var itemType = CheckExpression(arrayIndexAssignment.ArrayIndexAccess); +// if (itemType == null) return; +// var valueType = CheckExpression(arrayIndexAssignment.Value, itemType); +// if (valueType == null) return; +// +// if (!NubType.IsCompatibleWith(itemType, valueType)) +// { +// ReportError($"'{valueType}' is not assignable to array of type '{itemType}'", arrayIndexAssignment); +// } +// } +// +// private static void CheckVariableAssignment(VariableAssignmentNode variableAssignment) +// { +// if (!_variables.TryGetValue(variableAssignment.Identifier.Name, out var variable)) +// { +// ReportError($"Variable '{variableAssignment.Identifier}' is not declared", variableAssignment); +// return; +// } +// +// var valueType = CheckExpression(variableAssignment.Value, variable); +// if (valueType == null) return; +// +// if (!NubType.IsCompatibleWith(variableAssignment.Value.Type, variable)) +// { +// ReportError($"Cannot assign expression of type '{variableAssignment.Value.Type}' to variable '{variableAssignment.Identifier}' with type '{variable}'", variableAssignment); +// } +// } +// +// private static void CheckVariableVariableDeclaration(VariableDeclarationNode variableDeclaration) +// { +// if (_variables.TryGetValue(variableDeclaration.Name, out var variable)) +// { +// ReportError($"Cannot redeclare variable '{variable}'", variableDeclaration); +// } +// +// _variables[variableDeclaration.Name] = variableDeclaration.Type; +// } +// +// private static NubType? CheckDereference(DereferenceNode dereference) +// { +// var exprType = CheckExpression(dereference.Expression); +// if (exprType == null) return null; +// +// if (exprType is not NubPointerType nubPointerType) +// { +// ReportError($"Cannot dereference a non-pointer type {exprType}", dereference); +// return null; +// } +// +// return nubPointerType.BaseType; +// } +// +// private static NubType CheckFixedInitializerArray(FixedArrayInitializerNode fixedArrayInitializer) +// { +// return new NubFixedArrayType(fixedArrayInitializer.ElementType, fixedArrayInitializer.Capacity); +// } +// +// private static NubType? CheckFuncCall(FuncCallNode funcCall) +// { +// var identType = CheckExpression(funcCall.Expression); +// if (identType is not NubFuncType funcType) +// { +// ReportError("Cannot call function on non-function type", funcCall); +// return null; +// } +// +// if (funcCall.Parameters.Count != funcType.Parameters.Count) +// { +// ReportError($"{funcType} expects {funcType.Parameters.Count} arguments, but was called with {funcType.Parameters.Count} arguments", funcCall); +// return null; +// } +// +// for (var i = 0; i < funcCall.Parameters.Count; i++) +// { +// var parameter = funcCall.Parameters[i]; +// var parameterType = CheckExpression(parameter); +// if (parameterType == null) return null; +// +// if (!NubType.IsCompatibleWith(parameterType, funcType.Parameters[i])) +// { +// ReportError($"'{parameterType}' does not match expected type {funcType.Parameters[i]}", funcCall); +// return null; +// } +// } +// +// return funcType.ReturnType; +// } +// +// private static void CheckIf(IfNode ifNode) +// { +// 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); +// } +// +// CheckBlock(ifNode.Body); +// +// if (ifNode.Else.HasValue) +// { +// var elseValue = ifNode.Else.Value; +// elseValue.Match(CheckIf, CheckBlock); +// } +// } +// +// private static void CheckWhile(WhileNode whileNode) +// { +// 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); +// } +// +// CheckBlock(whileNode.Body); +// } +// +// private static void CheckReturn(ReturnNode returnNode) +// { +// if (returnNode.Value.HasValue) +// { +// var returnType = CheckExpression(returnNode.Value.Value, _currentFunctionReturnType); +// if (returnType == null) return; +// +// if (_currentFunctionReturnType == null) +// { +// ReportError("Cannot return a value from a function with no return type", returnNode.Value.Value); +// return; +// } +// +// if (!NubType.IsCompatibleWith(returnType, _currentFunctionReturnType)) +// { +// ReportError($"Return value of type '{returnType}' is not compatible with function return type '{_currentFunctionReturnType}'", returnNode.Value.Value); +// } +// } +// else if (_currentFunctionReturnType != null) +// { +// ReportError($"Function must return a value of type '{_currentFunctionReturnType}'", returnNode); +// } +// } +// +// private static void CheckDereferenceAssignment(DereferenceAssignmentNode dereferenceAssignment) +// { +// var dereferenceType = CheckExpression(dereferenceAssignment.Dereference); +// if (dereferenceType == null) return; +// var valueType = CheckExpression(dereferenceAssignment.Value, dereferenceType); +// if (valueType == null) return; +// +// if (!NubType.IsCompatibleWith(dereferenceType, valueType)) +// { +// ReportError($"'{valueType}' is not assignable to type '{dereferenceType}'", dereferenceAssignment); +// } +// } +// +// private static NubType? CheckExpression(ExpressionNode expression, NubType? expectedType = null) +// { +// var resultType = expression switch +// { +// 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() +// }; +// +// if (resultType != null) +// { +// expression.Type = resultType; +// } +// +// return resultType; +// } +// +// private static NubType CheckAnonymousFunc(AnonymousFuncNode anonymousFunc) +// { +// _anonymousFunctions.Enqueue(anonymousFunc); +// return new NubFuncType(anonymousFunc.ReturnType, anonymousFunc.Parameters.Select(p => p.Type).ToList()); +// } +// +// private static NubType? CheckLiteral(LiteralNode literal, NubType? expectedType = null) +// { +// if (expectedType != null) +// { +// if (expectedType.IsNumber && literal.Kind is not LiteralKind.Integer and not LiteralKind.Float) +// { +// ReportError("Expression expects a numeric literal", literal); +// return null; +// } +// +// if (expectedType.IsInteger && literal.Kind == LiteralKind.Float) +// { +// if (literal.Kind == LiteralKind.Float) +// { +// ReportWarning("Float used in integer context. Everything after the '.' will be ignored", literal); +// } +// } +// +// return expectedType; +// } +// +// return literal.Kind switch +// { +// LiteralKind.Integer => NubPrimitiveType.I64, +// LiteralKind.Float => NubPrimitiveType.F64, +// LiteralKind.String => new NubCStringType(), +// LiteralKind.Bool => NubPrimitiveType.Bool, +// _ => throw new ArgumentOutOfRangeException() +// }; +// } +// +// private static NubType? CheckArrayIndex(ArrayIndexAccessNode arrayIndexAccess) +// { +// var expressionType = CheckExpression(arrayIndexAccess.Array); +// if (expressionType == null) return null; +// var indexType = CheckExpression(arrayIndexAccess.Index, NubPrimitiveType.U64); +// if (indexType is { IsInteger: false }) +// { +// ReportError("Array index type must be a number", arrayIndexAccess.Index); +// } +// +// if (expressionType is NubArrayType arrayType) +// { +// return arrayType.ElementType; +// } +// +// if (expressionType is NubFixedArrayType fixedArrayType) +// { +// return fixedArrayType.ElementType; +// } +// +// ReportError($"Cannot access index of non-array type {expressionType}", arrayIndexAccess.Array); +// return null; +// } +// +// private static NubType CheckArrayInitializer(ArrayInitializerNode arrayInitializer) +// { +// var capacityType = CheckExpression(arrayInitializer.Capacity, NubPrimitiveType.U64); +// if (capacityType is { IsInteger: false }) +// { +// ReportError("Array capacity type must be an integer", arrayInitializer.Capacity); +// } +// +// return new NubArrayType(arrayInitializer.ElementType); +// } +// +// private static NubType? CheckIdentifier(IdentifierNode identifier) +// { +// var definition = _definitionTable.LookupFunc(identifier.Namespace.Or(_syntaxTree.Namespace), identifier.Name); +// if (definition.HasValue) +// { +// return new NubFuncType(definition.Value.ReturnType, definition.Value.Parameters.Select(p => p.Type).ToList()); +// } +// +// if (!identifier.Namespace.HasValue) +// { +// return _variables[identifier.Name]; +// } +// +// ReportError($"Identifier '{identifier}' not found", identifier); +// return null; +// } +// +// private static NubType? CheckAddressOf(AddressOfNode addressOf) +// { +// var exprType = CheckExpression(addressOf.Expression); +// if (exprType == null) return null; +// +// return new NubPointerType(exprType); +// } +// +// private static NubType? CheckBinaryExpression(BinaryExpressionNode binaryExpr) +// { +// var leftType = CheckExpression(binaryExpr.Left); +// var rightType = CheckExpression(binaryExpr.Right); +// +// if (leftType == null || rightType == null) return null; +// +// if (!leftType.Equals(rightType)) +// { +// ReportError($"Left '{leftType}' and right '{rightType}' side of the binary expression must be the same type", binaryExpr); +// return null; +// } +// +// switch (binaryExpr.Operator) +// { +// case BinaryExpressionOperator.Equal: +// case BinaryExpressionOperator.NotEqual: +// return NubPrimitiveType.Bool; +// case BinaryExpressionOperator.GreaterThan: +// case BinaryExpressionOperator.GreaterThanOrEqual: +// case BinaryExpressionOperator.LessThan: +// case BinaryExpressionOperator.LessThanOrEqual: +// if (!IsNumeric(leftType)) +// { +// ReportError($"Comparison operators require numeric operands, got '{leftType}' and '{rightType}'", binaryExpr); +// return null; +// } +// +// return NubPrimitiveType.Bool; +// case BinaryExpressionOperator.Plus: +// case BinaryExpressionOperator.Minus: +// case BinaryExpressionOperator.Multiply: +// case BinaryExpressionOperator.Divide: +// if (!IsNumeric(leftType)) +// { +// ReportError($"Arithmetic operators require numeric operands, got '{leftType}' and '{rightType}'", binaryExpr); +// return null; +// } +// +// return leftType; +// default: +// ReportError($"Unsupported binary operator: {binaryExpr.Operator}", binaryExpr); +// return null; +// } +// } +// +// private static NubType? CheckStructInitializer(StructInitializerNode structInit) +// { +// var initialized = new HashSet(); +// +// 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; +// } +// +// foreach (var initializer in structInit.Initializers) +// { +// var definitionField = definition.Fields.FirstOrDefault(f => f.Name == initializer.Key); +// if (definitionField == null) +// { +// ReportError($"Field '{initializer.Key}' does not exist in struct '{structInit.StructType.Name}'", initializer.Value); +// continue; +// } +// +// 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); +// } +// +// initialized.Add(initializer.Key); +// } +// +// foreach (var field in definition.Fields.Where(f => f.Value.HasValue)) +// { +// initialized.Add(field.Name); +// } +// +// foreach (var field in definition.Fields) +// { +// if (!initialized.Contains(field.Name)) +// { +// ReportError($"Struct field '{field.Name}' is not initialized on type '{structInit.StructType.Name}'", structInit); +// } +// } +// +// return structInit.StructType; +// } +// +// private static NubType? CheckUnaryExpression(UnaryExpressionNode unaryExpression) +// { +// var operandType = CheckExpression(unaryExpression.Operand); +// if (operandType == null) return null; +// +// switch (unaryExpression.Operator) +// { +// case UnaryExpressionOperator.Negate: +// { +// if (operandType.Equals(NubPrimitiveType.I8) || +// operandType.Equals(NubPrimitiveType.I16) || +// operandType.Equals(NubPrimitiveType.I32) || +// operandType.Equals(NubPrimitiveType.I64) || +// operandType.Equals(NubPrimitiveType.F32) || +// operandType.Equals(NubPrimitiveType.F64)) +// { +// return operandType; +// } +// +// ReportError($"Cannot negate non-numeric type {operandType}", unaryExpression.Operand); +// return null; +// } +// case UnaryExpressionOperator.Invert: +// { +// if (!operandType.Equals(NubPrimitiveType.Bool)) +// { +// ReportError($"Cannot invert non-boolean type {operandType}", unaryExpression.Operand); +// return null; +// } +// +// return operandType; +// } +// default: +// { +// ReportError($"Unsupported unary operator: {unaryExpression.Operator}", unaryExpression); +// return null; +// } +// } +// } +// +// private static NubType? CheckMemberAccess(MemberAccessNode memberAccess) +// { +// var expressionType = CheckExpression(memberAccess.Expression); +// if (expressionType == null) return null; +// +// switch (expressionType) +// { +// case NubArrayType: +// { +// if (memberAccess.Member == "count") +// { +// return NubPrimitiveType.I64; +// } +// +// break; +// } +// case NubStructType structType: +// { +// 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; +// } +// +// var field = definition.Fields.FirstOrDefault(f => f.Name == memberAccess.Member); +// if (field == null) +// { +// ReportError($"Field '{memberAccess.Member}' does not exist in struct '{structType.Name}'", memberAccess); +// return null; +// } +// +// return field.Type; +// } +// } +// +// ReportError($"Cannot access member '{memberAccess.Member}' on type '{expressionType}'", memberAccess); +// return null; +// } +// +// private static void ReportError(string message, Node node) +// { +// var diagnostic = Diagnostic.Error(message).At(node).Build(); +// _diagnostics.Add(diagnostic); +// } +// +// private static void ReportWarning(string message, Node node) +// { +// var diagnostic = Diagnostic.Warning(message).At(node).Build(); +// _diagnostics.Add(diagnostic); +// } +// +// private static bool IsNumeric(NubType type) +// { +// if (type is not NubPrimitiveType primitiveType) +// { +// return false; +// } +// +// switch (primitiveType.Kind) +// { +// case PrimitiveTypeKind.I8: +// case PrimitiveTypeKind.I16: +// case PrimitiveTypeKind.I32: +// case PrimitiveTypeKind.I64: +// case PrimitiveTypeKind.U8: +// case PrimitiveTypeKind.U16: +// case PrimitiveTypeKind.U32: +// case PrimitiveTypeKind.U64: +// case PrimitiveTypeKind.F32: +// case PrimitiveTypeKind.F64: +// return true; +// default: +// return false; +// } +// } +// } \ No newline at end of file