From 7a1ff15a5862adb3bb89a3150e6376b8db512fea Mon Sep 17 00:00:00 2001 From: nub31 Date: Fri, 4 Jul 2025 17:48:56 +0200 Subject: [PATCH] ... --- example/src/main.nub | 22 ++- src/compiler/CLI/Program.cs | 3 +- src/compiler/Generation/QBE/QBEGenerator.cs | 171 +++++++++++------- src/compiler/Generation/QBE/QBEWriter.cs | 3 +- src/compiler/Syntax/DefinitionTable.cs | 56 ++++++ .../Syntax/Parsing/Node/Definition.cs | 6 + src/compiler/Syntax/Parsing/Parser.cs | 104 ++++++++++- .../Syntax/Tokenization/SymbolToken.cs | 4 +- src/compiler/Syntax/Tokenization/Tokenizer.cs | 2 + src/compiler/Syntax/Typing/Binder.cs | 47 +++++ .../Syntax/Typing/BoundNode/Definition.cs | 6 + 11 files changed, 346 insertions(+), 78 deletions(-) diff --git a/example/src/main.nub b/example/src/main.nub index b6e7b71..ce803d7 100644 --- a/example/src/main.nub +++ b/example/src/main.nub @@ -1,6 +1,7 @@ namespace main -struct Name { +struct Name +{ first: cstring last: cstring } @@ -11,6 +12,18 @@ struct Human age: i64 } +interface Printable +{ + func print() +} + +impl Human : Printable +{ + func print() { + c::puts(this.name) + } +} + export func main(args: []cstring): i64 { let x = alloc Human @@ -22,10 +35,7 @@ export func main(args: []cstring): i64 age = 23 } - print_name(x&) + x.print() + return 0 } - -func print_name(human: ^Human) { - c::puts(human^.name.last) -} diff --git a/src/compiler/CLI/Program.cs b/src/compiler/CLI/Program.cs index 456a89f..219aaf8 100644 --- a/src/compiler/CLI/Program.cs +++ b/src/compiler/CLI/Program.cs @@ -121,7 +121,8 @@ foreach (var file in options.Files) var outFileName = $"{HexString.CreateUnique(8)}_{Path.GetFileNameWithoutExtension(file)}"; var ssa = QBEGenerator.Emit(boundSyntaxTrees[file], boundDefinitionTable, file); - File.WriteAllText(Path.Join(INT_DEBUG_DIR, $"{outFileName}.ssa"), ssa); + // File.WriteAllText(Path.Join(INT_DEBUG_DIR, $"{outFileName}.ssa"), ssa); + File.WriteAllText(Path.Join(INT_DEBUG_DIR, $"{Path.GetFileNameWithoutExtension(file)}.ssa"), ssa); var asm = await QBE.Invoke(ssa); if (asm == null) diff --git a/src/compiler/Generation/QBE/QBEGenerator.cs b/src/compiler/Generation/QBE/QBEGenerator.cs index a2ec69f..bb41a19 100644 --- a/src/compiler/Generation/QBE/QBEGenerator.cs +++ b/src/compiler/Generation/QBE/QBEGenerator.cs @@ -54,16 +54,25 @@ public static class QBEGenerator foreach (var structDef in _definitionTable.GetStructs()) { EmitStructDefinition(structDef); + _writer.NewLine(); + } + + foreach (var @interface in _definitionTable.GetInterfaces()) + { + EmitInterfaceVTableType(@interface); + _writer.NewLine(); } foreach (var funcDef in _syntaxTree.Definitions.OfType()) { EmitFuncDefinition(funcDef, FuncName(funcDef), funcDef.Parameters, funcDef.ReturnType, funcDef.Body, funcDef.Exported); + _writer.NewLine(); } while (_anonymousFunctions.TryDequeue(out var anon)) { EmitFuncDefinition(anon.Func, anon.Name, anon.Func.Parameters, anon.Func.ReturnType, anon.Func.Body, false); + _writer.NewLine(); } foreach (var cStringLiteral in _cStringLiterals) @@ -117,6 +126,11 @@ public static class QBEGenerator return $":{structDef.Namespace}_{structDef.Name}"; } + private static string InterfaceVTableTypeName(BoundInterfaceDefinitionNode interfaceDef) + { + return $":{interfaceDef.Namespace}_{interfaceDef.Name}"; + } + private static void EmitStore(NubType type, string value, string destination) { var store = type switch @@ -135,7 +149,7 @@ public static class QBEGenerator _ => throw new NotSupportedException($"'{type}' type cannot be used in store instructions") }; - _writer.Code($"{store} {value}, {destination}"); + _writer.Indented($"{store} {value}, {destination}"); } private static Val EmitLoad(NubType type, string from) @@ -159,38 +173,38 @@ public static class QBEGenerator _ => throw new NotSupportedException($"'{type}' type cannot be used in load instructions") }; - _writer.Code($"{into} {QBEAssign(type)} {load} {from}"); + _writer.Indented($"{into} {QBEAssign(type)} {load} {from}"); return new Val(into, type, ValKind.Direct); } private static void EmitMemcpy(string source, string destination, string length) { - _writer.Code($"call $nub_memcpy(l {source}, l {destination}, l {length})"); + _writer.Indented($"call $nub_memcpy(l {source}, l {destination}, l {length})"); } private static string EmitArraySizeInBytes(NubArrayType type, string array) { var size = VarName(); - _writer.Code($"{size} =l loadl {array}"); - _writer.Code($"{size} =l mul {size}, {SizeOf(type.ElementType)}"); - _writer.Code($"{size} =l add {size}, 8"); + _writer.Indented($"{size} =l loadl {array}"); + _writer.Indented($"{size} =l mul {size}, {SizeOf(type.ElementType)}"); + _writer.Indented($"{size} =l add {size}, 8"); return size; } private static string EmitCStringSizeInBytes(string cstring) { var size = VarName(); - _writer.Code($"{size} =l call $nub_cstring_length(l {cstring})"); - _writer.Code($"{size} =l add {size}, 1"); + _writer.Indented($"{size} =l call $nub_cstring_length(l {cstring})"); + _writer.Indented($"{size} =l add {size}, 1"); return size; } private static string EmitStringSizeInBytes(string nubstring) { var size = VarName(); - _writer.Code($"{size} =l loadl {nubstring}"); - _writer.Code($"{size} =l add {size}, 8"); + _writer.Indented($"{size} =l loadl {nubstring}"); + _writer.Indented($"{size} =l add {size}, 8"); return size; } @@ -251,7 +265,7 @@ public static class QBEGenerator case NubStringType: { var buffer = VarName(); - _writer.Code($"{buffer} =l alloc8 {size}"); + _writer.Indented($"{buffer} =l alloc8 {size}"); EmitMemcpy(value, buffer, size); EmitStore(source.Type, buffer, destinationPointer); return; @@ -321,7 +335,7 @@ public static class QBEGenerator _ => throw new ArgumentOutOfRangeException(nameof(complexType)) }; - _writer.Code($"{destination} =l alloc8 {size}"); + _writer.Indented($"{destination} =l alloc8 {size}"); EmitMemcpy(value, destination, size); return destination; } @@ -534,19 +548,22 @@ public static class QBEGenerator { if (returnType is NubVoidType) { - _writer.Code("ret"); + _writer.Indented("ret"); } } _writer.EndFunction(); } - private static void EmitStructDefinition(BoundStructDefinitionNode structDefinition) + private static void EmitStructDefinition(BoundStructDefinitionNode structDef) { - _writer.Write($"type {StructName(structDefinition)} = {{ "); - foreach (var structDefinitionField in structDefinition.Fields) + _writer.WriteLine($"type {StructName(structDef)} = {{ "); + + var types = new Dictionary(); + + foreach (var field in structDef.Fields) { - var qbeType = structDefinitionField.Type switch + var qbeType = field.Type switch { NubPrimitiveType primitiveType => primitiveType.Kind switch { @@ -560,9 +577,29 @@ public static class QBEGenerator }, NubStructType structType => StructName(_definitionTable.LookupStruct(structType.Namespace, structType.Name).GetValue()), NubArrayType or NubPointerType or NubFuncType or NubCStringType or NubStringType => "l", - _ => throw new NotSupportedException($"'{structDefinitionField.Type}' type cannot be used in structs") + _ => throw new NotSupportedException($"'{field.Type}' type cannot be used in structs") }; - _writer.Write(qbeType + ", "); + + types.Add(field.Name, qbeType); + } + + var longest = types.Values.Max(x => x.Length); + foreach (var (name, type) in types) + { + var padding = longest - type.Length; + _writer.Indented($"{type},{new string(' ', padding)} # {name}"); + } + + _writer.WriteLine("}"); + } + + private static void EmitInterfaceVTableType(BoundInterfaceDefinitionNode interfaceDef) + { + _writer.WriteLine($"type {InterfaceVTableTypeName(interfaceDef)} = {{"); + + foreach (var func in interfaceDef.Functions) + { + _writer.Indented($"l, # func {func.Name}({string.Join(", ", func.Parameters.Select(x => $"{x.Name}: {x.Type}"))}): {func.ReturnType}"); } _writer.WriteLine("}"); @@ -637,13 +674,13 @@ public static class QBEGenerator private static void EmitBreak(BoundBreakNode @break) { - _writer.Code($"jmp {_breakLabels.Peek()}"); + _writer.Indented($"jmp {_breakLabels.Peek()}"); _codeIsReachable = false; } private static void EmitContinue(BoundContinueNode @continue) { - _writer.Code($"jmp {_continueLabels.Peek()}"); + _writer.Indented($"jmp {_continueLabels.Peek()}"); _codeIsReachable = false; } @@ -654,10 +691,10 @@ public static class QBEGenerator var endLabel = LabelName(); var result = EmitUnwrap(EmitExpression(ifStatement.Condition)); - _writer.Code($"jnz {result}, {trueLabel}, {falseLabel}"); + _writer.Indented($"jnz {result}, {trueLabel}, {falseLabel}"); _writer.WriteLine(trueLabel); EmitBlock(ifStatement.Body); - _writer.Code($"jmp {endLabel}"); + _writer.Indented($"jmp {endLabel}"); _writer.WriteLine(falseLabel); if (ifStatement.Else.HasValue) { @@ -676,18 +713,18 @@ public static class QBEGenerator if (@return.Value.HasValue) { var result = EmitUnwrap(EmitExpression(@return.Value.Value)); - _writer.Code($"ret {result}"); + _writer.Indented($"ret {result}"); } else { - _writer.Code("ret"); + _writer.Indented("ret"); } } private static void EmitVariableDeclaration(BoundVariableDeclarationNode variableDeclaration) { var variable = VarName(); - _writer.Code($"{variable} =l alloc8 {SizeOf(variableDeclaration.Type)}"); + _writer.Indented($"{variable} =l alloc8 {SizeOf(variableDeclaration.Type)}"); if (variableDeclaration.Assignment.HasValue) { @@ -706,12 +743,12 @@ public static class QBEGenerator _breakLabels.Push(endLabel); _continueLabels.Push(conditionLabel); - _writer.Code($"jmp {conditionLabel}"); + _writer.Indented($"jmp {conditionLabel}"); _writer.WriteLine(iterationLabel); EmitBlock(whileStatement.Body); _writer.WriteLine(conditionLabel); var result = EmitUnwrap(EmitExpression(whileStatement.Condition)); - _writer.Code($"jnz {result}, {iterationLabel}, {endLabel}"); + _writer.Indented($"jnz {result}, {iterationLabel}, {endLabel}"); _writer.WriteLine(endLabel); _continueLabels.Pop(); @@ -754,34 +791,34 @@ public static class QBEGenerator var elementType = ((NubArrayType)arrayIndexAccess.Array.Type).ElementType; var pointer = VarName(); - _writer.Code($"{pointer} =l mul {index}, {SizeOf(elementType)}"); - _writer.Code($"{pointer} =l add {pointer}, 8"); - _writer.Code($"{pointer} =l add {array}, {pointer}"); + _writer.Indented($"{pointer} =l mul {index}, {SizeOf(elementType)}"); + _writer.Indented($"{pointer} =l add {pointer}, 8"); + _writer.Indented($"{pointer} =l add {array}, {pointer}"); return new Val(pointer, arrayIndexAccess.Type, ValKind.Pointer); } private static void EmitArrayBoundsCheck(string array, string index) { var count = VarName(); - _writer.Code($"{count} =l loadl {array}"); + _writer.Indented($"{count} =l loadl {array}"); var isNegative = VarName(); - _writer.Code($"{isNegative} =w csltl {index}, 0"); + _writer.Indented($"{isNegative} =w csltl {index}, 0"); var isOob = VarName(); - _writer.Code($"{isOob} =w csgel {index}, {count}"); + _writer.Indented($"{isOob} =w csgel {index}, {count}"); var anyOob = VarName(); - _writer.Code($"{anyOob} =w or {isNegative}, {isOob}"); + _writer.Indented($"{anyOob} =w or {isNegative}, {isOob}"); var oobLabel = LabelName(); var notOobLabel = LabelName(); - _writer.Code($"jnz {anyOob}, {oobLabel}, {notOobLabel}"); + _writer.Indented($"jnz {anyOob}, {oobLabel}, {notOobLabel}"); - _writer.Code(oobLabel); - _writer.Code($"call $nub_panic_array_oob()"); + _writer.Indented(oobLabel); + _writer.Indented($"call $nub_panic_array_oob()"); - _writer.Code(notOobLabel); + _writer.Indented(notOobLabel); } private static Val EmitArrayInitializer(BoundArrayInitializerNode arrayInitializer) @@ -790,17 +827,17 @@ public static class QBEGenerator var elementSize = SizeOf(arrayInitializer.ElementType); var capacityInBytes = VarName(); - _writer.Code($"{capacityInBytes} =l mul {capacity}, {elementSize}"); + _writer.Indented($"{capacityInBytes} =l mul {capacity}, {elementSize}"); var totalSize = VarName(); - _writer.Code($"{totalSize} =l add {capacityInBytes}, 8"); + _writer.Indented($"{totalSize} =l add {capacityInBytes}, 8"); var arrayPointer = VarName(); - _writer.Code($"{arrayPointer} =l alloc8 {totalSize}"); - _writer.Code($"storel {capacity}, {arrayPointer}"); + _writer.Indented($"{arrayPointer} =l alloc8 {totalSize}"); + _writer.Indented($"storel {capacity}, {arrayPointer}"); var dataPointer = VarName(); - _writer.Code($"{dataPointer} =l add {arrayPointer}, 8"); - _writer.Code($"call $nub_memset(l {dataPointer}, w 0, l {capacityInBytes})"); + _writer.Indented($"{dataPointer} =l add {arrayPointer}, 8"); + _writer.Indented($"call $nub_memset(l {dataPointer}, w 0, l {capacityInBytes})"); return new Val(arrayPointer, arrayInitializer.Type, ValKind.Direct); } @@ -836,7 +873,7 @@ public static class QBEGenerator var instruction = EmitBinaryInstructionFor(binaryExpression.Operator, binaryExpression.Left.Type, left, right); - _writer.Code($"{outputName} {QBEAssign(binaryExpression.Left.Type)} {instruction} {left}, {right}"); + _writer.Indented($"{outputName} {QBEAssign(binaryExpression.Left.Type)} {instruction} {left}, {right}"); return new Val(outputName, binaryExpression.Type, ValKind.Direct); } @@ -856,13 +893,13 @@ public static class QBEGenerator { if (type.IsSignedInteger) { - _writer.Code($"{left} =w extsb {left}"); - _writer.Code($"{right} =w extsb {right}"); + _writer.Indented($"{left} =w extsb {left}"); + _writer.Indented($"{right} =w extsb {right}"); } else { - _writer.Code($"{left} =w extub {left}"); - _writer.Code($"{right} =w extub {right}"); + _writer.Indented($"{left} =w extub {left}"); + _writer.Indented($"{right} =w extub {right}"); } suffix = 'w'; @@ -871,13 +908,13 @@ public static class QBEGenerator { if (type.IsSignedInteger) { - _writer.Code($"{left} =w extsh {left}"); - _writer.Code($"{right} =w extsh {right}"); + _writer.Indented($"{left} =w extsh {left}"); + _writer.Indented($"{right} =w extsh {right}"); } else { - _writer.Code($"{left} =w extuh {left}"); - _writer.Code($"{right} =w extuh {right}"); + _writer.Indented($"{left} =w extuh {left}"); + _writer.Indented($"{right} =w extuh {right}"); } suffix = 'w'; @@ -1049,7 +1086,7 @@ public static class QBEGenerator { destination = VarName(); var size = SizeOf(structInitializer.StructType); - _writer.Code($"{destination} =l alloc8 {size}"); + _writer.Indented($"{destination} =l alloc8 {size}"); } foreach (var field in structDefinition.Fields) @@ -1062,7 +1099,7 @@ public static class QBEGenerator Debug.Assert(valueExpression != null); var offset = VarName(); - _writer.Code($"{offset} =l add {destination}, {OffsetOf(structDefinition, field.Name)}"); + _writer.Indented($"{offset} =l add {destination}, {OffsetOf(structDefinition, field.Name)}"); EmitCopyIntoOrInitialize(valueExpression, offset); } @@ -1081,16 +1118,16 @@ public static class QBEGenerator switch (unaryExpression.Operand.Type) { case NubPrimitiveType { Kind: PrimitiveTypeKind.I64 }: - _writer.Code($"{outputName} =l neg {operand}"); + _writer.Indented($"{outputName} =l neg {operand}"); return new Val(outputName, unaryExpression.Type, ValKind.Direct); case NubPrimitiveType { Kind: PrimitiveTypeKind.I32 or PrimitiveTypeKind.I16 or PrimitiveTypeKind.I8 }: - _writer.Code($"{outputName} =w neg {operand}"); + _writer.Indented($"{outputName} =w neg {operand}"); return new Val(outputName, unaryExpression.Type, ValKind.Direct); case NubPrimitiveType { Kind: PrimitiveTypeKind.F64 }: - _writer.Code($"{outputName} =d neg {operand}"); + _writer.Indented($"{outputName} =d neg {operand}"); return new Val(outputName, unaryExpression.Type, ValKind.Direct); case NubPrimitiveType { Kind: PrimitiveTypeKind.F32 }: - _writer.Code($"{outputName} =s neg {operand}"); + _writer.Indented($"{outputName} =s neg {operand}"); return new Val(outputName, unaryExpression.Type, ValKind.Direct); } @@ -1101,7 +1138,7 @@ public static class QBEGenerator switch (unaryExpression.Operand.Type) { case NubPrimitiveType { Kind: PrimitiveTypeKind.Bool }: - _writer.Code($"{outputName} =w xor {operand}, 1"); + _writer.Indented($"{outputName} =w xor {operand}, 1"); return new Val(outputName, unaryExpression.Type, ValKind.Direct); } @@ -1127,7 +1164,7 @@ public static class QBEGenerator { if (memberAccess.Member == "count") { - _writer.Code($"{output} =l loadl {item}"); + _writer.Indented($"{output} =l loadl {item}"); return new Val(output, memberAccess.Type, ValKind.Direct); } @@ -1137,7 +1174,7 @@ public static class QBEGenerator { if (memberAccess.Member == "count") { - _writer.Code($"{output} =l call $nub_string_length(l {item})"); + _writer.Indented($"{output} =l call $nub_string_length(l {item})"); return new Val(output, memberAccess.Type, ValKind.Direct); } @@ -1147,7 +1184,7 @@ public static class QBEGenerator { if (memberAccess.Member == "count") { - _writer.Code($"{output} =l call $nub_cstring_length(l {item})"); + _writer.Indented($"{output} =l call $nub_cstring_length(l {item})"); return new Val(output, memberAccess.Type, ValKind.Direct); } @@ -1158,7 +1195,7 @@ public static class QBEGenerator var structDefinition = _definitionTable.LookupStruct(structType.Namespace, structType.Name).GetValue(); var offset = OffsetOf(structDefinition, memberAccess.Member); - _writer.Code($"{output} =l add {item}, {offset}"); + _writer.Indented($"{output} =l add {item}, {offset}"); return new Val(output, memberAccess.Type, ValKind.Pointer); } } @@ -1203,12 +1240,12 @@ public static class QBEGenerator { var outputName = VarName(); - _writer.Code($"{outputName} {QBEAssign(funcCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})"); + _writer.Indented($"{outputName} {QBEAssign(funcCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})"); return new Val(outputName, funcCall.Type, ValKind.Direct); } else { - _writer.Code($"call {funcPointer}({string.Join(", ", parameterStrings)})"); + _writer.Indented($"call {funcPointer}({string.Join(", ", parameterStrings)})"); return new Val(string.Empty, funcCall.Type, ValKind.Direct); } } diff --git a/src/compiler/Generation/QBE/QBEWriter.cs b/src/compiler/Generation/QBE/QBEWriter.cs index 226bffc..c8e1447 100644 --- a/src/compiler/Generation/QBE/QBEWriter.cs +++ b/src/compiler/Generation/QBE/QBEWriter.cs @@ -18,7 +18,6 @@ internal class QBEWriter public void StartFunction(string signature) { _currentLine = -1; - _builder.AppendLine(); _builder.Append(signature); _builder.AppendLine(" {"); _builder.AppendLine("@start"); @@ -49,7 +48,7 @@ internal class QBEWriter } } - public void Code(string value) + public void Indented(string value) { _builder.Append('\t'); _builder.AppendLine(value); diff --git a/src/compiler/Syntax/DefinitionTable.cs b/src/compiler/Syntax/DefinitionTable.cs index 23720b5..9392f91 100644 --- a/src/compiler/Syntax/DefinitionTable.cs +++ b/src/compiler/Syntax/DefinitionTable.cs @@ -50,6 +50,34 @@ public class DefinitionTable .SelectMany(c => c.Definitions) .OfType(); } + + public IEnumerable GetLocalFunctions() + { + return _syntaxTrees + .SelectMany(c => c.Definitions) + .OfType(); + } + + public IEnumerable GetExternFunctions() + { + return _syntaxTrees + .SelectMany(c => c.Definitions) + .OfType(); + } + + public IEnumerable GetInterfaces() + { + return _syntaxTrees + .SelectMany(c => c.Definitions) + .OfType(); + } + + public IEnumerable GetImplementations() + { + return _syntaxTrees + .SelectMany(c => c.Definitions) + .OfType(); + } } public class BoundDefinitionTable @@ -96,4 +124,32 @@ public class BoundDefinitionTable .SelectMany(c => c.Definitions) .OfType(); } + + public IEnumerable GetLocalFunctions() + { + return _syntaxTrees + .SelectMany(c => c.Definitions) + .OfType(); + } + + public IEnumerable GetExternFunctions() + { + return _syntaxTrees + .SelectMany(c => c.Definitions) + .OfType(); + } + + public IEnumerable GetInterfaces() + { + return _syntaxTrees + .SelectMany(c => c.Definitions) + .OfType(); + } + + public IEnumerable GetImplementations() + { + return _syntaxTrees + .SelectMany(c => c.Definitions) + .OfType(); + } } \ No newline at end of file diff --git a/src/compiler/Syntax/Parsing/Node/Definition.cs b/src/compiler/Syntax/Parsing/Node/Definition.cs index aec8f54..968a9f1 100644 --- a/src/compiler/Syntax/Parsing/Node/Definition.cs +++ b/src/compiler/Syntax/Parsing/Node/Definition.cs @@ -13,3 +13,9 @@ public record ExternFuncDefinitionNode(IEnumerable Tokens, Optional Value); public record StructDefinitionNode(IEnumerable Tokens, Optional Documentation, string Namespace, string Name, List Fields) : DefinitionNode(Tokens, Documentation, Namespace); + +public record InterfaceFunc(string Name, List Parameters, NubType ReturnType); +public record InterfaceDefinitionNode(IEnumerable Tokens, Optional Documentation, string Namespace, string Name, List Functions) : DefinitionNode(Tokens, Documentation, Namespace); + +public record ImplementationFunc(string Name, List Parameters, NubType ReturnType, BlockNode Body); +public record ImplementationDefinitionNode(IEnumerable Tokens, Optional Documentation, string Namespace, NubType Type, NubType Interface, List Functions) : DefinitionNode(Tokens, Documentation, Namespace); \ No newline at end of file diff --git a/src/compiler/Syntax/Parsing/Parser.cs b/src/compiler/Syntax/Parsing/Parser.cs index d211f72..9d60451 100644 --- a/src/compiler/Syntax/Parsing/Parser.cs +++ b/src/compiler/Syntax/Parsing/Parser.cs @@ -81,6 +81,8 @@ public static class Parser { Symbol.Func => ParseFuncDefinition(startIndex, modifiers, Optional.OfNullable(documentation)), Symbol.Struct => ParseStruct(startIndex, modifiers, Optional.OfNullable(documentation)), + Symbol.Interface => ParseInterface(startIndex, modifiers, Optional.OfNullable(documentation)), + Symbol.Impl => ParseImplementation(startIndex, modifiers, Optional.OfNullable(documentation)), _ => throw new ParseException(Diagnostic .Error($"Expected 'func' or 'struct', but found '{keyword.Symbol}'") .WithHelp("Valid definition keywords are 'func' and 'struct'") @@ -149,7 +151,7 @@ public static class Parser return new LocalFuncDefinitionNode(GetTokensForNode(startIndex), documentation, _namespace, name.Value, parameters, body, returnType, exported); } - private static StructDefinitionNode ParseStruct(int startIndex, List _, Optional documentation) + private static StructDefinitionNode ParseStruct(int startIndex, List modifiers, Optional documentation) { var name = ExpectIdentifier().Value; @@ -173,9 +175,109 @@ public static class Parser variables.Add(new StructField(variableName, variableType, variableValue)); } + if (modifiers.Count != 0) + { + throw new ParseException(Diagnostic + .Error($"Invalid modifiers for struct: {modifiers[0].Modifier}") + .WithHelp($"Structs cannot use the '{modifiers[0].Modifier}' modifier") + .At(modifiers[0]) + .Build()); + } + return new StructDefinitionNode(GetTokensForNode(startIndex), documentation, _namespace, name, variables); } + private static InterfaceDefinitionNode ParseInterface(int startIndex, List modifiers, Optional documentation) + { + var name = ExpectIdentifier().Value; + + ExpectSymbol(Symbol.OpenBrace); + + List functions = []; + + while (!TryExpectSymbol(Symbol.CloseBrace)) + { + ExpectSymbol(Symbol.Func); + + var funcName = ExpectIdentifier().Value; + var parameters = new List(); + + ExpectSymbol(Symbol.OpenParen); + while (!TryExpectSymbol(Symbol.CloseParen)) + { + var parameter = ParseFuncParameter(); + parameters.Add(parameter); + if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var nextToken) && nextToken is not SymbolToken { Symbol: Symbol.CloseParen }) + { + _diagnostics.Add(Diagnostic + .Warning("Missing comma between function arguments") + .WithHelp("Add a ',' to separate arguments") + .At(nextToken) + .Build()); + } + } + + var returnType = TryExpectSymbol(Symbol.Colon) ? ParseType() : new NubVoidType(); + + if (modifiers.Count != 0) + { + throw new ParseException(Diagnostic + .Error($"Invalid modifiers for interface: {modifiers[0].Modifier}") + .WithHelp($"Interface cannot use the '{modifiers[0].Modifier}' modifier") + .At(modifiers[0]) + .Build()); + } + + functions.Add(new InterfaceFunc(funcName, parameters, returnType)); + } + + return new InterfaceDefinitionNode(GetTokensForNode(startIndex), documentation, _namespace, name, functions); + } + + private static ImplementationDefinitionNode ParseImplementation(int startIndex, List modifiers, Optional documentation) + { + var type = ParseType(); + ExpectSymbol(Symbol.Colon); + var @interface = ParseType(); + + List functions = []; + + ExpectSymbol(Symbol.OpenBrace); + while (!TryExpectSymbol(Symbol.CloseBrace)) + { + ExpectSymbol(Symbol.Func); + var functionName = ExpectIdentifier().Value; + var parameters = new List + { + new("this", type) + }; + + ExpectSymbol(Symbol.OpenParen); + + while (!TryExpectSymbol(Symbol.CloseParen)) + { + parameters.Add(ParseFuncParameter()); + + if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var token) && token is not SymbolToken { Symbol: Symbol.CloseParen }) + { + _diagnostics.Add(Diagnostic + .Warning("Missing comma between function parameters") + .WithHelp("Add a ',' to separate parameters") + .At(token) + .Build()); + } + } + + var returnType = TryExpectSymbol(Symbol.Colon) ? ParseType() : new NubVoidType(); + + var body = ParseBlock(); + + functions.AddRange(new ImplementationFunc(functionName, parameters, returnType, body)); + } + + return new ImplementationDefinitionNode(GetTokensForNode(startIndex), documentation, _namespace, type, @interface, functions); + } + private static FuncParameter ParseFuncParameter() { var name = ExpectIdentifier(); diff --git a/src/compiler/Syntax/Tokenization/SymbolToken.cs b/src/compiler/Syntax/Tokenization/SymbolToken.cs index af31ca0..4bc01ac 100644 --- a/src/compiler/Syntax/Tokenization/SymbolToken.cs +++ b/src/compiler/Syntax/Tokenization/SymbolToken.cs @@ -43,5 +43,7 @@ public enum Symbol Namespace, Let, Alloc, - Calls + Calls, + Interface, + Impl } \ No newline at end of file diff --git a/src/compiler/Syntax/Tokenization/Tokenizer.cs b/src/compiler/Syntax/Tokenization/Tokenizer.cs index 5a3784d..c8d644d 100644 --- a/src/compiler/Syntax/Tokenization/Tokenizer.cs +++ b/src/compiler/Syntax/Tokenization/Tokenizer.cs @@ -19,6 +19,8 @@ public static class Tokenizer ["struct"] = Symbol.Struct, ["let"] = Symbol.Let, ["calls"] = Symbol.Calls, + ["interface"] = Symbol.Interface, + ["impl"] = Symbol.Impl, }; private static readonly Dictionary Modifiers = new() diff --git a/src/compiler/Syntax/Typing/Binder.cs b/src/compiler/Syntax/Typing/Binder.cs index 4adf6ce..2abfcbd 100644 --- a/src/compiler/Syntax/Typing/Binder.cs +++ b/src/compiler/Syntax/Typing/Binder.cs @@ -41,12 +41,53 @@ public static class Binder return node switch { ExternFuncDefinitionNode definition => BindExternFuncDefinition(definition), + ImplementationDefinitionNode definition => BindImplementation(definition), + InterfaceDefinitionNode definition => BindInterfaceDefinition(definition), LocalFuncDefinitionNode definition => BindLocalFuncDefinition(definition), StructDefinitionNode definition => BindStruct(definition), _ => throw new ArgumentOutOfRangeException(nameof(node)) }; } + private static BoundImplementationDefinitionNode BindImplementation(ImplementationDefinitionNode node) + { + _variables.Clear(); + var functions = new List(); + + foreach (var function in node.Functions) + { + var parameters = new List(); + foreach (var parameter in function.Parameters) + { + parameters.Add(new BoundFuncParameter(parameter.Name, parameter.Type)); + _variables[parameter.Name] = parameter.Type; + } + + functions.Add(new BoundImplementationFunc(function.Name, parameters, function.ReturnType, BindBlock(function.Body))); + } + + return new BoundImplementationDefinitionNode(node.Tokens, node.Documentation, node.Namespace, node.Type, node.Interface, functions); + } + + private static BoundInterfaceDefinitionNode BindInterfaceDefinition(InterfaceDefinitionNode node) + { + var functions = new List(); + + foreach (var func in node.Functions) + { + var parameters = new List(); + + foreach (var parameter in func.Parameters) + { + parameters.Add(new BoundFuncParameter(parameter.Name, parameter.Type)); + } + + functions.Add(new BoundInterfaceFunc(func.Name, parameters, func.ReturnType)); + } + + return new BoundInterfaceDefinitionNode(node.Tokens, node.Documentation, node.Namespace, node.Name, functions); + } + private static BoundStructDefinitionNode BindStruct(StructDefinitionNode node) { var defOpt = _definitionTable.LookupStruct(node.Namespace, node.Name); @@ -354,6 +395,12 @@ public static class Binder NubType? type = null; + var implementation = _definitionTable.GetImplementations().FirstOrDefault(x => x.Type.Equals(boundExpression.Type)); + if (implementation != null) + { + if (implementation.Interface.) + } + switch (boundExpression.Type) { case NubArrayType: diff --git a/src/compiler/Syntax/Typing/BoundNode/Definition.cs b/src/compiler/Syntax/Typing/BoundNode/Definition.cs index fcfa22c..20105b0 100644 --- a/src/compiler/Syntax/Typing/BoundNode/Definition.cs +++ b/src/compiler/Syntax/Typing/BoundNode/Definition.cs @@ -12,3 +12,9 @@ public record BoundExternFuncDefinitionNode(IEnumerable Tokens, Optional< 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); + +public record BoundInterfaceFunc(string Name, List Parameters, NubType ReturnType); +public record BoundInterfaceDefinitionNode(IEnumerable Tokens, Optional Documentation, string Namespace, string Name, List Functions) : BoundDefinitionNode(Tokens, Documentation, Namespace); + +public record BoundImplementationFunc(string Name, List Parameters, NubType ReturnType, BoundBlockNode Body); +public record BoundImplementationDefinitionNode(IEnumerable Tokens, Optional Documentation, string Namespace, NubType Type, NubInterfaceType Interface, List Functions) : BoundDefinitionNode(Tokens, Documentation, Namespace); \ No newline at end of file