From 5f2d1ff3f94e60835ffdfc2e163c9fbe5801d784 Mon Sep 17 00:00:00 2001 From: nub31 Date: Sun, 4 May 2025 16:02:48 +0200 Subject: [PATCH 01/32] ... --- build.sh | 3 +- example/core/array.nub | 1 - example/core/print.nub | 20 +- example/core/string.nub | 4 +- example/program.nub | 15 +- .../.idea.Nub.Lang/.idea/indexLayout.xml | 1 + lang/Nub.Lang/Backend/Custom/Generator.cs | 293 +++++++----------- lang/Nub.Lang/Backend/Custom/SymbolTable.cs | 26 +- lang/Nub.Lang/Frontend/Lexing/Lexer.cs | 6 +- lang/Nub.Lang/Frontend/Lexing/LiteralToken.cs | 4 +- .../Frontend/Parsing/ArrayIndexAccessNode.cs | 9 - .../Parsing/ArrayIndexAssignmentNode.cs | 8 - .../Frontend/Parsing/ArrayInitializerNode.cs | 7 - .../Frontend/Parsing/ExpressionNode.cs | 4 +- .../Parsing/ExternFuncDefinitionNode.cs | 4 +- lang/Nub.Lang/Frontend/Parsing/LiteralNode.cs | 4 +- .../Parsing/LocalFuncDefinitionNode.cs | 4 +- lang/Nub.Lang/Frontend/Parsing/Parser.cs | 151 ++++----- .../Frontend/Parsing/StructInitializerNode.cs | 4 +- .../Frontend/Typing/ExpressionTyper.cs | 66 +--- lang/Nub.Lang/FuncParameter.cs | 4 +- lang/Nub.Lang/NubType.cs | 41 +++ lang/Nub.Lang/StructMember.cs | 4 +- lang/Nub.Lang/Type.cs | 114 ------- std/core/arr_size.asm | 6 - 25 files changed, 264 insertions(+), 539 deletions(-) delete mode 100644 example/core/array.nub delete mode 100644 lang/Nub.Lang/Frontend/Parsing/ArrayIndexAccessNode.cs delete mode 100644 lang/Nub.Lang/Frontend/Parsing/ArrayIndexAssignmentNode.cs delete mode 100644 lang/Nub.Lang/Frontend/Parsing/ArrayInitializerNode.cs create mode 100644 lang/Nub.Lang/NubType.cs delete mode 100644 lang/Nub.Lang/Type.cs delete mode 100644 std/core/arr_size.asm diff --git a/build.sh b/build.sh index 5aaabed..5e62e94 100755 --- a/build.sh +++ b/build.sh @@ -7,9 +7,8 @@ gcc -c -g -fno-stack-protector -fno-builtin std/baseline/gc.c -o out/gc.o nasm -g -felf64 std/baseline/str_cmp.asm -o out/str_cmp.o nasm -g -felf64 std/core/str_len.asm -o out/str_len.o -nasm -g -felf64 std/core/arr_size.asm -o out/arr_size.o nasm -g -felf64 std/core/itoa.asm -o out/itoa.o nasm -g -felf64 out/out.asm -o out/out.o -gcc -no-pie -nostartfiles -o out/program out/gc.o out/str_cmp.o out/str_len.o out/arr_size.o out/itoa.o out/out.o +gcc -no-pie -nostartfiles -o out/program out/gc.o out/str_cmp.o out/str_len.o out/itoa.o out/out.o diff --git a/example/core/array.nub b/example/core/array.nub deleted file mode 100644 index dc7e6e3..0000000 --- a/example/core/array.nub +++ /dev/null @@ -1 +0,0 @@ -extern func arr_size(array: Array): int64; \ No newline at end of file diff --git a/example/core/print.nub b/example/core/print.nub index 624233a..4c559e8 100644 --- a/example/core/print.nub +++ b/example/core/print.nub @@ -2,16 +2,16 @@ let SYS_WRITE = 1; let STD_OUT = 1; let STD_ERR = 2; -func print(msg: String) { +func print(msg: string) { syscall(SYS_WRITE, STD_OUT, msg, str_len(msg)); } -func print(value1: int64) { - print(itoa(value1)); +func print(value: int64) { + print(itoa(value)); } -func print(value2: bool) { - if value2 { +func print(value: bool) { + if value { print("true"); } else { print("false"); @@ -22,17 +22,17 @@ func println() { print("\n"); } -func println(msg: String) { +func println(msg: string) { print(msg); println(); } -func println(value3: bool) { - print(value3); +func println(value: bool) { + print(value); println(); } -func println(value4: int64) { - print(value4); +func println(value: int64) { + print(value); println(); } diff --git a/example/core/string.nub b/example/core/string.nub index 3e0ce0b..45b1051 100644 --- a/example/core/string.nub +++ b/example/core/string.nub @@ -1,2 +1,2 @@ -extern func str_len(msg: String): int64; -extern func itoa(value: int64): String; \ No newline at end of file +extern func str_len(msg: string): int64; +extern func itoa(value: int64): string; diff --git a/example/program.nub b/example/program.nub index 1f2735b..52466a4 100644 --- a/example/program.nub +++ b/example/program.nub @@ -1,16 +1,5 @@ import "core"; -struct Human { - let name: String; - let age: int64; -} - func main() { - while true { - let x = new Human - { - name = "test", - age = 34958743 - }; - } -} \ No newline at end of file + println("test"); +} diff --git a/lang/.idea/.idea.Nub.Lang/.idea/indexLayout.xml b/lang/.idea/.idea.Nub.Lang/.idea/indexLayout.xml index 1479e46..d39e765 100644 --- a/lang/.idea/.idea.Nub.Lang/.idea/indexLayout.xml +++ b/lang/.idea/.idea.Nub.Lang/.idea/indexLayout.xml @@ -2,6 +2,7 @@ + ../example ../std diff --git a/lang/Nub.Lang/Backend/Custom/Generator.cs b/lang/Nub.Lang/Backend/Custom/Generator.cs index da3b950..db25bae 100644 --- a/lang/Nub.Lang/Backend/Custom/Generator.cs +++ b/lang/Nub.Lang/Backend/Custom/Generator.cs @@ -6,7 +6,6 @@ namespace Nub.Lang.Backend.Custom; public class Generator { private const string Entrypoint = "main"; - private const bool ZeroBasedIndexing = false; private readonly List _definitions; private readonly SymbolTable _symbolTable; @@ -129,17 +128,20 @@ public class Generator } case LiteralNode literal: { - if (literal.Type is not PrimitiveType primitiveType) + if (literal.LiteralType.Equals(NubType.Int64) + || literal.LiteralType.Equals(NubType.Int32) + || literal.LiteralType.Equals(NubType.Int16) + || literal.LiteralType.Equals(NubType.Int8)) { - throw new NotSupportedException("Global variable literals must be of a primitive type"); + return literal.Literal; } - return primitiveType.Kind switch + if (literal.LiteralType.Equals(NubType.Bool)) { - PrimitiveTypeKind.Bool => bool.Parse(literal.Literal) ? "1" : "0", - PrimitiveTypeKind.Int64 or PrimitiveTypeKind.Int32 => $"{literal.Literal}", - _ => throw new ArgumentOutOfRangeException() - }; + return bool.Parse(literal.Literal) ? "1" : "0"; + } + + throw new InvalidOperationException("BAD"); } default: { @@ -194,9 +196,6 @@ public class Generator { switch (statement) { - case ArrayIndexAssignmentNode arrayIndexAssignment: - GenerateArrayIndexAssignment(arrayIndexAssignment, func); - break; case BreakNode: GenerateBreak(); break; @@ -239,15 +238,6 @@ public class Generator _builder.AppendLine($" jmp {_loops.Peek().StartLabel}"); } - private void GenerateArrayIndexAssignment(ArrayIndexAssignmentNode arrayIndexAssignment, LocalFunc func) - { - GenerateExpression(arrayIndexAssignment.Value, func); - _builder.AppendLine(" push rax"); - GenerateArrayIndexPointerAccess(arrayIndexAssignment.Identifier, arrayIndexAssignment.Index, func); - _builder.AppendLine(" pop rdx"); - _builder.AppendLine(" mov [rax], rdx"); - } - private void GenerateIf(IfNode ifStatement, LocalFunc func) { var endLabel = _labelFactory.Create(); @@ -319,12 +309,6 @@ public class Generator { switch (expression) { - case ArrayIndexAccessNode arrayIndexAccess: - GenerateArrayIndexAccess(arrayIndexAccess, func); - break; - case ArrayInitializerNode arrayInitializer: - GenerateArrayInitializer(arrayInitializer); - break; case BinaryExpressionNode binaryExpression: GenerateBinaryExpression(binaryExpression, func); break; @@ -355,31 +339,21 @@ public class Generator { var variable = func.ResolveLocalVariable(structMemberAccessor.Members[0]); - if (variable.Type is not StructType structType) - { - throw new Exception($"Cannot access struct member on {variable} since it is not a struct type"); - } - _builder.AppendLine($" mov rax, [rbp - {variable.Offset}]"); - Type prevMemberType = structType; + var prevMemberType = variable.Type; for (var i = 1; i < structMemberAccessor.Members.Count; i++) { - if (prevMemberType is not StructType prevMemberStructType) - { - throw new Exception($"Cannot access {structMemberAccessor.Members[i]} on type {prevMemberType} because it is not a struct type"); - } - - var structDefinition = _definitions.OfType().FirstOrDefault(sd => sd.Name == prevMemberStructType.Name); + var structDefinition = _definitions.OfType().FirstOrDefault(sd => sd.Name == prevMemberType.Name); if (structDefinition == null) { - throw new Exception($"Struct {prevMemberStructType} is not defined"); + throw new Exception($"Struct {prevMemberType} is not defined"); } var member = structDefinition.Members.FirstOrDefault(m => m.Name == structMemberAccessor.Members[i]); if (member == null) { - throw new Exception($"Struct {prevMemberStructType} has no member with name {structMemberAccessor.Members[i]}"); + throw new Exception($"Struct {prevMemberType} has no member with name {structMemberAccessor.Members[i]}"); } var offset = structDefinition.Members.IndexOf(member); @@ -389,19 +363,6 @@ public class Generator } } - private void GenerateArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess, LocalFunc func) - { - GenerateArrayIndexPointerAccess(arrayIndexAccess.Identifier, arrayIndexAccess.Index, func); - _builder.AppendLine(" mov rax, [rax]"); - } - - private void GenerateArrayInitializer(ArrayInitializerNode arrayInitializer) - { - _builder.AppendLine($" mov rdi, {8 + arrayInitializer.Length * 8}"); - _builder.AppendLine(" call gc_alloc"); - _builder.AppendLine($" mov qword [rax], {arrayInitializer.Length}"); - } - private void GenerateBinaryExpression(BinaryExpressionNode binaryExpression, LocalFunc func) { GenerateExpression(binaryExpression.Left, func); @@ -459,108 +420,121 @@ public class Generator } } - private void GenerateComparison(Type type) + private void GenerateComparison(NubType type) { - switch (type) + if (type.Equals(NubType.String)) { - case AnyType: - throw new InvalidOperationException($"Cannot compare type {type}"); - case ArrayType: - // compare pointers - _builder.AppendLine(" cmp rax, rcx"); - break; - case PrimitiveType: - _builder.AppendLine(" cmp rax, rcx"); - break; - case StringType: - _builder.AppendLine(" mov rdi, rax"); - _builder.AppendLine(" mov rsi, rcx"); - _builder.AppendLine(" call str_cmp"); - break; - default: - throw new ArgumentOutOfRangeException(nameof(type)); + _builder.AppendLine(" mov rdi, rax"); + _builder.AppendLine(" mov rsi, rcx"); + _builder.AppendLine(" call str_cmp"); + } + else if (type.Equals(NubType.Bool) || type.Equals(NubType.Int64) || type.Equals(NubType.Int32) || type.Equals(NubType.Int16) || type.Equals(NubType.Int8)) + { + _builder.AppendLine(" cmp rax, rcx"); + } + else + { + throw new ArgumentOutOfRangeException(nameof(type)); } } - private void GenerateBinaryAddition(Type type) + private void GenerateBinaryAddition(NubType type) { - if (type is not PrimitiveType primitiveType) + if (type.Equals(NubType.Int64)) { - throw new InvalidOperationException("Addition can only be done on primitive types"); + _builder.AppendLine(" add rax, rcx"); } - - switch (primitiveType.Kind) + else if (type.Equals(NubType.Int32)) { - case PrimitiveTypeKind.Int64: - _builder.AppendLine(" add rax, rcx"); - break; - case PrimitiveTypeKind.Int32: - _builder.AppendLine(" add eax, ecx"); - break; - default: - throw new InvalidOperationException($"Invalid type {primitiveType.Kind}"); + _builder.AppendLine(" add eax, ecx"); + } + else if (type.Equals(NubType.Int16)) + { + _builder.AppendLine(" add ax, cx"); + } + else if (type.Equals(NubType.Int8)) + { + _builder.AppendLine(" add al, cl"); + } + else + { + throw new InvalidOperationException($"Invalid type for addition {type}"); } } - private void GenerateBinarySubtraction(Type type) + private void GenerateBinarySubtraction(NubType type) { - if (type is not PrimitiveType primitiveType) + if (type.Equals(NubType.Int64)) { - throw new InvalidOperationException("Subtraction can only be done on primitive types"); + _builder.AppendLine(" sub rax, rcx"); } - - switch (primitiveType.Kind) + else if (type.Equals(NubType.Int32)) { - case PrimitiveTypeKind.Int64: - _builder.AppendLine(" sub rax, rcx"); - break; - case PrimitiveTypeKind.Int32: - _builder.AppendLine(" sub eax, ecx"); - break; - default: - throw new InvalidOperationException($"Invalid type {primitiveType.Kind}"); + _builder.AppendLine(" sub eax, ecx"); + } + else if (type.Equals(NubType.Int16)) + { + _builder.AppendLine(" sub ax, cx"); + } + else if (type.Equals(NubType.Int8)) + { + _builder.AppendLine(" sub al, cl"); + } + else + { + throw new InvalidOperationException($"Invalid type for subtraction {type}"); } } - private void GenerateBinaryMultiplication(Type type) + private void GenerateBinaryMultiplication(NubType type) { - if (type is not PrimitiveType primitiveType) + if (type.Equals(NubType.Int64)) { - throw new InvalidOperationException("Multiplication can only be done on primitive types"); + _builder.AppendLine(" imul rcx"); } - - switch (primitiveType.Kind) + else if (type.Equals(NubType.Int32)) { - case PrimitiveTypeKind.Int64: - _builder.AppendLine(" imul rcx"); - break; - case PrimitiveTypeKind.Int32: - _builder.AppendLine(" imul ecx"); - break; - default: - throw new InvalidOperationException($"Invalid type {primitiveType.Kind}"); + _builder.AppendLine(" imul ecx"); + } + else if (type.Equals(NubType.Int16)) + { + _builder.AppendLine(" imul cx"); + } + else if (type.Equals(NubType.Int8)) + { + _builder.AppendLine(" imul cl"); + } + else + { + throw new InvalidOperationException($"Invalid type for multiplication {type}"); } } - private void GenerateBinaryDivision(Type type) + private void GenerateBinaryDivision(NubType type) { - if (type is not PrimitiveType primitiveType) + if (type.Equals(NubType.Int64)) { - throw new InvalidOperationException("Division can only be done on primitive types"); + _builder.AppendLine(" cqo"); + _builder.AppendLine(" idiv rcx"); } - - switch (primitiveType.Kind) + else if (type.Equals(NubType.Int32)) { - case PrimitiveTypeKind.Int64: - _builder.AppendLine(" cqo"); - _builder.AppendLine(" idiv rcx"); - break; - case PrimitiveTypeKind.Int32: - _builder.AppendLine(" cdq"); - _builder.AppendLine(" idiv ecx"); - break; - default: - throw new InvalidOperationException($"Invalid type {primitiveType.Kind}"); + _builder.AppendLine(" cdq"); + _builder.AppendLine(" idiv ecx"); + } + else if (type.Equals(NubType.Int16)) + { + _builder.AppendLine(" cwd"); + _builder.AppendLine(" idiv cx"); + } + else if (type.Equals(NubType.Int8)) + { + _builder.AppendLine(" cbw"); + _builder.AppendLine(" idiv cl"); + } + else + { + throw new InvalidOperationException($"Invalid type for division {type}"); } } @@ -586,34 +560,22 @@ public class Generator private void GenerateLiteral(LiteralNode literal) { - switch (literal.Type) + if (literal.Type.Equals(NubType.String)) { - case StringType: - { - var label = _symbolTable.DefineString(literal.Literal); - _builder.AppendLine($" mov rax, {label}"); - break; - } - case PrimitiveType primitive: - { - switch (primitive.Kind) - { - case PrimitiveTypeKind.Bool: - _builder.AppendLine($" mov rax, {(bool.Parse(literal.Literal) ? "1" : "0")}"); - break; - case PrimitiveTypeKind.Int64: - _builder.AppendLine($" mov rax, {literal.Literal}"); - break; - case PrimitiveTypeKind.Int32: - _builder.AppendLine($" mov rax, {literal.Literal}"); - break; - default: - throw new Exception("Cannot convert literal to string"); - } - break; - } - default: - throw new ArgumentOutOfRangeException(); + var label = _symbolTable.DefineString(literal.Literal); + _builder.AppendLine($" mov rax, {label}"); + } + else if (literal.Type.Equals(NubType.Int64) || literal.Type.Equals(NubType.Int32) || literal.Type.Equals(NubType.Int16) || literal.Type.Equals(NubType.Int8)) + { + _builder.AppendLine($" mov rax, {literal.Literal}"); + } + else if (literal.Type.Equals(NubType.Bool)) + { + _builder.AppendLine($" mov rax, {(bool.Parse(literal.Literal) ? "1" : "0")}"); + } + else + { + throw new NotImplementedException($"Literal type {literal.Type} not implemented"); } } @@ -706,31 +668,4 @@ public class Generator _builder.AppendLine(" syscall"); } - - private void GenerateArrayIndexPointerAccess(IdentifierNode identifier, ExpressionNode index, LocalFunc func) - { - GenerateExpression(index, func); - _builder.AppendLine(" push rax"); - GenerateIdentifier(identifier, func); - _builder.AppendLine(" pop rdx"); - - // rcx now holds the length of the array which we can use to check bounds - _builder.AppendLine(" mov rcx, [rax]"); - _builder.AppendLine(" cmp rdx, rcx"); - if (ZeroBasedIndexing) - { - _builder.AppendLine(" jge eb6e_oob_error"); - _builder.AppendLine(" cmp rdx, 0"); - } - else - { - _builder.AppendLine(" jg eb6e_oob_error"); - _builder.AppendLine(" cmp rdx, 1"); - } - _builder.AppendLine(" jl eb6e_oob_error"); - - _builder.AppendLine(" inc rdx"); - _builder.AppendLine(" shl rdx, 3"); - _builder.AppendLine(" add rax, rdx"); - } } \ No newline at end of file diff --git a/lang/Nub.Lang/Backend/Custom/SymbolTable.cs b/lang/Nub.Lang/Backend/Custom/SymbolTable.cs index ea0b193..81ea344 100644 --- a/lang/Nub.Lang/Backend/Custom/SymbolTable.cs +++ b/lang/Nub.Lang/Backend/Custom/SymbolTable.cs @@ -118,7 +118,7 @@ public class SymbolTable return offset; } - public Func ResolveFunc(string name, List parameterTypes) + public Func ResolveFunc(string name, List parameterTypes) { var func = _funcDefinitions.FirstOrDefault(f => f.SignatureMatches(name, parameterTypes)); if (func == null) @@ -129,7 +129,7 @@ public class SymbolTable return func; } - public LocalFunc ResolveLocalFunc(string name, List parameterTypes) + public LocalFunc ResolveLocalFunc(string name, List parameterTypes) { var func = ResolveFunc(name, parameterTypes); if (func is not LocalFunc localFunc) @@ -139,7 +139,7 @@ public class SymbolTable return localFunc; } - public ExternFunc ResolveExternFunc(string name, List parameterTypes) + public ExternFunc ResolveExternFunc(string name, List parameterTypes) { var func = ResolveFunc(name, parameterTypes); if (func is not ExternFunc externFunc) @@ -161,27 +161,27 @@ public class SymbolTable } } -public abstract class Variable(string name, Type type) +public abstract class Variable(string name, NubType type) { public string Name { get; } = name; - public Type Type { get; } = type; + public NubType Type { get; } = type; public override string ToString() => $"{Name}: {Type}"; } -public class LocalVariable(string name, Type type, int offset) : Variable(name, type) +public class LocalVariable(string name, NubType type, int offset) : Variable(name, type) { public int Offset { get; } = offset; } -public class GlobalVariable(string name, Type type, string identifier) : Variable(name, type) +public class GlobalVariable(string name, NubType type, string identifier) : Variable(name, type) { public string Identifier { get; } = identifier; } public abstract class Func { - protected Func(string name, string startLabel, List parameters, Optional returnType) + protected Func(string name, string startLabel, List parameters, Optional returnType) { Name = name; Parameters = parameters; @@ -192,16 +192,16 @@ public abstract class Func public string Name { get; } public string StartLabel { get; } public List Parameters { get; } - public Optional ReturnType { get; } + public Optional ReturnType { get; } - public bool SignatureMatches(string name, List parameterTypes) + public bool SignatureMatches(string name, List parameterTypes) { if (Name != name) return false; if (Parameters.Count != parameterTypes.Count) return false; for (var i = 0; i < parameterTypes.Count; i++) { - if (!Parameters[i].Type.IsAssignableTo(parameterTypes[i])) return false; + if (!Parameters[i].Type.Equals(parameterTypes[i])) return false; } return true; @@ -212,14 +212,14 @@ public abstract class Func public class ExternFunc : Func { - public ExternFunc(string name, string startLabel, List parameters, Optional returnType) : base(name, startLabel, parameters, returnType) + public ExternFunc(string name, string startLabel, List parameters, Optional returnType) : base(name, startLabel, parameters, returnType) { } } public class LocalFunc : Func { - public LocalFunc(string name, string startLabel, string endLabel, List parameters, Optional returnType, List variables) : base(name, startLabel, parameters, returnType) + public LocalFunc(string name, string startLabel, string endLabel, List parameters, Optional returnType, List variables) : base(name, startLabel, parameters, returnType) { EndLabel = endLabel; Variables = variables; diff --git a/lang/Nub.Lang/Frontend/Lexing/Lexer.cs b/lang/Nub.Lang/Frontend/Lexing/Lexer.cs index c87c255..32e8bdd 100644 --- a/lang/Nub.Lang/Frontend/Lexing/Lexer.cs +++ b/lang/Nub.Lang/Frontend/Lexing/Lexer.cs @@ -86,7 +86,7 @@ public class Lexer if (buffer is "true" or "false") { - return new LiteralToken(new PrimitiveType(PrimitiveTypeKind.Bool), buffer); + return new LiteralToken(NubType.Bool, buffer); } return new IdentifierToken(buffer); @@ -103,7 +103,7 @@ public class Lexer current = Peek(); } - return new LiteralToken(new PrimitiveType(PrimitiveTypeKind.Int64), buffer); + return new LiteralToken(NubType.Int64, buffer); } // TODO: Revisit this @@ -148,7 +148,7 @@ public class Lexer buffer += current.Value; } - return new LiteralToken(new StringType(), buffer); + return new LiteralToken(NubType.String, buffer); } if (char.IsWhiteSpace(current.Value)) diff --git a/lang/Nub.Lang/Frontend/Lexing/LiteralToken.cs b/lang/Nub.Lang/Frontend/Lexing/LiteralToken.cs index 7128b1a..a1b3b20 100644 --- a/lang/Nub.Lang/Frontend/Lexing/LiteralToken.cs +++ b/lang/Nub.Lang/Frontend/Lexing/LiteralToken.cs @@ -1,7 +1,7 @@ namespace Nub.Lang.Frontend.Lexing; -public class LiteralToken(Type type, string value) : Token +public class LiteralToken(NubType type, string value) : Token { - public Type Type { get; } = type; + public NubType Type { get; } = type; public string Value { get; } = value; } \ No newline at end of file diff --git a/lang/Nub.Lang/Frontend/Parsing/ArrayIndexAccessNode.cs b/lang/Nub.Lang/Frontend/Parsing/ArrayIndexAccessNode.cs deleted file mode 100644 index cecdbee..0000000 --- a/lang/Nub.Lang/Frontend/Parsing/ArrayIndexAccessNode.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Nub.Lang.Frontend.Parsing; - -public class ArrayIndexAccessNode(IdentifierNode identifier, ExpressionNode index) : ExpressionNode -{ - public IdentifierNode Identifier { get; } = identifier; - public ExpressionNode Index { get; } = index; - - public override string ToString() => $"{Identifier}[{Index}]"; -} \ No newline at end of file diff --git a/lang/Nub.Lang/Frontend/Parsing/ArrayIndexAssignmentNode.cs b/lang/Nub.Lang/Frontend/Parsing/ArrayIndexAssignmentNode.cs deleted file mode 100644 index bcee92a..0000000 --- a/lang/Nub.Lang/Frontend/Parsing/ArrayIndexAssignmentNode.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Nub.Lang.Frontend.Parsing; - -public class ArrayIndexAssignmentNode(IdentifierNode identifier, ExpressionNode index, ExpressionNode value) : StatementNode -{ - public IdentifierNode Identifier { get; } = identifier; - public ExpressionNode Index { get; } = index; - public ExpressionNode Value { get; } = value; -} \ No newline at end of file diff --git a/lang/Nub.Lang/Frontend/Parsing/ArrayInitializerNode.cs b/lang/Nub.Lang/Frontend/Parsing/ArrayInitializerNode.cs deleted file mode 100644 index 7f7414b..0000000 --- a/lang/Nub.Lang/Frontend/Parsing/ArrayInitializerNode.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Nub.Lang.Frontend.Parsing; - -public class ArrayInitializerNode(long length, Type innerType) : ExpressionNode -{ - public long Length { get; } = length; - public Type InnerType { get; } = innerType; -} \ No newline at end of file diff --git a/lang/Nub.Lang/Frontend/Parsing/ExpressionNode.cs b/lang/Nub.Lang/Frontend/Parsing/ExpressionNode.cs index fa7c103..c02c83c 100644 --- a/lang/Nub.Lang/Frontend/Parsing/ExpressionNode.cs +++ b/lang/Nub.Lang/Frontend/Parsing/ExpressionNode.cs @@ -2,8 +2,8 @@ public abstract class ExpressionNode : Node { - private Type? _type; - public Type Type + private NubType? _type; + public NubType Type { get => _type ?? throw new Exception("Tried to access expression type before type was populated"); set => _type = value; diff --git a/lang/Nub.Lang/Frontend/Parsing/ExternFuncDefinitionNode.cs b/lang/Nub.Lang/Frontend/Parsing/ExternFuncDefinitionNode.cs index 466b903..f841795 100644 --- a/lang/Nub.Lang/Frontend/Parsing/ExternFuncDefinitionNode.cs +++ b/lang/Nub.Lang/Frontend/Parsing/ExternFuncDefinitionNode.cs @@ -1,10 +1,10 @@ namespace Nub.Lang.Frontend.Parsing; -public class ExternFuncDefinitionNode(string name, List parameters, Optional returnType) : DefinitionNode +public class ExternFuncDefinitionNode(string name, List parameters, Optional returnType) : DefinitionNode { public string Name { get; } = name; public List Parameters { get; } = parameters; - public Optional ReturnType { get; } = returnType; + public Optional ReturnType { get; } = returnType; public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}"; } \ No newline at end of file diff --git a/lang/Nub.Lang/Frontend/Parsing/LiteralNode.cs b/lang/Nub.Lang/Frontend/Parsing/LiteralNode.cs index a5065d8..b9798bc 100644 --- a/lang/Nub.Lang/Frontend/Parsing/LiteralNode.cs +++ b/lang/Nub.Lang/Frontend/Parsing/LiteralNode.cs @@ -1,7 +1,7 @@ namespace Nub.Lang.Frontend.Parsing; -public class LiteralNode(string literal, Type type) : ExpressionNode +public class LiteralNode(string literal, NubType type) : ExpressionNode { public string Literal { get; } = literal; - public Type LiteralType { get; } = type; + public NubType LiteralType { get; } = type; } \ No newline at end of file diff --git a/lang/Nub.Lang/Frontend/Parsing/LocalFuncDefinitionNode.cs b/lang/Nub.Lang/Frontend/Parsing/LocalFuncDefinitionNode.cs index 561b40d..d1e0714 100644 --- a/lang/Nub.Lang/Frontend/Parsing/LocalFuncDefinitionNode.cs +++ b/lang/Nub.Lang/Frontend/Parsing/LocalFuncDefinitionNode.cs @@ -1,11 +1,11 @@ namespace Nub.Lang.Frontend.Parsing; -public class LocalFuncDefinitionNode(string name, List parameters, BlockNode body, Optional returnType) : DefinitionNode +public class LocalFuncDefinitionNode(string name, List parameters, BlockNode body, Optional returnType) : DefinitionNode { public string Name { get; } = name; public List Parameters { get; } = parameters; public BlockNode Body { get; } = body; - public Optional ReturnType { get; } = returnType; + public Optional ReturnType { get; } = returnType; public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}"; } \ No newline at end of file diff --git a/lang/Nub.Lang/Frontend/Parsing/Parser.cs b/lang/Nub.Lang/Frontend/Parsing/Parser.cs index 8a4d508..c564d8c 100644 --- a/lang/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/lang/Nub.Lang/Frontend/Parsing/Parser.cs @@ -7,21 +7,21 @@ public class Parser { private List _tokens = []; private int _index; - + public ModuleNode ParseModule(List tokens, string path) { _index = 0; _tokens = tokens; - + List definitions = []; List imports = []; - + while (Peek().HasValue) { if (TryExpectSymbol(Symbol.Import)) { var name = ExpectLiteral(); - if (name.Type is not StringType) + if (!name.Type.Equals(NubType.String)) { throw new Exception("Import statements must have a string literal value"); } @@ -75,10 +75,10 @@ public class Parser } } - var returnType = Optional.Empty(); + var returnType = Optional.Empty(); if (TryExpectSymbol(Symbol.Colon)) { - returnType = ParseType(); + returnType = ParseTypeInstance(); } var body = ParseBlock(); @@ -101,14 +101,14 @@ public class Parser } } - var returnType = Optional.Empty(); + var returnType = Optional.Empty(); if (TryExpectSymbol(Symbol.Colon)) { - returnType = ParseType(); + returnType = ParseTypeInstance(); } ExpectSymbol(Symbol.Semicolon); - + return new ExternFuncDefinitionNode(name.Value, parameters, returnType); } @@ -117,28 +117,27 @@ public class Parser var name = ExpectIdentifier().Value; ExpectSymbol(Symbol.OpenBrace); - + List variables = []; - + while (!TryExpectSymbol(Symbol.CloseBrace)) { - ExpectSymbol(Symbol.Let); var variableName = ExpectIdentifier().Value; ExpectSymbol(Symbol.Colon); - var variableType = ParseType(); + var variableType = ParseTypeInstance(); var variableValue = Optional.Empty(); - + if (TryExpectSymbol(Symbol.Assign)) { variableValue = ParseExpression(); } - + ExpectSymbol(Symbol.Semicolon); - + variables.Add(new StructMember(variableName, variableType, variableValue)); } - + return new StructDefinitionNode(name, variables); } @@ -146,7 +145,7 @@ public class Parser { var name = ExpectIdentifier(); ExpectSymbol(Symbol.Colon); - var type = ParseType(); + var type = ParseTypeInstance(); return new FuncParameter(name.Value, type); } @@ -179,15 +178,6 @@ public class Parser return new FuncCallStatementNode(new FuncCall(identifier.Value, parameters)); } - case Symbol.OpenBracket: - { - var index = ParseExpression(); - ExpectSymbol(Symbol.CloseBracket); - ExpectSymbol(Symbol.Assign); - var value = ParseExpression(); - ExpectSymbol(Symbol.Semicolon); - return new ArrayIndexAssignmentNode(new IdentifierNode(identifier.Value), index, value); - } case Symbol.Assign: { var value = ParseExpression(); @@ -238,7 +228,7 @@ public class Parser ExpectSymbol(Symbol.Assign); var value = ParseExpression(); ExpectSymbol(Symbol.Semicolon); - + return new VariableAssignmentNode(name, value); } @@ -254,7 +244,7 @@ public class Parser ? (Variant)ParseIf() : (Variant)ParseBlock(); } - + return new IfNode(condition, body, elseStatement); } @@ -276,7 +266,7 @@ public class Parser ExpectSymbol(Symbol.Semicolon); return new ContinueNode(); } - + private ExpressionNode ParseExpression(int precedence = 0) { var left = ParsePrimaryExpression(); @@ -284,15 +274,16 @@ public class Parser while (true) { var token = Peek(); - if (!token.HasValue || token.Value is not SymbolToken symbolToken || !TryGetBinaryOperator(symbolToken.Symbol, out var op) || GetBinaryOperatorPrecedence(op.Value) < precedence) + if (!token.HasValue || token.Value is not SymbolToken symbolToken || !TryGetBinaryOperator(symbolToken.Symbol, out var op) || + GetBinaryOperatorPrecedence(op.Value) < precedence) break; - + Next(); var right = ParseExpression(GetBinaryOperatorPrecedence(op.Value) + 1); left = new BinaryExpressionNode(left, op.Value, right); } - + return left; } @@ -360,9 +351,13 @@ public class Parser switch (token) { case LiteralToken literal: + { return new LiteralNode(literal.Value, literal.Type); + } case IdentifierToken identifier: + { return ParseExpressionIdentifier(identifier); + } case SymbolToken symbolToken: { switch (symbolToken.Symbol) @@ -375,45 +370,25 @@ public class Parser } case Symbol.New: { - var type = ParseType(); - switch (type) + var type = ParseTypeInstance(); + Dictionary initializers = []; + ExpectSymbol(Symbol.OpenBrace); + while (!TryExpectSymbol(Symbol.CloseBrace)) { - case ArrayType: - { - ExpectSymbol(Symbol.OpenParen); - var size = ExpectLiteral(); - if (size.Type is not PrimitiveType { Kind: PrimitiveTypeKind.Int64 }) - { - throw new Exception($"Array initializer size must be an {PrimitiveTypeKind.Int64}"); - } - ExpectSymbol(Symbol.CloseParen); - - return new ArrayInitializerNode(long.Parse(size.Value), type); - } - case StructType structType: - { - Dictionary initializers = []; - ExpectSymbol(Symbol.OpenBrace); - while (!TryExpectSymbol(Symbol.CloseBrace)) - { - var name = ExpectIdentifier().Value; - ExpectSymbol(Symbol.Assign); - var value = ParseExpression(); - TryExpectSymbol(Symbol.Comma); - initializers.Add(name, value); - } - - return new StructInitializerNode(structType, initializers); - } - default: - throw new Exception($"Type {type} cannot be initialized with the new keyword"); + var name = ExpectIdentifier().Value; + ExpectSymbol(Symbol.Assign); + var value = ParseExpression(); + TryExpectSymbol(Symbol.Semicolon); + initializers.Add(name, value); } + + return new StructInitializerNode(type, initializers); } default: throw new Exception($"Unknown symbol: {symbolToken.Symbol}"); } } - default: + default: throw new Exception($"Unexpected token type {token.GetType().Name}"); } } @@ -448,13 +423,6 @@ public class Parser return new StructMemberAccessorNode(members); } - case Symbol.OpenBracket: - { - Next(); - var index = ParseExpression(); - ExpectSymbol(Symbol.CloseBracket); - return new ArrayIndexAccessNode(new IdentifierNode(identifier.Value), index); - } case Symbol.OpenParen: { Next(); @@ -473,10 +441,11 @@ public class Parser return new FuncCallExpressionNode(new FuncCall(identifier.Value, parameters)); } } + break; } } - + return new IdentifierNode(identifier.Value); } @@ -492,36 +461,22 @@ public class Parser return new BlockNode(statements); } - private Type ParseType() + private NubType ParseTypeInstance() { + var parameters = new List(); var name = ExpectIdentifier().Value; - switch (name) + + if (TryExpectSymbol(Symbol.LessThan)) { - case "String": + do { - return new StringType(); - } - case "Array": - { - ExpectSymbol(Symbol.LessThan); - var innerType = ParseType(); - ExpectSymbol(Symbol.GreaterThan); - return new ArrayType(innerType); - } - case "Any": - { - return new AnyType(); - } - default: - { - if (PrimitiveType.TryParse(name, out var primitiveType)) - { - return primitiveType; - } - - return new StructType(name); - } + parameters.Add(ParseTypeInstance()); + } while (TryExpectSymbol(Symbol.Comma)); + + ExpectSymbol(Symbol.GreaterThan); } + + return new NubType(name, parameters.ToArray()); } private Token ExpectToken() diff --git a/lang/Nub.Lang/Frontend/Parsing/StructInitializerNode.cs b/lang/Nub.Lang/Frontend/Parsing/StructInitializerNode.cs index a04df84..2ab9b85 100644 --- a/lang/Nub.Lang/Frontend/Parsing/StructInitializerNode.cs +++ b/lang/Nub.Lang/Frontend/Parsing/StructInitializerNode.cs @@ -1,7 +1,7 @@ namespace Nub.Lang.Frontend.Parsing; -public class StructInitializerNode(StructType structType, Dictionary initializers) : ExpressionNode +public class StructInitializerNode(NubType structType, Dictionary initializers) : ExpressionNode { - public StructType StructType { get; } = structType; + public NubType StructType { get; } = structType; public Dictionary Initializers { get; } = initializers; } \ No newline at end of file diff --git a/lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs b/lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs index 4bae6cf..c8b5f3a 100644 --- a/lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs +++ b/lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs @@ -2,12 +2,12 @@ namespace Nub.Lang.Frontend.Typing; -public class Func(string name, List parameters, Optional body, Optional returnType) +public class Func(string name, List parameters, Optional body, Optional returnType) { public string Name { get; } = name; public List Parameters { get; } = parameters; public Optional Body { get; } = body; - public Optional ReturnType { get; } = returnType; + public Optional ReturnType { get; } = returnType; } public class ExpressionTyper @@ -96,9 +96,6 @@ public class ExpressionTyper { switch (statement) { - case ArrayIndexAssignmentNode arrayIndexAssignment: - PopulateArrayIndexAssignment(arrayIndexAssignment); - break; case BreakNode: case ContinueNode: break; @@ -128,13 +125,6 @@ public class ExpressionTyper } } - private void PopulateArrayIndexAssignment(ArrayIndexAssignmentNode arrayIndexAssignment) - { - PopulateIdentifier(arrayIndexAssignment.Identifier); - PopulateExpression(arrayIndexAssignment.Index); - PopulateExpression(arrayIndexAssignment.Value); - } - private void PopulateFuncCallStatement(FuncCallStatementNode funcCall) { foreach (var parameter in funcCall.FuncCall.Parameters) @@ -194,12 +184,6 @@ public class ExpressionTyper { switch (expression) { - case ArrayIndexAccessNode arrayIndexAccess: - PopulateArrayIndexAccess(arrayIndexAccess); - break; - case ArrayInitializerNode arrayInitializer: - PopulateArrayInitializer(arrayInitializer); - break; case BinaryExpressionNode binaryExpression: PopulateBinaryExpression(binaryExpression); break; @@ -226,30 +210,6 @@ public class ExpressionTyper } } - private void PopulateArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess) - { - PopulateExpression(arrayIndexAccess.Index); - PopulateIdentifier(arrayIndexAccess.Identifier); - - var variable = _variables.FirstOrDefault(v => v.Name == arrayIndexAccess.Identifier.Identifier); - if (variable == null) - { - throw new Exception($"Variable {arrayIndexAccess.Identifier} is not defined"); - } - - if (variable.Type is not ArrayType arrayType) - { - throw new Exception($"Variable {arrayIndexAccess.Identifier} is not an array type"); - } - - arrayIndexAccess.Type = arrayType.InnerType; - } - - private void PopulateArrayInitializer(ArrayInitializerNode arrayInitializer) - { - arrayInitializer.Type = arrayInitializer.InnerType; - } - private void PopulateBinaryExpression(BinaryExpressionNode binaryExpression) { PopulateExpression(binaryExpression.Left); @@ -263,7 +223,7 @@ public class ExpressionTyper case BinaryExpressionOperator.LessThan: case BinaryExpressionOperator.LessThanOrEqual: { - binaryExpression.Type = new PrimitiveType(PrimitiveTypeKind.Bool); + binaryExpression.Type = new NubType("bool", []); break; } case BinaryExpressionOperator.Plus: @@ -333,13 +293,8 @@ public class ExpressionTyper { throw new Exception($"Variable {structMemberAccessor.Members[0]} is not defined"); } - - if (variable.Type is not StructType variableType) - { - throw new Exception("Variable " + structMemberAccessor.Members[0] + " is not a struct"); - } - var definition = _structDefinitions.FirstOrDefault(sd => sd.Name == variableType.Name); + var definition = _structDefinitions.FirstOrDefault(sd => sd.Name == variable.Type.Name); if (definition == null) { throw new Exception($"Struct {structMemberAccessor.Members[0]} is not defined"); @@ -352,13 +307,8 @@ public class ExpressionTyper { throw new Exception($"Member {structMemberAccessor.Members[i]} does not exist on struct {definition.Name}"); } - - if (member.Type is not StructType memberType) - { - throw new Exception($"Member {structMemberAccessor.Members[i]} on struct {definition.Name} is not a struct"); - } - definition = _structDefinitions.FirstOrDefault(sd => sd.Name == memberType.Name); + definition = _structDefinitions.FirstOrDefault(sd => sd.Name == member.Type.Name); if (definition == null) { throw new Exception($"Struct {structMemberAccessor.Members[i]} is not defined"); @@ -381,12 +331,12 @@ public class ExpressionTyper PopulateExpression(parameter); } - syscall.Type = new PrimitiveType(PrimitiveTypeKind.Int64); + syscall.Type = new NubType("int64", []); } - private class Variable(string name, Type type) + private class Variable(string name, NubType type) { public string Name { get; } = name; - public Type Type { get; } = type; + public NubType Type { get; } = type; } } \ No newline at end of file diff --git a/lang/Nub.Lang/FuncParameter.cs b/lang/Nub.Lang/FuncParameter.cs index cfbb556..f6c49de 100644 --- a/lang/Nub.Lang/FuncParameter.cs +++ b/lang/Nub.Lang/FuncParameter.cs @@ -1,9 +1,9 @@ namespace Nub.Lang; -public class FuncParameter(string name, Type type) +public class FuncParameter(string name, NubType type) { public string Name { get; } = name; - public Type Type { get; } = type; + public NubType Type { get; } = type; public override string ToString() => $"{Name}: {Type}"; } \ No newline at end of file diff --git a/lang/Nub.Lang/NubType.cs b/lang/Nub.Lang/NubType.cs new file mode 100644 index 0000000..61b48cc --- /dev/null +++ b/lang/Nub.Lang/NubType.cs @@ -0,0 +1,41 @@ +namespace Nub.Lang; + +public sealed class NubType +{ + public NubType(string name, NubType[] generics) + { + Name = name; + Generics = generics; + } + + public string Name { get; } + public NubType[] Generics { get; } + + public static NubType Int64 => new("int64", []); + public static NubType Int32 => new("int32", []); + public static NubType Int16 => new("int16", []); + public static NubType Int8 => new("int8", []); + public static NubType Bool => new("bool", []); + public static NubType String => new("string", []); + public static NubType Array(NubType innerType) => new("array", [innerType]); + + public override bool Equals(object? obj) + { + if (obj is not NubType item) + { + return false; + } + + return Name.Equals(item.Name) && Generics.SequenceEqual(item.Generics); + } + + public override int GetHashCode() + { + return HashCode.Combine(Name, Generics); + } + + public override string ToString() + { + return $"{Name}<{string.Join(", ", Generics.Select(x => x.ToString()))}>"; + } +} \ No newline at end of file diff --git a/lang/Nub.Lang/StructMember.cs b/lang/Nub.Lang/StructMember.cs index 600687c..b3057c6 100644 --- a/lang/Nub.Lang/StructMember.cs +++ b/lang/Nub.Lang/StructMember.cs @@ -2,9 +2,9 @@ namespace Nub.Lang; -public class StructMember(string name, Type type, Optional value) +public class StructMember(string name, NubType type, Optional value) { public string Name { get; } = name; - public Type Type { get; } = type; + public NubType Type { get; } = type; public Optional Value { get; } = value; } \ No newline at end of file diff --git a/lang/Nub.Lang/Type.cs b/lang/Nub.Lang/Type.cs deleted file mode 100644 index 76b5a3b..0000000 --- a/lang/Nub.Lang/Type.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Nub.Lang; - -public abstract class Type -{ - public virtual bool IsAssignableTo(Type otherType) - { - return this == otherType || otherType is AnyType; - } - - public override bool Equals(object? obj) - { - if (ReferenceEquals(this, obj)) return true; - if (obj is not Type otherType) return false; - return Equals(otherType); - } - - protected abstract bool Equals(Type other); - public abstract override int GetHashCode(); - - public static bool operator == (Type? left, Type? right) - { - if (left is null && right is null) return true; - if (left is null || right is null) return false; - return ReferenceEquals(left, right) || left.Equals(right); - } - - public static bool operator !=(Type? left, Type? right) => !(left == right); -} - -public class AnyType : Type -{ - protected override bool Equals(Type other) => other is AnyType; - public override int GetHashCode() => nameof(AnyType).GetHashCode(); - public override string ToString() => "Any"; -} - -public class PrimitiveType(PrimitiveTypeKind kind) : Type -{ - // TODO: This should be looked at more in the future - public override bool IsAssignableTo(Type otherType) - { - if (base.IsAssignableTo(otherType)) return true; - - if (otherType is PrimitiveType otherPrimitive) - { - return (Kind, otherPrimitive.Kind) switch - { - (PrimitiveTypeKind.Int32, PrimitiveTypeKind.Int64) => true, - _ => false - }; - } - - return false; - } - - public static bool TryParse(string value, [NotNullWhen(true)] out PrimitiveType? result) - { - result = value switch - { - "bool" => new PrimitiveType(PrimitiveTypeKind.Bool), - "int64" => new PrimitiveType(PrimitiveTypeKind.Int64), - "int32" => new PrimitiveType(PrimitiveTypeKind.Int32), - _ => null - }; - - return result != null; - } - - public PrimitiveTypeKind Kind { get; } = kind; - - protected override bool Equals(Type other) => other is PrimitiveType primitiveType && Kind == primitiveType.Kind; - public override int GetHashCode() => Kind.GetHashCode(); - public override string ToString() => Kind.ToString(); -} - -public enum PrimitiveTypeKind -{ - Bool, - Int64, - Int32, -} - -public class StringType : Type -{ - protected override bool Equals(Type other) => other is StringType; - public override int GetHashCode() => nameof(StringType).GetHashCode(); - public override string ToString() => "String"; -} - -public class ArrayType(Type innerType) : Type -{ - public Type InnerType { get; } = innerType; - - public override bool IsAssignableTo(Type otherType) - { - if (otherType is ArrayType arrayType && arrayType.InnerType.IsAssignableTo(InnerType)) return true; - return base.IsAssignableTo(otherType); - } - - protected override bool Equals(Type other) => other is ArrayType at && InnerType.Equals(at.InnerType); - public override int GetHashCode() => HashCode.Combine(InnerType); - public override string ToString() => $"Array<{InnerType}>"; -} - -public class StructType(string name) : Type -{ - public string Name { get; } = name; - - protected override bool Equals(Type other) => other is StructType classType && Name == classType.Name; - public override int GetHashCode() => Name.GetHashCode(); - public override string ToString() => Name; -} \ No newline at end of file diff --git a/std/core/arr_size.asm b/std/core/arr_size.asm deleted file mode 100644 index 4c6291a..0000000 --- a/std/core/arr_size.asm +++ /dev/null @@ -1,6 +0,0 @@ -global arr_size - -section .text -arr_size: - mov rax, [rdi] - ret \ No newline at end of file From 37e788cc3a84c3bbc5a5418399059106ee5ea2ef Mon Sep 17 00:00:00 2001 From: nub31 Date: Sun, 4 May 2025 16:03:17 +0200 Subject: [PATCH 02/32] ... --- lang/Nub.Lang/Backend/{Custom => }/Generator.cs | 2 +- lang/Nub.Lang/Backend/{Custom => }/LabelFactory.cs | 2 +- lang/Nub.Lang/Backend/{Custom => }/SymbolTable.cs | 2 +- lang/Nub.Lang/Program.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename lang/Nub.Lang/Backend/{Custom => }/Generator.cs (99%) rename lang/Nub.Lang/Backend/{Custom => }/LabelFactory.cs (72%) rename lang/Nub.Lang/Backend/{Custom => }/SymbolTable.cs (99%) diff --git a/lang/Nub.Lang/Backend/Custom/Generator.cs b/lang/Nub.Lang/Backend/Generator.cs similarity index 99% rename from lang/Nub.Lang/Backend/Custom/Generator.cs rename to lang/Nub.Lang/Backend/Generator.cs index db25bae..a645632 100644 --- a/lang/Nub.Lang/Backend/Custom/Generator.cs +++ b/lang/Nub.Lang/Backend/Generator.cs @@ -1,7 +1,7 @@ using System.Text; using Nub.Lang.Frontend.Parsing; -namespace Nub.Lang.Backend.Custom; +namespace Nub.Lang.Backend; public class Generator { diff --git a/lang/Nub.Lang/Backend/Custom/LabelFactory.cs b/lang/Nub.Lang/Backend/LabelFactory.cs similarity index 72% rename from lang/Nub.Lang/Backend/Custom/LabelFactory.cs rename to lang/Nub.Lang/Backend/LabelFactory.cs index 6edd4f4..495c15a 100644 --- a/lang/Nub.Lang/Backend/Custom/LabelFactory.cs +++ b/lang/Nub.Lang/Backend/LabelFactory.cs @@ -1,4 +1,4 @@ -namespace Nub.Lang.Backend.Custom; +namespace Nub.Lang.Backend; public class LabelFactory { diff --git a/lang/Nub.Lang/Backend/Custom/SymbolTable.cs b/lang/Nub.Lang/Backend/SymbolTable.cs similarity index 99% rename from lang/Nub.Lang/Backend/Custom/SymbolTable.cs rename to lang/Nub.Lang/Backend/SymbolTable.cs index 81ea344..eef8668 100644 --- a/lang/Nub.Lang/Backend/Custom/SymbolTable.cs +++ b/lang/Nub.Lang/Backend/SymbolTable.cs @@ -1,6 +1,6 @@ using Nub.Lang.Frontend.Parsing; -namespace Nub.Lang.Backend.Custom; +namespace Nub.Lang.Backend; public class SymbolTable { diff --git a/lang/Nub.Lang/Program.cs b/lang/Nub.Lang/Program.cs index 33da40d..c43156e 100644 --- a/lang/Nub.Lang/Program.cs +++ b/lang/Nub.Lang/Program.cs @@ -1,4 +1,4 @@ -using Nub.Lang.Backend.Custom; +using Nub.Lang.Backend; using Nub.Lang.Frontend.Lexing; using Nub.Lang.Frontend.Parsing; using Nub.Lang.Frontend.Typing; From 862971bf17a4e5ee7a95a3e4ad2e031aa7b2a591 Mon Sep 17 00:00:00 2001 From: nub31 Date: Sun, 4 May 2025 16:49:42 +0200 Subject: [PATCH 03/32] Start llvm --- lang/Nub.Lang/Backend/Generator.cs | 545 +----------------- lang/Nub.Lang/Backend/LabelFactory.cs | 7 - lang/Nub.Lang/Backend/SymbolTable.cs | 309 ++++------ .../Parsing/GlobalVariableDefinitionNode.cs | 7 - lang/Nub.Lang/Frontend/Parsing/Parser.cs | 11 - .../Frontend/Typing/ExpressionTyper.cs | 9 - 6 files changed, 129 insertions(+), 759 deletions(-) delete mode 100644 lang/Nub.Lang/Backend/LabelFactory.cs delete mode 100644 lang/Nub.Lang/Frontend/Parsing/GlobalVariableDefinitionNode.cs diff --git a/lang/Nub.Lang/Backend/Generator.cs b/lang/Nub.Lang/Backend/Generator.cs index a645632..19c8cfd 100644 --- a/lang/Nub.Lang/Backend/Generator.cs +++ b/lang/Nub.Lang/Backend/Generator.cs @@ -5,186 +5,40 @@ namespace Nub.Lang.Backend; public class Generator { - private const string Entrypoint = "main"; - private readonly List _definitions; + private readonly StringBuilder _builder = new(); private readonly SymbolTable _symbolTable; - private readonly StringBuilder _builder; - private readonly LabelFactory _labelFactory; - private readonly Stack<(string StartLabel, string EndLabel)> _loops; public Generator(List definitions) { _definitions = definitions; - _builder = new StringBuilder(); - _labelFactory = new LabelFactory(); - _symbolTable = new SymbolTable(_labelFactory); - _loops = []; - - foreach (var globalVariableDefinition in definitions.OfType()) - { - _symbolTable.DefineGlobalVariable(globalVariableDefinition); - } - - foreach (var funcDefinitionNode in definitions.OfType()) - { - _symbolTable.DefineFunc(funcDefinitionNode); - } - - foreach (var funcDefinitionNode in definitions.OfType()) - { - _symbolTable.DefineFunc(funcDefinitionNode); - } + _symbolTable = SymbolTable.Create(definitions); } public string Generate() { - _builder.AppendLine("global _start"); - _builder.AppendLine("extern gc_init"); - _builder.AppendLine("extern gc_alloc"); - _builder.AppendLine("extern str_cmp"); - foreach (var externFuncDefinition in _definitions.OfType()) { - _builder.AppendLine($"extern {externFuncDefinition.Name}"); + GenerateExternFuncDefinition(externFuncDefinition); } - _builder.AppendLine(); - _builder.AppendLine("section .text"); - - // TODO: Only add start label if entrypoint is present, otherwise assume library - var main = _symbolTable.ResolveLocalFunc(Entrypoint, []); - - _builder.AppendLine("_start:"); - _builder.AppendLine(" call gc_init"); - _builder.AppendLine($" call {main.StartLabel}"); - - _builder.AppendLine(main.ReturnType.HasValue - ? " mov rdi, rax" - : " mov rdi, 0"); - _builder.AppendLine(" mov rax, 60"); - _builder.AppendLine(" syscall"); - foreach (var funcDefinition in _definitions.OfType()) { - _builder.AppendLine(); GenerateFuncDefinition(funcDefinition); } - _builder.AppendLine(""" - - eb6e_oob_error: - mov rax, 60 - mov rdi, 139 - syscall - """); - - _builder.AppendLine(); - _builder.AppendLine("section .data"); - - foreach (var str in _symbolTable.Strings) - { - _builder.AppendLine($" {str.Key}: db `{str.Value}`, 0"); - } - - Dictionary completed = []; - foreach (var globalVariableDefinition in _definitions.OfType()) - { - var variable = _symbolTable.ResolveGlobalVariable(globalVariableDefinition.Name); - var evaluated = EvaluateExpression(globalVariableDefinition.Value, completed); - _builder.AppendLine($" {variable.Identifier}: dq {evaluated}"); - completed[variable.Name] = evaluated; - } - return _builder.ToString(); } - private string EvaluateExpression(ExpressionNode expression, Dictionary completed) + private void GenerateExternFuncDefinition(ExternFuncDefinitionNode externFuncDefinition) { - switch (expression) - { - case BinaryExpressionNode binaryExpression: - { - var left = EvaluateExpression(binaryExpression.Left, completed); - var right = EvaluateExpression(binaryExpression.Right, completed); - return binaryExpression.Operator switch - { - BinaryExpressionOperator.Equal => bool.Parse(left) == bool.Parse(right) ? "1" : "0", - BinaryExpressionOperator.NotEqual => bool.Parse(left) != bool.Parse(right) ? "1" : "0", - BinaryExpressionOperator.GreaterThan => long.Parse(left) > long.Parse(right) ? "1" : "0", - BinaryExpressionOperator.GreaterThanOrEqual => long.Parse(left) >= long.Parse(right) ? "1" : "0", - BinaryExpressionOperator.LessThan => long.Parse(left) < long.Parse(right) ? "1" : "0", - BinaryExpressionOperator.LessThanOrEqual => long.Parse(left) <= long.Parse(right) ? "1" : "0", - BinaryExpressionOperator.Plus => (long.Parse(left) + long.Parse(right)).ToString(), - BinaryExpressionOperator.Minus => (long.Parse(left) - long.Parse(right)).ToString(), - BinaryExpressionOperator.Multiply => (long.Parse(left) * long.Parse(right)).ToString(), - BinaryExpressionOperator.Divide => (long.Parse(left) / long.Parse(right)).ToString(), - _ => throw new ArgumentOutOfRangeException() - }; - } - case IdentifierNode identifier: - { - return completed[identifier.Identifier]; - } - case LiteralNode literal: - { - if (literal.LiteralType.Equals(NubType.Int64) - || literal.LiteralType.Equals(NubType.Int32) - || literal.LiteralType.Equals(NubType.Int16) - || literal.LiteralType.Equals(NubType.Int8)) - { - return literal.Literal; - } - - if (literal.LiteralType.Equals(NubType.Bool)) - { - return bool.Parse(literal.Literal) ? "1" : "0"; - } - - throw new InvalidOperationException("BAD"); - } - default: - { - throw new InvalidOperationException("Global variables must be compile time consistant"); - } - } } - + private void GenerateFuncDefinition(LocalFuncDefinitionNode node) { - var func = _symbolTable.ResolveLocalFunc(node.Name, node.Parameters.Select(p => p.Type).ToList()); - - _builder.AppendLine($"{func.StartLabel}:"); - _builder.AppendLine(" push rbp"); - _builder.AppendLine(" mov rbp, rsp"); - _builder.AppendLine($" sub rsp, {func.StackAllocation}"); - - string[] registers = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"]; - - for (var i = 0; i < func.Parameters.Count; i++) - { - var parameter = func.ResolveLocalVariable(func.Parameters.ElementAt(i).Name); - if (i < registers.Length) - { - _builder.AppendLine($" mov [rbp - {parameter.Offset}], {registers[i]}"); - } - else - { - var stackOffset = 16 + (i - registers.Length) * 8; - _builder.AppendLine($" mov rax, [rbp + {stackOffset}]"); - _builder.AppendLine($" mov [rbp - {parameter.Offset}], rax"); - } - } - - GenerateBlock(node.Body, func); - - _builder.AppendLine($"{func.EndLabel}:"); - _builder.AppendLine(" mov rsp, rbp"); - _builder.AppendLine(" pop rbp"); - _builder.AppendLine(" ret"); } - private void GenerateBlock(BlockNode block, LocalFunc func) + private void GenerateBlock(BlockNode block, LocalFuncDef func) { foreach (var statement in block.Statements) { @@ -192,7 +46,7 @@ public class Generator } } - private void GenerateStatement(StatementNode statement, LocalFunc func) + private void GenerateStatement(StatementNode statement, LocalFuncDef func) { switch (statement) { @@ -230,82 +84,33 @@ public class Generator private void GenerateBreak() { - _builder.AppendLine($" jmp {_loops.Peek().EndLabel}"); } private void GenerateContinue() { - _builder.AppendLine($" jmp {_loops.Peek().StartLabel}"); } - private void GenerateIf(IfNode ifStatement, LocalFunc func) + private void GenerateIf(IfNode ifStatement, LocalFuncDef func) { - var endLabel = _labelFactory.Create(); - GenerateIf(ifStatement, endLabel, func); - _builder.AppendLine($"{endLabel}:"); } - - private void GenerateIf(IfNode ifStatement, string endLabel, LocalFunc func) + + private void GenerateReturn(ReturnNode @return, LocalFuncDef func) { - var nextLabel = _labelFactory.Create(); - GenerateExpression(ifStatement.Condition, func); - _builder.AppendLine(" cmp rax, 0"); - _builder.AppendLine($" je {nextLabel}"); - GenerateBlock(ifStatement.Body, func); - _builder.AppendLine($" jmp {endLabel}"); - _builder.AppendLine($"{nextLabel}:"); - - if (ifStatement.Else.HasValue) - { - ifStatement.Else.Value.Match - ( - elseIfStatement => GenerateIf(elseIfStatement, endLabel, func), - elseStatement => GenerateBlock(elseStatement, func) - ); - } } - private void GenerateReturn(ReturnNode @return, LocalFunc func) + private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment, LocalFuncDef func) { - if (@return.Value.HasValue) - { - GenerateExpression(@return.Value.Value, func); - } - - _builder.AppendLine($" jmp {func.EndLabel}"); } - private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment, LocalFunc func) + private void GenerateVariableReassignment(VariableReassignmentNode variableReassignment, LocalFuncDef func) { - var variable = func.ResolveLocalVariable(variableAssignment.Name); - GenerateExpression(variableAssignment.Value, func); - _builder.AppendLine($" mov [rbp - {variable.Offset}], rax"); } - private void GenerateVariableReassignment(VariableReassignmentNode variableReassignment, LocalFunc func) + private void GenerateWhile(WhileNode whileStatement, LocalFuncDef func) { - var variable = func.ResolveLocalVariable(variableReassignment.Name); - GenerateExpression(variableReassignment.Value, func); - _builder.AppendLine($" mov [rbp - {variable.Offset}], rax"); } - private void GenerateWhile(WhileNode whileStatement, LocalFunc func) - { - var startLabel = _labelFactory.Create(); - var endLabel = _labelFactory.Create(); - - _builder.AppendLine($"{startLabel}:"); - GenerateExpression(whileStatement.Condition, func); - _builder.AppendLine(" cmp rax, 0"); - _builder.AppendLine($" je {endLabel}"); - _loops.Push((startLabel, endLabel)); - GenerateBlock(whileStatement.Body, func); - _loops.Pop(); - _builder.AppendLine($" jmp {startLabel}"); - _builder.AppendLine($"{endLabel}:"); - } - - private void GenerateExpression(ExpressionNode expression, LocalFunc func) + private void GenerateExpression(ExpressionNode expression, LocalFuncDef func) { switch (expression) { @@ -319,7 +124,7 @@ public class Generator GenerateIdentifier(identifier, func); break; case LiteralNode literal: - GenerateLiteral(literal); + GenerateLiteral(literal, func); break; case StructInitializerNode structInitializer: GenerateStructInitializer(structInitializer, func); @@ -335,337 +140,31 @@ public class Generator } } - private void GenerateStructMemberAccessor(StructMemberAccessorNode structMemberAccessor, LocalFunc func) + private void GenerateStructMemberAccessor(StructMemberAccessorNode structMemberAccessor, LocalFuncDef func) { - var variable = func.ResolveLocalVariable(structMemberAccessor.Members[0]); - - _builder.AppendLine($" mov rax, [rbp - {variable.Offset}]"); - - var prevMemberType = variable.Type; - for (var i = 1; i < structMemberAccessor.Members.Count; i++) - { - var structDefinition = _definitions.OfType().FirstOrDefault(sd => sd.Name == prevMemberType.Name); - if (structDefinition == null) - { - throw new Exception($"Struct {prevMemberType} is not defined"); - } - - var member = structDefinition.Members.FirstOrDefault(m => m.Name == structMemberAccessor.Members[i]); - if (member == null) - { - throw new Exception($"Struct {prevMemberType} has no member with name {structMemberAccessor.Members[i]}"); - } - - var offset = structDefinition.Members.IndexOf(member); - _builder.AppendLine($" mov rax, [rax + {offset * 8}]"); - - prevMemberType = member.Type; - } } - private void GenerateBinaryExpression(BinaryExpressionNode binaryExpression, LocalFunc func) + private void GenerateBinaryExpression(BinaryExpressionNode binaryExpression, LocalFuncDef func) { - GenerateExpression(binaryExpression.Left, func); - _builder.AppendLine(" push rax"); - GenerateExpression(binaryExpression.Right, func); - _builder.AppendLine(" mov rcx, rax"); - _builder.AppendLine(" pop rax"); - - switch (binaryExpression.Operator) - { - case BinaryExpressionOperator.Equal: - GenerateComparison(binaryExpression.Left.Type); - _builder.AppendLine(" sete al"); - _builder.AppendLine(" movzx rax, al"); - break; - case BinaryExpressionOperator.NotEqual: - GenerateComparison(binaryExpression.Left.Type); - _builder.AppendLine(" setne al"); - _builder.AppendLine(" movzx rax, al"); - break; - case BinaryExpressionOperator.GreaterThan: - GenerateComparison(binaryExpression.Left.Type); - _builder.AppendLine(" setg al"); - _builder.AppendLine(" movzx rax, al"); - break; - case BinaryExpressionOperator.GreaterThanOrEqual: - GenerateComparison(binaryExpression.Left.Type); - _builder.AppendLine(" setge al"); - _builder.AppendLine(" movzx rax, al"); - break; - case BinaryExpressionOperator.LessThan: - GenerateComparison(binaryExpression.Left.Type); - _builder.AppendLine(" setl al"); - _builder.AppendLine(" movzx rax, al"); - break; - case BinaryExpressionOperator.LessThanOrEqual: - GenerateComparison(binaryExpression.Left.Type); - _builder.AppendLine(" setle al"); - _builder.AppendLine(" movzx rax, al"); - break; - case BinaryExpressionOperator.Plus: - GenerateBinaryAddition(binaryExpression.Left.Type); - break; - case BinaryExpressionOperator.Minus: - GenerateBinarySubtraction(binaryExpression.Left.Type); - break; - case BinaryExpressionOperator.Multiply: - GenerateBinaryMultiplication(binaryExpression.Left.Type); - break; - case BinaryExpressionOperator.Divide: - GenerateBinaryDivision(binaryExpression.Left.Type); - break; - default: - throw new ArgumentOutOfRangeException(nameof(binaryExpression.Operator)); - } } - private void GenerateComparison(NubType type) + private void GenerateIdentifier(IdentifierNode identifier, LocalFuncDef func) { - if (type.Equals(NubType.String)) - { - _builder.AppendLine(" mov rdi, rax"); - _builder.AppendLine(" mov rsi, rcx"); - _builder.AppendLine(" call str_cmp"); - } - else if (type.Equals(NubType.Bool) || type.Equals(NubType.Int64) || type.Equals(NubType.Int32) || type.Equals(NubType.Int16) || type.Equals(NubType.Int8)) - { - _builder.AppendLine(" cmp rax, rcx"); - } - else - { - throw new ArgumentOutOfRangeException(nameof(type)); - } } - private void GenerateBinaryAddition(NubType type) + private void GenerateLiteral(LiteralNode literal, LocalFuncDef func) { - if (type.Equals(NubType.Int64)) - { - _builder.AppendLine(" add rax, rcx"); - } - else if (type.Equals(NubType.Int32)) - { - _builder.AppendLine(" add eax, ecx"); - } - else if (type.Equals(NubType.Int16)) - { - _builder.AppendLine(" add ax, cx"); - } - else if (type.Equals(NubType.Int8)) - { - _builder.AppendLine(" add al, cl"); - } - else - { - throw new InvalidOperationException($"Invalid type for addition {type}"); - } - } - - private void GenerateBinarySubtraction(NubType type) - { - if (type.Equals(NubType.Int64)) - { - _builder.AppendLine(" sub rax, rcx"); - } - else if (type.Equals(NubType.Int32)) - { - _builder.AppendLine(" sub eax, ecx"); - } - else if (type.Equals(NubType.Int16)) - { - _builder.AppendLine(" sub ax, cx"); - } - else if (type.Equals(NubType.Int8)) - { - _builder.AppendLine(" sub al, cl"); - } - else - { - throw new InvalidOperationException($"Invalid type for subtraction {type}"); - } } - private void GenerateBinaryMultiplication(NubType type) + private void GenerateStructInitializer(StructInitializerNode structInitializer, LocalFuncDef func) { - if (type.Equals(NubType.Int64)) - { - _builder.AppendLine(" imul rcx"); - } - else if (type.Equals(NubType.Int32)) - { - _builder.AppendLine(" imul ecx"); - } - else if (type.Equals(NubType.Int16)) - { - _builder.AppendLine(" imul cx"); - } - else if (type.Equals(NubType.Int8)) - { - _builder.AppendLine(" imul cl"); - } - else - { - throw new InvalidOperationException($"Invalid type for multiplication {type}"); - } } - private void GenerateBinaryDivision(NubType type) + private void GenerateFuncCall(FuncCall funcCall, LocalFuncDef func) { - if (type.Equals(NubType.Int64)) - { - _builder.AppendLine(" cqo"); - _builder.AppendLine(" idiv rcx"); - } - else if (type.Equals(NubType.Int32)) - { - _builder.AppendLine(" cdq"); - _builder.AppendLine(" idiv ecx"); - } - else if (type.Equals(NubType.Int16)) - { - _builder.AppendLine(" cwd"); - _builder.AppendLine(" idiv cx"); - } - else if (type.Equals(NubType.Int8)) - { - _builder.AppendLine(" cbw"); - _builder.AppendLine(" idiv cl"); - } - else - { - throw new InvalidOperationException($"Invalid type for division {type}"); - } } - private void GenerateIdentifier(IdentifierNode identifier, LocalFunc func) + private void GenerateSyscall(Syscall syscall, LocalFuncDef func) { - var variable = func.ResolveVariable(identifier.Identifier); - switch (variable) - { - case GlobalVariable globalVariable: - _builder.AppendLine($" mov rax, [{globalVariable.Identifier}]"); - break; - case LocalVariable localVariable: - { - _builder.AppendLine($" mov rax, [rbp - {localVariable.Offset}]"); - break; - } - default: - { - throw new ArgumentOutOfRangeException(nameof(variable)); - } - } - } - - private void GenerateLiteral(LiteralNode literal) - { - if (literal.Type.Equals(NubType.String)) - { - var label = _symbolTable.DefineString(literal.Literal); - _builder.AppendLine($" mov rax, {label}"); - } - else if (literal.Type.Equals(NubType.Int64) || literal.Type.Equals(NubType.Int32) || literal.Type.Equals(NubType.Int16) || literal.Type.Equals(NubType.Int8)) - { - _builder.AppendLine($" mov rax, {literal.Literal}"); - } - else if (literal.Type.Equals(NubType.Bool)) - { - _builder.AppendLine($" mov rax, {(bool.Parse(literal.Literal) ? "1" : "0")}"); - } - else - { - throw new NotImplementedException($"Literal type {literal.Type} not implemented"); - } - } - - private void GenerateStructInitializer(StructInitializerNode structInitializer, LocalFunc func) - { - var structDefinition = _definitions - .OfType() - .FirstOrDefault(sd => sd.Name == structInitializer.StructType.Name); - - if (structDefinition == null) - { - throw new Exception($"Struct {structInitializer.StructType} is not defined"); - } - - _builder.AppendLine($" mov rdi, {structDefinition.Members.Count * 8}"); - _builder.AppendLine(" call gc_alloc"); - _builder.AppendLine(" mov rcx, rax"); - - foreach (var initializer in structInitializer.Initializers) - { - _builder.AppendLine(" push rcx"); - GenerateExpression(initializer.Value, func); - var index = structDefinition.Members.FindIndex(sd => sd.Name == initializer.Key); - if (index == -1) - { - throw new Exception($"Member {initializer.Key} is not defined on struct {structInitializer.StructType}"); - } - - _builder.AppendLine(" pop rcx"); - _builder.AppendLine($" mov [rcx + {index * 8}], rax"); - } - - foreach (var uninitializedMember in structDefinition.Members.Where(m => !structInitializer.Initializers.ContainsKey(m.Name))) - { - if (!uninitializedMember.Value.HasValue) - { - throw new Exception($"Struct {structInitializer.StructType} must be initializer with member {uninitializedMember.Name}"); - } - - _builder.AppendLine(" push rcx"); - GenerateExpression(uninitializedMember.Value.Value, func); - var index = structDefinition.Members.IndexOf(uninitializedMember); - _builder.AppendLine(" pop rcx"); - _builder.AppendLine($" mov [rcx + {index * 8}], rax"); - } - - _builder.AppendLine(" mov rax, rcx"); - } - - private void GenerateFuncCall(FuncCall funcCall, LocalFunc func) - { - var symbol = _symbolTable.ResolveFunc(funcCall.Name, funcCall.Parameters.Select(p => p.Type).ToList()); - string[] registers = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"]; - - for (var i = funcCall.Parameters.Count - 1; i >= 0; i--) - { - GenerateExpression(funcCall.Parameters.ElementAt(i), func); - _builder.AppendLine(" push rax"); - } - - var registerParameters = Math.Min(registers.Length, funcCall.Parameters.Count); - var stackParameters = funcCall.Parameters.Count - registerParameters; - - for (var i = 0; i < registerParameters; i++) - { - _builder.AppendLine($" pop {registers[i]}"); - } - - _builder.AppendLine($" call {symbol.StartLabel}"); - if (stackParameters != 0) - { - _builder.AppendLine($" add rsp, {stackParameters}"); - } - } - - private void GenerateSyscall(Syscall syscall, LocalFunc func) - { - string[] registers = ["rax", "rdi", "rsi", "rdx", "r10", "r8", "r9"]; - - foreach (var parameter in syscall.Parameters) - { - GenerateExpression(parameter, func); - _builder.AppendLine(" push rax"); - } - - for (var i = syscall.Parameters.Count - 1; i >= 0; i--) - { - _builder.AppendLine($" pop {registers[i]}"); - } - - _builder.AppendLine(" syscall"); } } \ No newline at end of file diff --git a/lang/Nub.Lang/Backend/LabelFactory.cs b/lang/Nub.Lang/Backend/LabelFactory.cs deleted file mode 100644 index 495c15a..0000000 --- a/lang/Nub.Lang/Backend/LabelFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Nub.Lang.Backend; - -public class LabelFactory -{ - private int _index; - public string Create() => $"label{++_index}"; -} \ No newline at end of file diff --git a/lang/Nub.Lang/Backend/SymbolTable.cs b/lang/Nub.Lang/Backend/SymbolTable.cs index eef8668..550e6ef 100644 --- a/lang/Nub.Lang/Backend/SymbolTable.cs +++ b/lang/Nub.Lang/Backend/SymbolTable.cs @@ -4,195 +4,129 @@ namespace Nub.Lang.Backend; public class SymbolTable { - private readonly List _funcDefinitions = []; - private readonly List _globalVariables = []; - private readonly LabelFactory _labelFactory; - - public readonly Dictionary Strings = []; + public static SymbolTable Create(IEnumerable program) + { + var externFuncDefs = new List(); + var localFuncDefs = new List(); - public SymbolTable(LabelFactory labelFactory) - { - _labelFactory = labelFactory; - } - - public string DefineString(string value) - { - var label = _labelFactory.Create(); - Strings.Add(label, value); - return label; - } - - public void DefineGlobalVariable(GlobalVariableDefinitionNode globalVariableDefinition) - { - var identifier = _labelFactory.Create(); - _globalVariables.Add(new GlobalVariable(globalVariableDefinition.Name, globalVariableDefinition.Value.Type, identifier)); - } - - public void DefineFunc(ExternFuncDefinitionNode externFuncDefinition) - { - var existing = _funcDefinitions - .FirstOrDefault(f => f - .SignatureMatches - ( - externFuncDefinition.Name, - externFuncDefinition.Parameters.Select(p => p.Type).ToList() - )); - - if (existing != null) + foreach (var node in program) { - throw new Exception($"Func {existing} is already defined"); - } - - _funcDefinitions.Add(new ExternFunc(externFuncDefinition.Name, externFuncDefinition.Name, externFuncDefinition.Parameters, externFuncDefinition.ReturnType)); - } - - public void DefineFunc(LocalFuncDefinitionNode localFuncDefinition) - { - var existing = _funcDefinitions - .FirstOrDefault(f => f - .SignatureMatches - ( - localFuncDefinition.Name, - localFuncDefinition.Parameters.Select(p => p.Type).ToList() - )); - - if (existing != null) - { - throw new Exception($"Func {existing} is already defined"); - } - - var startLabel = _labelFactory.Create(); - var endLabel = _labelFactory.Create(); - _funcDefinitions.Add(new LocalFunc(localFuncDefinition.Name, startLabel, endLabel, localFuncDefinition.Parameters, localFuncDefinition.ReturnType, _globalVariables.Concat(ResolveFuncVariables(localFuncDefinition)).ToList())); - } - - private static List ResolveFuncVariables(LocalFuncDefinitionNode localFuncDefinition) - { - var offset = 0; - List variables = []; - - foreach (var parameter in localFuncDefinition.Parameters) - { - offset += 8; - variables.Add(new LocalVariable(parameter.Name, parameter.Type, offset)); - } - - ResolveBlockVariables(localFuncDefinition.Body, variables, offset); - - return variables; - } - - private static int ResolveBlockVariables(BlockNode block, List variables, int offset) - { - foreach (var statement in block.Statements) - { - switch (statement) + switch (node) { - case IfNode ifStatement: + case ExternFuncDefinitionNode externFuncDefinitionNode: { - offset = ResolveBlockVariables(ifStatement.Body, variables, offset); - if (ifStatement.Else.HasValue) + var parameters = externFuncDefinitionNode.Parameters.Select(parameter => new Variable(parameter.Name, parameter.Type)).ToList(); + externFuncDefs.Add(new ExternFuncDef { - ifStatement.Else.Value.Match - ( - elseIfStatement => offset = ResolveBlockVariables(elseIfStatement.Body, variables, offset), - elseStatement => offset = ResolveBlockVariables(elseStatement, variables, offset) - ); + Name = externFuncDefinitionNode.Name, + Parameters = parameters, + ReturnType = externFuncDefinitionNode.ReturnType + }); + break; + } + case LocalFuncDefinitionNode localFuncDefinitionNode: + { + var parameters = localFuncDefinitionNode.Parameters.Select(parameter => new Variable(parameter.Name, parameter.Type)).ToList(); + var localVariables = new List(); + + FindVariables(localFuncDefinitionNode.Body); + + localFuncDefs.Add(new LocalFuncDef + { + Name = localFuncDefinitionNode.Name, + Parameters = parameters, + LocalVariables = localVariables, + ReturnType = localFuncDefinitionNode.ReturnType + }); + break; + + void FindVariables(BlockNode blockNode) + { + foreach (var statement in blockNode.Statements) + { + switch (statement) + { + case IfNode ifNode: + { + FindVariables(ifNode.Body); + break; + } + case WhileNode whileNode: + { + FindVariables(whileNode.Body); + break; + } + case VariableAssignmentNode variableAssignmentNode: + { + localVariables.Add(new Variable(variableAssignmentNode.Name, variableAssignmentNode.Value.Type)); + break; + } + } + } } - break; } - case WhileNode whileStatement: + case StructDefinitionNode structDefinitionNode: { - offset = ResolveBlockVariables(whileStatement.Body, variables, offset); - break; + throw new NotImplementedException(); } - case VariableAssignmentNode variableAssignment: + default: { - offset += 8; - variables.Add(new LocalVariable(variableAssignment.Name, variableAssignment.Value.Type, offset)); - break; + throw new ArgumentOutOfRangeException(nameof(node)); } } } - return offset; - } - - public Func ResolveFunc(string name, List parameterTypes) + return new SymbolTable(externFuncDefs, localFuncDefs); + } + + private readonly List _externFuncDefs; + private readonly List _localFuncDefs; + + private SymbolTable(List externFuncDefs, List localFuncDefs) { - var func = _funcDefinitions.FirstOrDefault(f => f.SignatureMatches(name, parameterTypes)); - if (func == null) + _externFuncDefs = externFuncDefs; + _localFuncDefs = localFuncDefs; + } + + public FuncDef ResolveFunc(string name, List parameters) + { + var matching = _externFuncDefs.Concat(_localFuncDefs).Where(funcDef => funcDef.SignatureMatches(name, parameters)).ToArray(); + return matching.Length switch { - throw new Exception($"Func {name}({string.Join(", ", parameterTypes)}) is not defined"); + 0 => throw new Exception($"Could not resolve a func with signature {name}({string.Join(", ", parameters)})"), + > 1 => throw new Exception($"Multiple functions matches the signature {name}({string.Join(", ", parameters)})"), + _ => matching[0] + }; + } + + public LocalFuncDef ResolveLocalFunc(string name, List parameters) + { + var funcDef = ResolveFunc(name, parameters); + if (funcDef is LocalFuncDef localFuncDef) + { + return localFuncDef; } - return func; + throw new Exception($"Could not resolve a local func with signature {name}({string.Join(", ", parameters)})"); } - - public LocalFunc ResolveLocalFunc(string name, List parameterTypes) + + public ExternFuncDef ResolveExternFunc(string name, List parameters) { - var func = ResolveFunc(name, parameterTypes); - if (func is not LocalFunc localFunc) + var funcDef = ResolveFunc(name, parameters); + if (funcDef is ExternFuncDef externFuncDef) { - throw new Exception($"Func {func} is not a local func"); - } - return localFunc; - } - - public ExternFunc ResolveExternFunc(string name, List parameterTypes) - { - var func = ResolveFunc(name, parameterTypes); - if (func is not ExternFunc externFunc) - { - throw new Exception($"Func {func} is not an extern func"); - } - return externFunc; - } - - public GlobalVariable ResolveGlobalVariable(string name) - { - var variable = _globalVariables.FirstOrDefault(v => v.Name == name); - if (variable == null) - { - throw new Exception($"Global variable {name} is not defined"); + return externFuncDef; } - return variable; + throw new Exception($"Could not resolve a extern func with signature {name}({string.Join(", ", parameters)})"); } } -public abstract class Variable(string name, NubType type) +public abstract class FuncDef { - public string Name { get; } = name; - public NubType Type { get; } = type; - - public override string ToString() => $"{Name}: {Type}"; -} - -public class LocalVariable(string name, NubType type, int offset) : Variable(name, type) -{ - public int Offset { get; } = offset; -} - -public class GlobalVariable(string name, NubType type, string identifier) : Variable(name, type) -{ - public string Identifier { get; } = identifier; -} - -public abstract class Func -{ - protected Func(string name, string startLabel, List parameters, Optional returnType) - { - Name = name; - Parameters = parameters; - ReturnType = returnType; - StartLabel = startLabel; - } - - public string Name { get; } - public string StartLabel { get; } - public List Parameters { get; } - public Optional ReturnType { get; } + public required string Name { get; init; } + public required List Parameters { get; init; } + public required Optional ReturnType { get; init; } public bool SignatureMatches(string name, List parameterTypes) { @@ -206,53 +140,24 @@ public abstract class Func return true; } - - public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}"; } -public class ExternFunc : Func +public sealed class LocalFuncDef : FuncDef { - public ExternFunc(string name, string startLabel, List parameters, Optional returnType) : base(name, startLabel, parameters, returnType) + public required List LocalVariables { get; set; } + + public override string ToString() { + return $"func {Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}"; } } -public class LocalFunc : Func +public sealed class ExternFuncDef : FuncDef; + +public sealed class Variable(string name, NubType type) { - public LocalFunc(string name, string startLabel, string endLabel, List parameters, Optional returnType, List variables) : base(name, startLabel, parameters, returnType) - { - EndLabel = endLabel; - Variables = variables; - } + public string Name { get; } = name; + public NubType Type { get; } = type; - public string EndLabel { get; } - public List Variables { get; } - public int StackAllocation => Variables.OfType().Sum(variable => variable.Offset); - - public Variable ResolveVariable(string name) - { - var variable = Variables.FirstOrDefault(v => v.Name == name); - if (variable == null) - { - throw new Exception($"Variable {name} is not defined"); - } - - return variable; - } - - public LocalVariable ResolveLocalVariable(string name) - { - var variable = Variables.FirstOrDefault(v => v.Name == name); - if (variable == null) - { - throw new Exception($"Variable {name} is not defined"); - } - - if (variable is not LocalVariable localVariable) - { - throw new Exception($"Variable {name} is not a local variable"); - } - - return localVariable; - } + public override string ToString() => $"{Name}: {Type}"; } \ No newline at end of file diff --git a/lang/Nub.Lang/Frontend/Parsing/GlobalVariableDefinitionNode.cs b/lang/Nub.Lang/Frontend/Parsing/GlobalVariableDefinitionNode.cs deleted file mode 100644 index 04a69d2..0000000 --- a/lang/Nub.Lang/Frontend/Parsing/GlobalVariableDefinitionNode.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Nub.Lang.Frontend.Parsing; - -public class GlobalVariableDefinitionNode(string name, ExpressionNode value) : DefinitionNode -{ - public string Name { get; } = name; - public ExpressionNode Value { get; } = value; -} \ No newline at end of file diff --git a/lang/Nub.Lang/Frontend/Parsing/Parser.cs b/lang/Nub.Lang/Frontend/Parsing/Parser.cs index c564d8c..81d740f 100644 --- a/lang/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/lang/Nub.Lang/Frontend/Parsing/Parser.cs @@ -43,7 +43,6 @@ public class Parser var keyword = ExpectSymbol(); return keyword.Symbol switch { - Symbol.Let => ParseGlobalVariableDefinition(), Symbol.Func => ParseFuncDefinition(), Symbol.Extern => ParseExternFuncDefinition(), Symbol.Struct => ParseStruct(), @@ -51,16 +50,6 @@ public class Parser }; } - private GlobalVariableDefinitionNode ParseGlobalVariableDefinition() - { - var name = ExpectIdentifier(); - ExpectSymbol(Symbol.Assign); - var value = ParseExpression(); - ExpectSymbol(Symbol.Semicolon); - - return new GlobalVariableDefinitionNode(name.Value, value); - } - private LocalFuncDefinitionNode ParseFuncDefinition() { var name = ExpectIdentifier(); diff --git a/lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs b/lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs index c8b5f3a..0fe8d2c 100644 --- a/lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs +++ b/lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs @@ -13,7 +13,6 @@ public class Func(string name, List parameters, Optional _functions; - private readonly List _variableDefinitions; private readonly List _structDefinitions; private readonly Stack _variables; @@ -21,7 +20,6 @@ public class ExpressionTyper { _variables = new Stack(); _functions = []; - _variableDefinitions = []; _structDefinitions = definitions.OfType().ToList(); @@ -37,7 +35,6 @@ public class ExpressionTyper _functions.AddRange(functions); _functions.AddRange(externFunctions); - _variableDefinitions.AddRange(definitions.OfType()); } public void Populate() @@ -55,12 +52,6 @@ public class ExpressionTyper } } - foreach (var variable in _variableDefinitions) - { - PopulateExpression(variable.Value); - _variables.Push(new Variable(variable.Name, variable.Value.Type)); - } - foreach (var function in _functions) { foreach (var parameter in function.Parameters) From 6755342cdbed8e3763469d7748815f30ea7ba68f Mon Sep 17 00:00:00 2001 From: nub31 Date: Sun, 4 May 2025 19:30:06 +0200 Subject: [PATCH 04/32] Rename struct member to struct field --- lang/Nub.Lang/Frontend/Parsing/Parser.cs | 4 ++-- lang/Nub.Lang/Frontend/Parsing/StructDefinitionNode.cs | 4 ++-- lang/Nub.Lang/{StructMember.cs => StructField.cs} | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename lang/Nub.Lang/{StructMember.cs => StructField.cs} (70%) diff --git a/lang/Nub.Lang/Frontend/Parsing/Parser.cs b/lang/Nub.Lang/Frontend/Parsing/Parser.cs index 81d740f..f2a7faf 100644 --- a/lang/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/lang/Nub.Lang/Frontend/Parsing/Parser.cs @@ -107,7 +107,7 @@ public class Parser ExpectSymbol(Symbol.OpenBrace); - List variables = []; + List variables = []; while (!TryExpectSymbol(Symbol.CloseBrace)) { @@ -124,7 +124,7 @@ public class Parser ExpectSymbol(Symbol.Semicolon); - variables.Add(new StructMember(variableName, variableType, variableValue)); + variables.Add(new StructField(variableName, variableType, variableValue)); } return new StructDefinitionNode(name, variables); diff --git a/lang/Nub.Lang/Frontend/Parsing/StructDefinitionNode.cs b/lang/Nub.Lang/Frontend/Parsing/StructDefinitionNode.cs index d4eaada..cb7fd0a 100644 --- a/lang/Nub.Lang/Frontend/Parsing/StructDefinitionNode.cs +++ b/lang/Nub.Lang/Frontend/Parsing/StructDefinitionNode.cs @@ -1,7 +1,7 @@ namespace Nub.Lang.Frontend.Parsing; -public class StructDefinitionNode(string name, List members) : DefinitionNode +public class StructDefinitionNode(string name, List members) : DefinitionNode { public string Name { get; } = name; - public List Members { get; } = members; + public List Members { get; } = members; } \ No newline at end of file diff --git a/lang/Nub.Lang/StructMember.cs b/lang/Nub.Lang/StructField.cs similarity index 70% rename from lang/Nub.Lang/StructMember.cs rename to lang/Nub.Lang/StructField.cs index b3057c6..56d9946 100644 --- a/lang/Nub.Lang/StructMember.cs +++ b/lang/Nub.Lang/StructField.cs @@ -2,7 +2,7 @@ namespace Nub.Lang; -public class StructMember(string name, NubType type, Optional value) +public class StructField(string name, NubType type, Optional value) { public string Name { get; } = name; public NubType Type { get; } = type; From 2e7249fc87884f87d972e0e87cac124d16f6320d Mon Sep 17 00:00:00 2001 From: nub31 Date: Sun, 4 May 2025 20:52:24 +0200 Subject: [PATCH 05/32] Strings --- example/core/print.nub | 6 +- lang/Nub.Lang/Backend/Generator.cs | 45 ++++++--- lang/Nub.Lang/Backend/SymbolTable.cs | 97 +++++++++++++++++-- lang/Nub.Lang/Frontend/Parsing/Parser.cs | 10 -- lang/Nub.Lang/Frontend/Parsing/Syscall.cs | 6 -- .../Frontend/Parsing/SyscallExpressionNode.cs | 6 -- .../Frontend/Parsing/SyscallStatementNode.cs | 6 -- .../Frontend/Typing/ExpressionTyper.cs | 24 ----- lang/Nub.Lang/NubType.cs | 3 - lang/Nub.Lang/Program.cs | 3 +- 10 files changed, 123 insertions(+), 83 deletions(-) delete mode 100644 lang/Nub.Lang/Frontend/Parsing/Syscall.cs delete mode 100644 lang/Nub.Lang/Frontend/Parsing/SyscallExpressionNode.cs delete mode 100644 lang/Nub.Lang/Frontend/Parsing/SyscallStatementNode.cs diff --git a/example/core/print.nub b/example/core/print.nub index 4c559e8..735117c 100644 --- a/example/core/print.nub +++ b/example/core/print.nub @@ -1,9 +1,5 @@ -let SYS_WRITE = 1; -let STD_OUT = 1; -let STD_ERR = 2; - func print(msg: string) { - syscall(SYS_WRITE, STD_OUT, msg, str_len(msg)); + syscall(1, 1, msg, str_len(msg)); } func print(value: int64) { diff --git a/lang/Nub.Lang/Backend/Generator.cs b/lang/Nub.Lang/Backend/Generator.cs index 19c8cfd..ce9e900 100644 --- a/lang/Nub.Lang/Backend/Generator.cs +++ b/lang/Nub.Lang/Backend/Generator.cs @@ -17,11 +17,6 @@ public class Generator public string Generate() { - foreach (var externFuncDefinition in _definitions.OfType()) - { - GenerateExternFuncDefinition(externFuncDefinition); - } - foreach (var funcDefinition in _definitions.OfType()) { GenerateFuncDefinition(funcDefinition); @@ -30,12 +25,40 @@ public class Generator return _builder.ToString(); } - private void GenerateExternFuncDefinition(ExternFuncDefinitionNode externFuncDefinition) + private string QbeTypeName(NubType type) { + if (type.Equals(NubType.Int64)) + { + return "l"; + } + + if (type.Equals(NubType.Int32)) + { + return "w"; + } + + if (type.Equals(NubType.String)) + { + return "l"; + } + + throw new Exception($"Invalid qbe type {type}"); } private void GenerateFuncDefinition(LocalFuncDefinitionNode node) { + var parameters = node.Parameters.Select(p => $"{QbeTypeName(p.Type)} %{p.Name}"); + _builder.Append("function "); + if (node.ReturnType.HasValue) + { + _builder.Append($"{QbeTypeName(node.ReturnType.Value)} "); + } + + _builder.Append(node.Name); + _builder.AppendLine($"({string.Join(", ", parameters)}) {{"); + _builder.AppendLine("@start"); + GenerateBlock(node.Body, _symbolTable.ResolveLocalFunc(node.Name, node.Parameters.Select(x => x.Type).ToList())); + _builder.AppendLine("}"); } private void GenerateBlock(BlockNode block, LocalFuncDef func) @@ -65,9 +88,6 @@ public class Generator case ReturnNode @return: GenerateReturn(@return, func); break; - case SyscallStatementNode syscallStatement: - GenerateSyscall(syscallStatement.Syscall, func); - break; case VariableAssignmentNode variableAssignment: GenerateVariableAssignment(variableAssignment, func); break; @@ -132,9 +152,6 @@ public class Generator case StructMemberAccessorNode structMemberAccessor: GenerateStructMemberAccessor(structMemberAccessor, func); break; - case SyscallExpressionNode syscallExpression: - GenerateSyscall(syscallExpression.Syscall, func); - break; default: throw new ArgumentOutOfRangeException(nameof(expression)); } @@ -163,8 +180,4 @@ public class Generator private void GenerateFuncCall(FuncCall funcCall, LocalFuncDef func) { } - - private void GenerateSyscall(Syscall syscall, LocalFuncDef func) - { - } } \ No newline at end of file diff --git a/lang/Nub.Lang/Backend/SymbolTable.cs b/lang/Nub.Lang/Backend/SymbolTable.cs index 550e6ef..08fedea 100644 --- a/lang/Nub.Lang/Backend/SymbolTable.cs +++ b/lang/Nub.Lang/Backend/SymbolTable.cs @@ -9,6 +9,8 @@ public class SymbolTable var externFuncDefs = new List(); var localFuncDefs = new List(); + var strings = new List(); + foreach (var node in program) { switch (node) @@ -29,7 +31,7 @@ public class SymbolTable var parameters = localFuncDefinitionNode.Parameters.Select(parameter => new Variable(parameter.Name, parameter.Type)).ToList(); var localVariables = new List(); - FindVariables(localFuncDefinitionNode.Body); + Search(localFuncDefinitionNode.Body); localFuncDefs.Add(new LocalFuncDef { @@ -40,27 +42,97 @@ public class SymbolTable }); break; - void FindVariables(BlockNode blockNode) + void Search(BlockNode blockNode) { foreach (var statement in blockNode.Statements) { switch (statement) { + case FuncCallStatementNode funcCallStatementNode: + { + foreach (var parameter in funcCallStatementNode.FuncCall.Parameters) + { + FindStrings(parameter); + } + break; + } case IfNode ifNode: { - FindVariables(ifNode.Body); + SearchIf(ifNode); + break; + } + case ReturnNode returnNode: + { + if (returnNode.Value.HasValue) + { + FindStrings(returnNode.Value.Value); + } break; } case WhileNode whileNode: { - FindVariables(whileNode.Body); + FindStrings(whileNode.Condition); + Search(whileNode.Body); break; } case VariableAssignmentNode variableAssignmentNode: { + FindStrings(variableAssignmentNode.Value); localVariables.Add(new Variable(variableAssignmentNode.Name, variableAssignmentNode.Value.Type)); break; } + case VariableReassignmentNode variableReassignmentNode: + { + FindStrings(variableReassignmentNode.Value); + break; + } + } + } + } + + void SearchIf(IfNode ifNode) + { + FindStrings(ifNode.Condition); + Search(ifNode.Body); + if (ifNode.Else.HasValue) + { + ifNode.Else.Value.Match(SearchIf, Search); + } + } + + void FindStrings(ExpressionNode expressionNode) + { + switch (expressionNode) + { + case BinaryExpressionNode binaryExpressionNode: + { + FindStrings(binaryExpressionNode.Left); + FindStrings(binaryExpressionNode.Right); + break; + } + case FuncCallExpressionNode funcCallExpressionNode: + { + foreach (var parameter in funcCallExpressionNode.FuncCall.Parameters) + { + FindStrings(parameter); + } + break; + } + case LiteralNode literalNode: + { + if (literalNode.LiteralType.Equals(NubType.String)) + { + strings.Add(literalNode.Literal); + } + break; + } + case StructInitializerNode structInitializerNode: + { + foreach (var initializer in structInitializerNode.Initializers) + { + FindStrings(initializer.Value); + } + break; } } } @@ -76,18 +148,31 @@ public class SymbolTable } } - return new SymbolTable(externFuncDefs, localFuncDefs); + return new SymbolTable(strings, externFuncDefs, localFuncDefs); } + private readonly List _strings; private readonly List _externFuncDefs; private readonly List _localFuncDefs; - private SymbolTable(List externFuncDefs, List localFuncDefs) + private SymbolTable(List strings, List externFuncDefs, List localFuncDefs) { + _strings = strings; _externFuncDefs = externFuncDefs; _localFuncDefs = localFuncDefs; } + public int ResolveString(string value) + { + var index = _strings.IndexOf(value); + if (index == -1) + { + throw new Exception("String not found: " + value); + } + + return index; + } + public FuncDef ResolveFunc(string name, List parameters) { var matching = _externFuncDefs.Concat(_localFuncDefs).Where(funcDef => funcDef.SignatureMatches(name, parameters)).ToArray(); diff --git a/lang/Nub.Lang/Frontend/Parsing/Parser.cs b/lang/Nub.Lang/Frontend/Parsing/Parser.cs index f2a7faf..f6dcd87 100644 --- a/lang/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/lang/Nub.Lang/Frontend/Parsing/Parser.cs @@ -160,11 +160,6 @@ public class Parser ExpectSymbol(Symbol.Semicolon); - if (identifier.Value == "syscall") - { - return new SyscallStatementNode(new Syscall(parameters)); - } - return new FuncCallStatementNode(new FuncCall(identifier.Value, parameters)); } case Symbol.Assign: @@ -422,11 +417,6 @@ public class Parser TryExpectSymbol(Symbol.Comma); } - if (identifier.Value == "syscall") - { - return new SyscallExpressionNode(new Syscall(parameters)); - } - return new FuncCallExpressionNode(new FuncCall(identifier.Value, parameters)); } } diff --git a/lang/Nub.Lang/Frontend/Parsing/Syscall.cs b/lang/Nub.Lang/Frontend/Parsing/Syscall.cs deleted file mode 100644 index 7c0ab16..0000000 --- a/lang/Nub.Lang/Frontend/Parsing/Syscall.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Nub.Lang.Frontend.Parsing; - -public class Syscall(List parameters) -{ - public List Parameters { get; } = parameters; -} \ No newline at end of file diff --git a/lang/Nub.Lang/Frontend/Parsing/SyscallExpressionNode.cs b/lang/Nub.Lang/Frontend/Parsing/SyscallExpressionNode.cs deleted file mode 100644 index 0426ead..0000000 --- a/lang/Nub.Lang/Frontend/Parsing/SyscallExpressionNode.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Nub.Lang.Frontend.Parsing; - -public class SyscallExpressionNode(Syscall syscall) : ExpressionNode -{ - public Syscall Syscall { get; } = syscall; -} \ No newline at end of file diff --git a/lang/Nub.Lang/Frontend/Parsing/SyscallStatementNode.cs b/lang/Nub.Lang/Frontend/Parsing/SyscallStatementNode.cs deleted file mode 100644 index 48fedb1..0000000 --- a/lang/Nub.Lang/Frontend/Parsing/SyscallStatementNode.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Nub.Lang.Frontend.Parsing; - -public class SyscallStatementNode(Syscall syscall) : StatementNode -{ - public Syscall Syscall { get; } = syscall; -} \ No newline at end of file diff --git a/lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs b/lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs index 0fe8d2c..adc73c2 100644 --- a/lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs +++ b/lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs @@ -99,9 +99,6 @@ public class ExpressionTyper case ReturnNode returnNode: PopulateReturn(returnNode); break; - case SyscallStatementNode syscall: - PopulateSyscallStatement(syscall); - break; case VariableAssignmentNode variableAssignment: PopulateVariableAssignment(variableAssignment); break; @@ -138,14 +135,6 @@ public class ExpressionTyper } } - private void PopulateSyscallStatement(SyscallStatementNode syscall) - { - foreach (var parameter in syscall.Syscall.Parameters) - { - PopulateExpression(parameter); - } - } - private void PopulateReturn(ReturnNode returnNode) { if (returnNode.Value.HasValue) @@ -193,9 +182,6 @@ public class ExpressionTyper case StructMemberAccessorNode structMemberAccessor: GenerateStructMemberAccessorNode(structMemberAccessor); break; - case SyscallExpressionNode syscall: - PopulateSyscallExpression(syscall); - break; default: throw new ArgumentOutOfRangeException(nameof(expression)); } @@ -315,16 +301,6 @@ public class ExpressionTyper structMemberAccessor.Type = tmp.Type; } - private void PopulateSyscallExpression(SyscallExpressionNode syscall) - { - foreach (var parameter in syscall.Syscall.Parameters) - { - PopulateExpression(parameter); - } - - syscall.Type = new NubType("int64", []); - } - private class Variable(string name, NubType type) { public string Name { get; } = name; diff --git a/lang/Nub.Lang/NubType.cs b/lang/Nub.Lang/NubType.cs index 61b48cc..3ced0a5 100644 --- a/lang/Nub.Lang/NubType.cs +++ b/lang/Nub.Lang/NubType.cs @@ -13,11 +13,8 @@ public sealed class NubType public static NubType Int64 => new("int64", []); public static NubType Int32 => new("int32", []); - public static NubType Int16 => new("int16", []); - public static NubType Int8 => new("int8", []); public static NubType Bool => new("bool", []); public static NubType String => new("string", []); - public static NubType Array(NubType innerType) => new("array", [innerType]); public override bool Equals(object? obj) { diff --git a/lang/Nub.Lang/Program.cs b/lang/Nub.Lang/Program.cs index c43156e..88212a0 100644 --- a/lang/Nub.Lang/Program.cs +++ b/lang/Nub.Lang/Program.cs @@ -50,7 +50,8 @@ internal static class Program var generator = new Generator(definitions); var asm = generator.Generate(); - File.WriteAllText(output, asm); + // File.WriteAllText(output, asm); + Console.WriteLine(asm); return 0; } From b67d33455d1dc00131e1f097615e9ba921fab225 Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 5 May 2025 16:05:27 +0200 Subject: [PATCH 06/32] ... --- build.sh | 8 +- example/core/c.nub | 1 + example/core/print.nub | 34 --------- example/program.nub | 3 +- lang/Nub.Lang/Backend/Generator.cs | 110 ++++++++++++++++++++------- lang/Nub.Lang/Backend/SymbolTable.cs | 28 ++++++- lang/Nub.Lang/Program.cs | 3 +- std/baseline/runtime.asm | 9 +++ 8 files changed, 126 insertions(+), 70 deletions(-) create mode 100644 example/core/c.nub delete mode 100644 example/core/print.nub create mode 100644 std/baseline/runtime.asm diff --git a/build.sh b/build.sh index 5e62e94..6396cc8 100755 --- a/build.sh +++ b/build.sh @@ -1,14 +1,16 @@ #!/bin/sh mkdir -p out -dotnet run --project lang/Nub.Lang example out/out.asm +dotnet run --project lang/Nub.Lang example out/out.qbe gcc -c -g -fno-stack-protector -fno-builtin std/baseline/gc.c -o out/gc.o nasm -g -felf64 std/baseline/str_cmp.asm -o out/str_cmp.o +nasm -g -felf64 std/baseline/runtime.asm -o out/runtime.o nasm -g -felf64 std/core/str_len.asm -o out/str_len.o nasm -g -felf64 std/core/itoa.asm -o out/itoa.o -nasm -g -felf64 out/out.asm -o out/out.o +qbe out/out.qbe > out/out.s +gcc -c -g out/out.s -o out/out.o -gcc -no-pie -nostartfiles -o out/program out/gc.o out/str_cmp.o out/str_len.o out/itoa.o out/out.o +gcc -no-pie -nostartfiles -o out/program out/gc.o out/str_cmp.o out/str_len.o out/itoa.o out/runtime.o out/out.o diff --git a/example/core/c.nub b/example/core/c.nub new file mode 100644 index 0000000..a9d3cdb --- /dev/null +++ b/example/core/c.nub @@ -0,0 +1 @@ +extern func puts(str: string); \ No newline at end of file diff --git a/example/core/print.nub b/example/core/print.nub deleted file mode 100644 index 735117c..0000000 --- a/example/core/print.nub +++ /dev/null @@ -1,34 +0,0 @@ -func print(msg: string) { - syscall(1, 1, msg, str_len(msg)); -} - -func print(value: int64) { - print(itoa(value)); -} - -func print(value: bool) { - if value { - print("true"); - } else { - print("false"); - } -} - -func println() { - print("\n"); -} - -func println(msg: string) { - print(msg); - println(); -} - -func println(value: bool) { - print(value); - println(); -} - -func println(value: int64) { - print(value); - println(); -} diff --git a/example/program.nub b/example/program.nub index 52466a4..2172256 100644 --- a/example/program.nub +++ b/example/program.nub @@ -1,5 +1,6 @@ import "core"; func main() { - println("test"); + let x = "test"; + puts(x); } diff --git a/lang/Nub.Lang/Backend/Generator.cs b/lang/Nub.Lang/Backend/Generator.cs index ce9e900..a752279 100644 --- a/lang/Nub.Lang/Backend/Generator.cs +++ b/lang/Nub.Lang/Backend/Generator.cs @@ -8,6 +8,7 @@ public class Generator private readonly List _definitions; private readonly StringBuilder _builder = new(); private readonly SymbolTable _symbolTable; + private readonly Dictionary _variables = new(); public Generator(List definitions) { @@ -17,6 +18,12 @@ public class Generator public string Generate() { + for (var i = 0; i < _symbolTable.Strings.Count; i++) + { + var str = _symbolTable.Strings[i]; + _builder.AppendLine($"data $str{i} = {{ b \"{str}\", b 0 }}"); + } + foreach (var funcDefinition in _definitions.OfType()) { GenerateFuncDefinition(funcDefinition); @@ -27,37 +34,39 @@ public class Generator private string QbeTypeName(NubType type) { - if (type.Equals(NubType.Int64)) + if (type.Equals(NubType.Int64) || type.Equals(NubType.String)) { return "l"; } - if (type.Equals(NubType.Int32)) + if (type.Equals(NubType.Int32) || type.Equals(NubType.Bool)) { return "w"; } - - if (type.Equals(NubType.String)) - { - return "l"; - } throw new Exception($"Invalid qbe type {type}"); } private void GenerateFuncDefinition(LocalFuncDefinitionNode node) { + _variables.Clear(); var parameters = node.Parameters.Select(p => $"{QbeTypeName(p.Type)} %{p.Name}"); - _builder.Append("function "); + _builder.Append("export function "); if (node.ReturnType.HasValue) { _builder.Append($"{QbeTypeName(node.ReturnType.Value)} "); } + _builder.Append('$'); _builder.Append(node.Name); + _builder.AppendLine($"({string.Join(", ", parameters)}) {{"); _builder.AppendLine("@start"); GenerateBlock(node.Body, _symbolTable.ResolveLocalFunc(node.Name, node.Parameters.Select(x => x.Type).ToList())); + if (!node.ReturnType.HasValue) + { + _builder.AppendLine(" ret"); + } _builder.AppendLine("}"); } @@ -80,7 +89,7 @@ public class Generator GenerateContinue(); break; case FuncCallStatementNode funcCallStatement: - GenerateFuncCall(funcCallStatement.FuncCall, func); + GenerateStatementFuncCall(funcCallStatement, func); break; case IfNode ifStatement: GenerateIf(ifStatement, func); @@ -109,6 +118,19 @@ public class Generator private void GenerateContinue() { } + + private void GenerateStatementFuncCall(FuncCallStatementNode funcCall, LocalFuncDef func) + { + var results = new List<(string, NubType)>(); + foreach (var parameter in funcCall.FuncCall.Parameters) + { + results.Add((GenerateExpression(parameter, func), parameter.Type)); + } + + var parameters = results.Select(p => $"{QbeTypeName(p.Item2)} {p.Item1}"); + + _builder.AppendLine($" call ${funcCall.FuncCall.Name}({string.Join(", ", parameters)})"); + } private void GenerateIf(IfNode ifStatement, LocalFuncDef func) { @@ -116,68 +138,100 @@ public class Generator private void GenerateReturn(ReturnNode @return, LocalFuncDef func) { + if (@return.Value.HasValue) + { + var result = GenerateExpression(@return.Value.Value, func); + _builder.AppendLine($" ret {result}"); + } + else + { + _builder.AppendLine(" ret"); + } } private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment, LocalFuncDef func) { + _variables[variableAssignment.Name] = GenerateExpression(variableAssignment.Value, func); } private void GenerateVariableReassignment(VariableReassignmentNode variableReassignment, LocalFuncDef func) { + _variables[variableReassignment.Name] = GenerateExpression(variableReassignment.Value, func); } private void GenerateWhile(WhileNode whileStatement, LocalFuncDef func) { } - private void GenerateExpression(ExpressionNode expression, LocalFuncDef func) + private string GenerateExpression(ExpressionNode expression, LocalFuncDef func) { switch (expression) { case BinaryExpressionNode binaryExpression: - GenerateBinaryExpression(binaryExpression, func); - break; + return GenerateBinaryExpression(binaryExpression, func); case FuncCallExpressionNode funcCallExpression: - GenerateFuncCall(funcCallExpression.FuncCall, func); - break; + return GenerateExpressionFuncCall(funcCallExpression, func); case IdentifierNode identifier: - GenerateIdentifier(identifier, func); - break; + return GenerateIdentifier(identifier, func); case LiteralNode literal: - GenerateLiteral(literal, func); - break; + return GenerateLiteral(literal, func); case StructInitializerNode structInitializer: - GenerateStructInitializer(structInitializer, func); - break; + return GenerateStructInitializer(structInitializer, func); case StructMemberAccessorNode structMemberAccessor: - GenerateStructMemberAccessor(structMemberAccessor, func); - break; + return GenerateStructMemberAccessor(structMemberAccessor, func); default: throw new ArgumentOutOfRangeException(nameof(expression)); } } - private void GenerateStructMemberAccessor(StructMemberAccessorNode structMemberAccessor, LocalFuncDef func) + private string GenerateStructMemberAccessor(StructMemberAccessorNode structMemberAccessor, LocalFuncDef func) { + throw new NotImplementedException(); } - private void GenerateBinaryExpression(BinaryExpressionNode binaryExpression, LocalFuncDef func) + private string GenerateBinaryExpression(BinaryExpressionNode binaryExpression, LocalFuncDef func) { + throw new NotImplementedException(); } - private void GenerateIdentifier(IdentifierNode identifier, LocalFuncDef func) + private string GenerateIdentifier(IdentifierNode identifier, LocalFuncDef func) { + return _variables[identifier.Identifier]; } - private void GenerateLiteral(LiteralNode literal, LocalFuncDef func) + private string GenerateLiteral(LiteralNode literal, LocalFuncDef func) { + if (literal.LiteralType.Equals(NubType.String)) + { + return $"$str{_symbolTable.ResolveString(literal.Literal)}"; + } + else + { + throw new NotImplementedException(); + } } - private void GenerateStructInitializer(StructInitializerNode structInitializer, LocalFuncDef func) + private string GenerateStructInitializer(StructInitializerNode structInitializer, LocalFuncDef func) { + throw new NotImplementedException(); } - private void GenerateFuncCall(FuncCall funcCall, LocalFuncDef func) + private string GenerateExpressionFuncCall(FuncCallExpressionNode funcCall, LocalFuncDef func) { + var results = new List<(string, NubType)>(); + foreach (var parameter in funcCall.FuncCall.Parameters) + { + results.Add((GenerateExpression(parameter, func), parameter.Type)); + } + + var parameters = results.Select(p => $"{QbeTypeName(p.Item2)} {p.Item1}"); + + var output = GenName(); + _builder.AppendLine($" %{output} ={QbeTypeName(funcCall.Type)} call ${funcCall.FuncCall.Name}({string.Join(", ", parameters)})"); + + return $"%{output}"; } + + private int _nameIndex; + private string GenName() => "v" + ++_nameIndex; } \ No newline at end of file diff --git a/lang/Nub.Lang/Backend/SymbolTable.cs b/lang/Nub.Lang/Backend/SymbolTable.cs index 08fedea..48747bb 100644 --- a/lang/Nub.Lang/Backend/SymbolTable.cs +++ b/lang/Nub.Lang/Backend/SymbolTable.cs @@ -8,6 +8,7 @@ public class SymbolTable { var externFuncDefs = new List(); var localFuncDefs = new List(); + var localFuncIndex = 0; var strings = new List(); @@ -54,6 +55,7 @@ public class SymbolTable { FindStrings(parameter); } + break; } case IfNode ifNode: @@ -67,6 +69,7 @@ public class SymbolTable { FindStrings(returnNode.Value.Value); } + break; } case WhileNode whileNode: @@ -116,6 +119,7 @@ public class SymbolTable { FindStrings(parameter); } + break; } case LiteralNode literalNode: @@ -124,6 +128,7 @@ public class SymbolTable { strings.Add(literalNode.Literal); } + break; } case StructInitializerNode structInitializerNode: @@ -132,6 +137,7 @@ public class SymbolTable { FindStrings(initializer.Value); } + break; } } @@ -162,6 +168,8 @@ public class SymbolTable _localFuncDefs = localFuncDefs; } + public IReadOnlyList Strings => _strings; + public int ResolveString(string value) { var index = _strings.IndexOf(value); @@ -172,7 +180,7 @@ public class SymbolTable return index; } - + public FuncDef ResolveFunc(string name, List parameters) { var matching = _externFuncDefs.Concat(_localFuncDefs).Where(funcDef => funcDef.SignatureMatches(name, parameters)).ToArray(); @@ -213,6 +221,17 @@ public abstract class FuncDef public required List Parameters { get; init; } public required Optional ReturnType { get; init; } + public virtual Variable ResolveVariable(string name) + { + var parameter = Parameters.FirstOrDefault(p => p.Name == name); + if (parameter == null) + { + throw new Exception($"Unable to resolve variable {name}"); + } + + return parameter; + } + public bool SignatureMatches(string name, List parameterTypes) { if (Name != name) return false; @@ -229,7 +248,12 @@ public abstract class FuncDef public sealed class LocalFuncDef : FuncDef { - public required List LocalVariables { get; set; } + public required List LocalVariables { get; init; } + + public override Variable ResolveVariable(string name) + { + return LocalVariables.FirstOrDefault(p => p.Name == name) ?? base.ResolveVariable(name); + } public override string ToString() { diff --git a/lang/Nub.Lang/Program.cs b/lang/Nub.Lang/Program.cs index 88212a0..c43156e 100644 --- a/lang/Nub.Lang/Program.cs +++ b/lang/Nub.Lang/Program.cs @@ -50,8 +50,7 @@ internal static class Program var generator = new Generator(definitions); var asm = generator.Generate(); - // File.WriteAllText(output, asm); - Console.WriteLine(asm); + File.WriteAllText(output, asm); return 0; } diff --git a/std/baseline/runtime.asm b/std/baseline/runtime.asm new file mode 100644 index 0000000..de732f1 --- /dev/null +++ b/std/baseline/runtime.asm @@ -0,0 +1,9 @@ +global _start +extern main + +section .text +_start: + call main + mov rax, 60 + mov rdi, 0 + syscall \ No newline at end of file From 1f42a3f82dbc9638bc9377fdb782b30ac5f8d440 Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 5 May 2025 16:10:16 +0200 Subject: [PATCH 07/32] Remove symbol table --- lang/Nub.Lang/Backend/Generator.cs | 105 ++++++----- lang/Nub.Lang/Backend/SymbolTable.cs | 272 --------------------------- 2 files changed, 53 insertions(+), 324 deletions(-) delete mode 100644 lang/Nub.Lang/Backend/SymbolTable.cs diff --git a/lang/Nub.Lang/Backend/Generator.cs b/lang/Nub.Lang/Backend/Generator.cs index a752279..2453837 100644 --- a/lang/Nub.Lang/Backend/Generator.cs +++ b/lang/Nub.Lang/Backend/Generator.cs @@ -7,28 +7,27 @@ public class Generator { private readonly List _definitions; private readonly StringBuilder _builder = new(); - private readonly SymbolTable _symbolTable; private readonly Dictionary _variables = new(); - + private readonly List _strings = []; + public Generator(List definitions) { _definitions = definitions; - _symbolTable = SymbolTable.Create(definitions); } public string Generate() { - for (var i = 0; i < _symbolTable.Strings.Count; i++) - { - var str = _symbolTable.Strings[i]; - _builder.AppendLine($"data $str{i} = {{ b \"{str}\", b 0 }}"); - } - foreach (var funcDefinition in _definitions.OfType()) { GenerateFuncDefinition(funcDefinition); } + for (var i = 0; i < _strings.Count; i++) + { + var str = _strings[i]; + _builder.AppendLine($"data $str{i + 1} = {{ b \"{str}\", b 0 }}"); + } + return _builder.ToString(); } @@ -38,7 +37,7 @@ public class Generator { return "l"; } - + if (type.Equals(NubType.Int32) || type.Equals(NubType.Bool)) { return "w"; @@ -59,26 +58,27 @@ public class Generator _builder.Append('$'); _builder.Append(node.Name); - + _builder.AppendLine($"({string.Join(", ", parameters)}) {{"); _builder.AppendLine("@start"); - GenerateBlock(node.Body, _symbolTable.ResolveLocalFunc(node.Name, node.Parameters.Select(x => x.Type).ToList())); + GenerateBlock(node.Body); if (!node.ReturnType.HasValue) { _builder.AppendLine(" ret"); } + _builder.AppendLine("}"); } - - private void GenerateBlock(BlockNode block, LocalFuncDef func) + + private void GenerateBlock(BlockNode block) { foreach (var statement in block.Statements) { - GenerateStatement(statement, func); + GenerateStatement(statement); } } - private void GenerateStatement(StatementNode statement, LocalFuncDef func) + private void GenerateStatement(StatementNode statement) { switch (statement) { @@ -89,22 +89,22 @@ public class Generator GenerateContinue(); break; case FuncCallStatementNode funcCallStatement: - GenerateStatementFuncCall(funcCallStatement, func); + GenerateStatementFuncCall(funcCallStatement); break; case IfNode ifStatement: - GenerateIf(ifStatement, func); + GenerateIf(ifStatement); break; case ReturnNode @return: - GenerateReturn(@return, func); + GenerateReturn(@return); break; case VariableAssignmentNode variableAssignment: - GenerateVariableAssignment(variableAssignment, func); + GenerateVariableAssignment(variableAssignment); break; case VariableReassignmentNode variableReassignment: - GenerateVariableReassignment(variableReassignment, func); + GenerateVariableReassignment(variableReassignment); break; case WhileNode whileStatement: - GenerateWhile(whileStatement, func); + GenerateWhile(whileStatement); break; default: throw new ArgumentOutOfRangeException(nameof(statement)); @@ -114,33 +114,33 @@ public class Generator private void GenerateBreak() { } - + private void GenerateContinue() { } - - private void GenerateStatementFuncCall(FuncCallStatementNode funcCall, LocalFuncDef func) + + private void GenerateStatementFuncCall(FuncCallStatementNode funcCall) { var results = new List<(string, NubType)>(); foreach (var parameter in funcCall.FuncCall.Parameters) { - results.Add((GenerateExpression(parameter, func), parameter.Type)); + results.Add((GenerateExpression(parameter), parameter.Type)); } var parameters = results.Select(p => $"{QbeTypeName(p.Item2)} {p.Item1}"); - + _builder.AppendLine($" call ${funcCall.FuncCall.Name}({string.Join(", ", parameters)})"); } - private void GenerateIf(IfNode ifStatement, LocalFuncDef func) + private void GenerateIf(IfNode ifStatement) { } - private void GenerateReturn(ReturnNode @return, LocalFuncDef func) + private void GenerateReturn(ReturnNode @return) { if (@return.Value.HasValue) { - var result = GenerateExpression(@return.Value.Value, func); + var result = GenerateExpression(@return.Value.Value); _builder.AppendLine($" ret {result}"); } else @@ -149,61 +149,62 @@ public class Generator } } - private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment, LocalFuncDef func) + private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment) { - _variables[variableAssignment.Name] = GenerateExpression(variableAssignment.Value, func); + _variables[variableAssignment.Name] = GenerateExpression(variableAssignment.Value); } - private void GenerateVariableReassignment(VariableReassignmentNode variableReassignment, LocalFuncDef func) + private void GenerateVariableReassignment(VariableReassignmentNode variableReassignment) { - _variables[variableReassignment.Name] = GenerateExpression(variableReassignment.Value, func); + _variables[variableReassignment.Name] = GenerateExpression(variableReassignment.Value); } - private void GenerateWhile(WhileNode whileStatement, LocalFuncDef func) + private void GenerateWhile(WhileNode whileStatement) { } - private string GenerateExpression(ExpressionNode expression, LocalFuncDef func) + private string GenerateExpression(ExpressionNode expression) { switch (expression) { case BinaryExpressionNode binaryExpression: - return GenerateBinaryExpression(binaryExpression, func); + return GenerateBinaryExpression(binaryExpression); case FuncCallExpressionNode funcCallExpression: - return GenerateExpressionFuncCall(funcCallExpression, func); + return GenerateExpressionFuncCall(funcCallExpression); case IdentifierNode identifier: - return GenerateIdentifier(identifier, func); + return GenerateIdentifier(identifier); case LiteralNode literal: - return GenerateLiteral(literal, func); + return GenerateLiteral(literal); case StructInitializerNode structInitializer: - return GenerateStructInitializer(structInitializer, func); + return GenerateStructInitializer(structInitializer); case StructMemberAccessorNode structMemberAccessor: - return GenerateStructMemberAccessor(structMemberAccessor, func); + return GenerateStructMemberAccessor(structMemberAccessor); default: throw new ArgumentOutOfRangeException(nameof(expression)); } } - private string GenerateStructMemberAccessor(StructMemberAccessorNode structMemberAccessor, LocalFuncDef func) + private string GenerateStructMemberAccessor(StructMemberAccessorNode structMemberAccessor) { throw new NotImplementedException(); } - private string GenerateBinaryExpression(BinaryExpressionNode binaryExpression, LocalFuncDef func) + private string GenerateBinaryExpression(BinaryExpressionNode binaryExpression) { throw new NotImplementedException(); } - private string GenerateIdentifier(IdentifierNode identifier, LocalFuncDef func) + private string GenerateIdentifier(IdentifierNode identifier) { return _variables[identifier.Identifier]; } - private string GenerateLiteral(LiteralNode literal, LocalFuncDef func) + private string GenerateLiteral(LiteralNode literal) { if (literal.LiteralType.Equals(NubType.String)) { - return $"$str{_symbolTable.ResolveString(literal.Literal)}"; + _strings.Add(literal.Literal); + return $"$str{_strings.Count}"; } else { @@ -211,24 +212,24 @@ public class Generator } } - private string GenerateStructInitializer(StructInitializerNode structInitializer, LocalFuncDef func) + private string GenerateStructInitializer(StructInitializerNode structInitializer) { throw new NotImplementedException(); } - private string GenerateExpressionFuncCall(FuncCallExpressionNode funcCall, LocalFuncDef func) + private string GenerateExpressionFuncCall(FuncCallExpressionNode funcCall) { var results = new List<(string, NubType)>(); foreach (var parameter in funcCall.FuncCall.Parameters) { - results.Add((GenerateExpression(parameter, func), parameter.Type)); + results.Add((GenerateExpression(parameter), parameter.Type)); } var parameters = results.Select(p => $"{QbeTypeName(p.Item2)} {p.Item1}"); - + var output = GenName(); _builder.AppendLine($" %{output} ={QbeTypeName(funcCall.Type)} call ${funcCall.FuncCall.Name}({string.Join(", ", parameters)})"); - + return $"%{output}"; } diff --git a/lang/Nub.Lang/Backend/SymbolTable.cs b/lang/Nub.Lang/Backend/SymbolTable.cs deleted file mode 100644 index 48747bb..0000000 --- a/lang/Nub.Lang/Backend/SymbolTable.cs +++ /dev/null @@ -1,272 +0,0 @@ -using Nub.Lang.Frontend.Parsing; - -namespace Nub.Lang.Backend; - -public class SymbolTable -{ - public static SymbolTable Create(IEnumerable program) - { - var externFuncDefs = new List(); - var localFuncDefs = new List(); - var localFuncIndex = 0; - - var strings = new List(); - - foreach (var node in program) - { - switch (node) - { - case ExternFuncDefinitionNode externFuncDefinitionNode: - { - var parameters = externFuncDefinitionNode.Parameters.Select(parameter => new Variable(parameter.Name, parameter.Type)).ToList(); - externFuncDefs.Add(new ExternFuncDef - { - Name = externFuncDefinitionNode.Name, - Parameters = parameters, - ReturnType = externFuncDefinitionNode.ReturnType - }); - break; - } - case LocalFuncDefinitionNode localFuncDefinitionNode: - { - var parameters = localFuncDefinitionNode.Parameters.Select(parameter => new Variable(parameter.Name, parameter.Type)).ToList(); - var localVariables = new List(); - - Search(localFuncDefinitionNode.Body); - - localFuncDefs.Add(new LocalFuncDef - { - Name = localFuncDefinitionNode.Name, - Parameters = parameters, - LocalVariables = localVariables, - ReturnType = localFuncDefinitionNode.ReturnType - }); - break; - - void Search(BlockNode blockNode) - { - foreach (var statement in blockNode.Statements) - { - switch (statement) - { - case FuncCallStatementNode funcCallStatementNode: - { - foreach (var parameter in funcCallStatementNode.FuncCall.Parameters) - { - FindStrings(parameter); - } - - break; - } - case IfNode ifNode: - { - SearchIf(ifNode); - break; - } - case ReturnNode returnNode: - { - if (returnNode.Value.HasValue) - { - FindStrings(returnNode.Value.Value); - } - - break; - } - case WhileNode whileNode: - { - FindStrings(whileNode.Condition); - Search(whileNode.Body); - break; - } - case VariableAssignmentNode variableAssignmentNode: - { - FindStrings(variableAssignmentNode.Value); - localVariables.Add(new Variable(variableAssignmentNode.Name, variableAssignmentNode.Value.Type)); - break; - } - case VariableReassignmentNode variableReassignmentNode: - { - FindStrings(variableReassignmentNode.Value); - break; - } - } - } - } - - void SearchIf(IfNode ifNode) - { - FindStrings(ifNode.Condition); - Search(ifNode.Body); - if (ifNode.Else.HasValue) - { - ifNode.Else.Value.Match(SearchIf, Search); - } - } - - void FindStrings(ExpressionNode expressionNode) - { - switch (expressionNode) - { - case BinaryExpressionNode binaryExpressionNode: - { - FindStrings(binaryExpressionNode.Left); - FindStrings(binaryExpressionNode.Right); - break; - } - case FuncCallExpressionNode funcCallExpressionNode: - { - foreach (var parameter in funcCallExpressionNode.FuncCall.Parameters) - { - FindStrings(parameter); - } - - break; - } - case LiteralNode literalNode: - { - if (literalNode.LiteralType.Equals(NubType.String)) - { - strings.Add(literalNode.Literal); - } - - break; - } - case StructInitializerNode structInitializerNode: - { - foreach (var initializer in structInitializerNode.Initializers) - { - FindStrings(initializer.Value); - } - - break; - } - } - } - } - case StructDefinitionNode structDefinitionNode: - { - throw new NotImplementedException(); - } - default: - { - throw new ArgumentOutOfRangeException(nameof(node)); - } - } - } - - return new SymbolTable(strings, externFuncDefs, localFuncDefs); - } - - private readonly List _strings; - private readonly List _externFuncDefs; - private readonly List _localFuncDefs; - - private SymbolTable(List strings, List externFuncDefs, List localFuncDefs) - { - _strings = strings; - _externFuncDefs = externFuncDefs; - _localFuncDefs = localFuncDefs; - } - - public IReadOnlyList Strings => _strings; - - public int ResolveString(string value) - { - var index = _strings.IndexOf(value); - if (index == -1) - { - throw new Exception("String not found: " + value); - } - - return index; - } - - public FuncDef ResolveFunc(string name, List parameters) - { - var matching = _externFuncDefs.Concat(_localFuncDefs).Where(funcDef => funcDef.SignatureMatches(name, parameters)).ToArray(); - return matching.Length switch - { - 0 => throw new Exception($"Could not resolve a func with signature {name}({string.Join(", ", parameters)})"), - > 1 => throw new Exception($"Multiple functions matches the signature {name}({string.Join(", ", parameters)})"), - _ => matching[0] - }; - } - - public LocalFuncDef ResolveLocalFunc(string name, List parameters) - { - var funcDef = ResolveFunc(name, parameters); - if (funcDef is LocalFuncDef localFuncDef) - { - return localFuncDef; - } - - throw new Exception($"Could not resolve a local func with signature {name}({string.Join(", ", parameters)})"); - } - - public ExternFuncDef ResolveExternFunc(string name, List parameters) - { - var funcDef = ResolveFunc(name, parameters); - if (funcDef is ExternFuncDef externFuncDef) - { - return externFuncDef; - } - - throw new Exception($"Could not resolve a extern func with signature {name}({string.Join(", ", parameters)})"); - } -} - -public abstract class FuncDef -{ - public required string Name { get; init; } - public required List Parameters { get; init; } - public required Optional ReturnType { get; init; } - - public virtual Variable ResolveVariable(string name) - { - var parameter = Parameters.FirstOrDefault(p => p.Name == name); - if (parameter == null) - { - throw new Exception($"Unable to resolve variable {name}"); - } - - return parameter; - } - - public bool SignatureMatches(string name, List parameterTypes) - { - if (Name != name) return false; - if (Parameters.Count != parameterTypes.Count) return false; - - for (var i = 0; i < parameterTypes.Count; i++) - { - if (!Parameters[i].Type.Equals(parameterTypes[i])) return false; - } - - return true; - } -} - -public sealed class LocalFuncDef : FuncDef -{ - public required List LocalVariables { get; init; } - - public override Variable ResolveVariable(string name) - { - return LocalVariables.FirstOrDefault(p => p.Name == name) ?? base.ResolveVariable(name); - } - - public override string ToString() - { - return $"func {Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}"; - } -} - -public sealed class ExternFuncDef : FuncDef; - -public sealed class Variable(string name, NubType type) -{ - public string Name { get; } = name; - public NubType Type { get; } = type; - - public override string ToString() => $"{Name}: {Type}"; -} \ No newline at end of file From f77fdb86f388134904957683e0752f68d3cc8270 Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 5 May 2025 16:27:11 +0200 Subject: [PATCH 08/32] Add support for global functions --- example/program.nub | 2 +- lang/Nub.Lang/Backend/Generator.cs | 10 ++++++- lang/Nub.Lang/Frontend/Lexing/Lexer.cs | 1 + lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs | 1 + .../Parsing/LocalFuncDefinitionNode.cs | 3 +- lang/Nub.Lang/Frontend/Parsing/Parser.cs | 29 ++++++++++++++++++- std/baseline/runtime.asm | 3 +- 7 files changed, 44 insertions(+), 5 deletions(-) diff --git a/example/program.nub b/example/program.nub index 2172256..8dae7c2 100644 --- a/example/program.nub +++ b/example/program.nub @@ -1,6 +1,6 @@ import "core"; -func main() { +global func main() { let x = "test"; puts(x); } diff --git a/lang/Nub.Lang/Backend/Generator.cs b/lang/Nub.Lang/Backend/Generator.cs index 2453837..e4e5a17 100644 --- a/lang/Nub.Lang/Backend/Generator.cs +++ b/lang/Nub.Lang/Backend/Generator.cs @@ -50,7 +50,11 @@ public class Generator { _variables.Clear(); var parameters = node.Parameters.Select(p => $"{QbeTypeName(p.Type)} %{p.Name}"); - _builder.Append("export function "); + if (node.Global) + { + _builder.Append("export "); + } + _builder.Append("function "); if (node.ReturnType.HasValue) { _builder.Append($"{QbeTypeName(node.ReturnType.Value)} "); @@ -113,10 +117,12 @@ public class Generator private void GenerateBreak() { + throw new NotImplementedException(); } private void GenerateContinue() { + throw new NotImplementedException(); } private void GenerateStatementFuncCall(FuncCallStatementNode funcCall) @@ -134,6 +140,7 @@ public class Generator private void GenerateIf(IfNode ifStatement) { + throw new NotImplementedException(); } private void GenerateReturn(ReturnNode @return) @@ -161,6 +168,7 @@ public class Generator private void GenerateWhile(WhileNode whileStatement) { + throw new NotImplementedException(); } private string GenerateExpression(ExpressionNode expression) diff --git a/lang/Nub.Lang/Frontend/Lexing/Lexer.cs b/lang/Nub.Lang/Frontend/Lexing/Lexer.cs index 32e8bdd..a3def22 100644 --- a/lang/Nub.Lang/Frontend/Lexing/Lexer.cs +++ b/lang/Nub.Lang/Frontend/Lexing/Lexer.cs @@ -5,6 +5,7 @@ public class Lexer private static readonly Dictionary Keywords = new() { ["func"] = Symbol.Func, + ["global"] = Symbol.Global, ["extern"] = Symbol.Extern, ["import"] = Symbol.Import, ["let"] = Symbol.Let, diff --git a/lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs b/lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs index 123364a..a73c376 100644 --- a/lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs +++ b/lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs @@ -10,6 +10,7 @@ public enum Symbol Whitespace, Import, Extern, + Global, Func, Return, Let, diff --git a/lang/Nub.Lang/Frontend/Parsing/LocalFuncDefinitionNode.cs b/lang/Nub.Lang/Frontend/Parsing/LocalFuncDefinitionNode.cs index d1e0714..6734a02 100644 --- a/lang/Nub.Lang/Frontend/Parsing/LocalFuncDefinitionNode.cs +++ b/lang/Nub.Lang/Frontend/Parsing/LocalFuncDefinitionNode.cs @@ -1,11 +1,12 @@ namespace Nub.Lang.Frontend.Parsing; -public class LocalFuncDefinitionNode(string name, List parameters, BlockNode body, Optional returnType) : DefinitionNode +public class LocalFuncDefinitionNode(string name, List parameters, BlockNode body, Optional returnType, bool global) : DefinitionNode { public string Name { get; } = name; public List Parameters { get; } = parameters; public BlockNode Body { get; } = body; public Optional ReturnType { get; } = returnType; + public bool Global { get; } = global; public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}"; } \ No newline at end of file diff --git a/lang/Nub.Lang/Frontend/Parsing/Parser.cs b/lang/Nub.Lang/Frontend/Parsing/Parser.cs index f6dcd87..2fdf890 100644 --- a/lang/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/lang/Nub.Lang/Frontend/Parsing/Parser.cs @@ -44,6 +44,7 @@ public class Parser return keyword.Symbol switch { Symbol.Func => ParseFuncDefinition(), + Symbol.Global => ParseGlobalFuncDefinition(), Symbol.Extern => ParseExternFuncDefinition(), Symbol.Struct => ParseStruct(), _ => throw new Exception("Unexpected symbol: " + keyword.Symbol) @@ -72,7 +73,33 @@ public class Parser var body = ParseBlock(); - return new LocalFuncDefinitionNode(name.Value, parameters, body, returnType); + return new LocalFuncDefinitionNode(name.Value, parameters, body, returnType, false); + } + + private LocalFuncDefinitionNode ParseGlobalFuncDefinition() + { + ExpectSymbol(Symbol.Func); + var name = ExpectIdentifier(); + List parameters = []; + ExpectSymbol(Symbol.OpenParen); + if (!TryExpectSymbol(Symbol.CloseParen)) + { + while (!TryExpectSymbol(Symbol.CloseParen)) + { + parameters.Add(ParseFuncParameter()); + TryExpectSymbol(Symbol.Comma); + } + } + + var returnType = Optional.Empty(); + if (TryExpectSymbol(Symbol.Colon)) + { + returnType = ParseTypeInstance(); + } + + var body = ParseBlock(); + + return new LocalFuncDefinitionNode(name.Value, parameters, body, returnType, true); } private ExternFuncDefinitionNode ParseExternFuncDefinition() diff --git a/std/baseline/runtime.asm b/std/baseline/runtime.asm index de732f1..90e0941 100644 --- a/std/baseline/runtime.asm +++ b/std/baseline/runtime.asm @@ -1,8 +1,9 @@ global _start -extern main +extern main, gc_init section .text _start: + call gc_init call main mov rax, 60 mov rdi, 0 From cca6fc55d62876b3fb0ff417c94f1ee8d2b054a8 Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 5 May 2025 16:36:24 +0200 Subject: [PATCH 09/32] cleanup --- build.sh | 10 +++------- example/{core/c.nub => c/bindings.nub} | 0 example/core/string.nub | 2 -- example/program.nub | 2 +- std/baseline/runtime.asm | 10 ---------- std/core/itoa.asm | 23 ----------------------- std/core/str_len.asm | 13 ------------- std/{baseline => }/gc.c | 0 std/{baseline/str_cmp.asm => runtime.asm} | 15 +++++++++++++-- 9 files changed, 17 insertions(+), 58 deletions(-) rename example/{core/c.nub => c/bindings.nub} (100%) delete mode 100644 example/core/string.nub delete mode 100644 std/baseline/runtime.asm delete mode 100644 std/core/itoa.asm delete mode 100644 std/core/str_len.asm rename std/{baseline => }/gc.c (100%) rename std/{baseline/str_cmp.asm => runtime.asm} (53%) diff --git a/build.sh b/build.sh index 6396cc8..a96bd06 100755 --- a/build.sh +++ b/build.sh @@ -3,14 +3,10 @@ mkdir -p out dotnet run --project lang/Nub.Lang example out/out.qbe -gcc -c -g -fno-stack-protector -fno-builtin std/baseline/gc.c -o out/gc.o -nasm -g -felf64 std/baseline/str_cmp.asm -o out/str_cmp.o -nasm -g -felf64 std/baseline/runtime.asm -o out/runtime.o - -nasm -g -felf64 std/core/str_len.asm -o out/str_len.o -nasm -g -felf64 std/core/itoa.asm -o out/itoa.o +gcc -c -g -fno-stack-protector -fno-builtin std/gc.c -o out/gc.o +nasm -g -felf64 std/runtime.asm -o out/runtime.o qbe out/out.qbe > out/out.s gcc -c -g out/out.s -o out/out.o -gcc -no-pie -nostartfiles -o out/program out/gc.o out/str_cmp.o out/str_len.o out/itoa.o out/runtime.o out/out.o +gcc -no-pie -nostartfiles -o out/program out/gc.o out/runtime.o out/out.o diff --git a/example/core/c.nub b/example/c/bindings.nub similarity index 100% rename from example/core/c.nub rename to example/c/bindings.nub diff --git a/example/core/string.nub b/example/core/string.nub deleted file mode 100644 index 45b1051..0000000 --- a/example/core/string.nub +++ /dev/null @@ -1,2 +0,0 @@ -extern func str_len(msg: string): int64; -extern func itoa(value: int64): string; diff --git a/example/program.nub b/example/program.nub index 8dae7c2..5ef2046 100644 --- a/example/program.nub +++ b/example/program.nub @@ -1,4 +1,4 @@ -import "core"; +import "c"; global func main() { let x = "test"; diff --git a/std/baseline/runtime.asm b/std/baseline/runtime.asm deleted file mode 100644 index 90e0941..0000000 --- a/std/baseline/runtime.asm +++ /dev/null @@ -1,10 +0,0 @@ -global _start -extern main, gc_init - -section .text -_start: - call gc_init - call main - mov rax, 60 - mov rdi, 0 - syscall \ No newline at end of file diff --git a/std/core/itoa.asm b/std/core/itoa.asm deleted file mode 100644 index 51f0d9e..0000000 --- a/std/core/itoa.asm +++ /dev/null @@ -1,23 +0,0 @@ -section .bss - buffer resb 20 - -section .text - global itoa - -itoa: - mov rax, rdi - mov rsi, buffer + 19 - mov byte [rsi], 0 - dec rsi -.loop: - xor rdx, rdx - mov rcx, 10 - div rcx - add dl, '0' - mov [rsi], dl - dec rsi - test rax, rax - jnz .loop - inc rsi - mov rax, rsi - ret diff --git a/std/core/str_len.asm b/std/core/str_len.asm deleted file mode 100644 index ea7fc00..0000000 --- a/std/core/str_len.asm +++ /dev/null @@ -1,13 +0,0 @@ -global str_len - -section .text -str_len: - xor rax, rax -.loop: - cmp byte [rdi], 0 - jz .done - inc rax - inc rdi - jmp .loop -.done: - ret \ No newline at end of file diff --git a/std/baseline/gc.c b/std/gc.c similarity index 100% rename from std/baseline/gc.c rename to std/gc.c diff --git a/std/baseline/str_cmp.asm b/std/runtime.asm similarity index 53% rename from std/baseline/str_cmp.asm rename to std/runtime.asm index 4da1302..f4c6102 100644 --- a/std/baseline/str_cmp.asm +++ b/std/runtime.asm @@ -1,7 +1,18 @@ -global str_cmp +global _start +extern main, gc_init section .text -str_cmp: +_start: + call gc_init + call main + mov rax, 60 + mov rdi, 0 + syscall + +global base_str_cmp + +section .text +base_str_cmp: xor rdx, rdx .loop: mov al, [rsi + rdx] From e5b7982d37bfd651091feb4085f7348f415e4199 Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 5 May 2025 16:38:41 +0200 Subject: [PATCH 10/32] rename strcmp --- std/runtime.asm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/std/runtime.asm b/std/runtime.asm index f4c6102..706777d 100644 --- a/std/runtime.asm +++ b/std/runtime.asm @@ -9,10 +9,9 @@ _start: mov rdi, 0 syscall -global base_str_cmp - +global nub_strcmp section .text -base_str_cmp: +nub_strcmp: xor rdx, rdx .loop: mov al, [rsi + rdx] From bfaad0950bc0f9855d2ecd7a9d9b3356c46dacca Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 5 May 2025 19:26:23 +0200 Subject: [PATCH 11/32] more renaming --- build.sh | 21 ++++++++++++++++--- {lang => src/compiler}/.gitignore | 0 .../.idea/.idea.Nub.Lang/.idea/.gitignore | 0 .../.idea/.idea.Nub.Lang/.idea/.name | 0 .../.idea/codeStyles/codeStyleConfig.xml | 0 .../.idea/.idea.Nub.Lang/.idea/encodings.xml | 0 .../.idea.Nub.Lang/.idea/indexLayout.xml | 4 ++-- .../.idea/.idea.Nub.Lang/.idea/vcs.xml | 1 + {lang => src/compiler}/Nub.Lang.sln | 0 .../compiler}/Nub.Lang/Backend/Generator.cs | 0 .../Frontend/Lexing/IdentifierToken.cs | 0 .../Nub.Lang/Frontend/Lexing/Lexer.cs | 0 .../Nub.Lang/Frontend/Lexing/LiteralToken.cs | 0 .../Nub.Lang/Frontend/Lexing/SymbolToken.cs | 0 .../Nub.Lang/Frontend/Lexing/Token.cs | 0 .../Frontend/Parsing/BinaryExpressionNode.cs | 0 .../Nub.Lang/Frontend/Parsing/BlockNode.cs | 0 .../Nub.Lang/Frontend/Parsing/BreakNode.cs | 0 .../Nub.Lang/Frontend/Parsing/ContinueNode.cs | 0 .../Frontend/Parsing/DefinitionNode.cs | 0 .../Frontend/Parsing/ExpressionNode.cs | 0 .../Parsing/ExternFuncDefinitionNode.cs | 0 .../Nub.Lang/Frontend/Parsing/FuncCall.cs | 0 .../Parsing/FuncCallExpressionNode.cs | 0 .../Frontend/Parsing/FuncCallStatementNode.cs | 0 .../Frontend/Parsing/IdentifierNode.cs | 0 .../Nub.Lang/Frontend/Parsing/IfNode.cs | 0 .../Nub.Lang/Frontend/Parsing/LiteralNode.cs | 0 .../Parsing/LocalFuncDefinitionNode.cs | 0 .../Nub.Lang/Frontend/Parsing/ModuleNode.cs | 0 .../Nub.Lang/Frontend/Parsing/Node.cs | 0 .../Nub.Lang/Frontend/Parsing/Parser.cs | 0 .../Nub.Lang/Frontend/Parsing/ReturnNode.cs | 0 .../Frontend/Parsing/StatementNode.cs | 0 .../Frontend/Parsing/StructDefinitionNode.cs | 0 .../Frontend/Parsing/StructInitializerNode.cs | 0 .../Parsing/StructMemberAccessorNode.cs | 0 .../Parsing/VariableAssignmentNode.cs | 0 .../Parsing/VariableReassignmentNode.cs | 0 .../Nub.Lang/Frontend/Parsing/WhileNode.cs | 0 .../Frontend/Typing/ExpressionTyper.cs | 0 .../compiler}/Nub.Lang/FuncParameter.cs | 0 .../compiler}/Nub.Lang/Nub.Lang.csproj | 2 ++ {lang => src/compiler}/Nub.Lang/NubType.cs | 0 {lang => src/compiler}/Nub.Lang/Optional.cs | 0 {lang => src/compiler}/Nub.Lang/Program.cs | 4 ++-- .../compiler}/Nub.Lang/StructField.cs | 0 {lang => src/compiler}/Nub.Lang/Variant.cs | 0 {std => src/runtime}/gc.c | 0 {std => src/runtime}/runtime.asm | 0 50 files changed, 25 insertions(+), 7 deletions(-) rename {lang => src/compiler}/.gitignore (100%) rename {lang => src/compiler}/.idea/.idea.Nub.Lang/.idea/.gitignore (100%) rename {lang => src/compiler}/.idea/.idea.Nub.Lang/.idea/.name (100%) rename {lang => src/compiler}/.idea/.idea.Nub.Lang/.idea/codeStyles/codeStyleConfig.xml (100%) rename {lang => src/compiler}/.idea/.idea.Nub.Lang/.idea/encodings.xml (100%) rename {lang => src/compiler}/.idea/.idea.Nub.Lang/.idea/indexLayout.xml (74%) rename {lang => src/compiler}/.idea/.idea.Nub.Lang/.idea/vcs.xml (71%) rename {lang => src/compiler}/Nub.Lang.sln (100%) rename {lang => src/compiler}/Nub.Lang/Backend/Generator.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Lexing/IdentifierToken.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Lexing/Lexer.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Lexing/LiteralToken.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Lexing/SymbolToken.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Lexing/Token.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/BinaryExpressionNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/BlockNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/BreakNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/ContinueNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/DefinitionNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/ExpressionNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/ExternFuncDefinitionNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/FuncCall.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/FuncCallExpressionNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/FuncCallStatementNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/IdentifierNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/IfNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/LiteralNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/LocalFuncDefinitionNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/ModuleNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/Node.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/Parser.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/ReturnNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/StatementNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/StructDefinitionNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/StructInitializerNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/StructMemberAccessorNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/VariableAssignmentNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/VariableReassignmentNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Parsing/WhileNode.cs (100%) rename {lang => src/compiler}/Nub.Lang/Frontend/Typing/ExpressionTyper.cs (100%) rename {lang => src/compiler}/Nub.Lang/FuncParameter.cs (100%) rename {lang => src/compiler}/Nub.Lang/Nub.Lang.csproj (76%) rename {lang => src/compiler}/Nub.Lang/NubType.cs (100%) rename {lang => src/compiler}/Nub.Lang/Optional.cs (100%) rename {lang => src/compiler}/Nub.Lang/Program.cs (96%) rename {lang => src/compiler}/Nub.Lang/StructField.cs (100%) rename {lang => src/compiler}/Nub.Lang/Variant.cs (100%) rename {std => src/runtime}/gc.c (100%) rename {std => src/runtime}/runtime.asm (100%) diff --git a/build.sh b/build.sh index a96bd06..329b3b7 100755 --- a/build.sh +++ b/build.sh @@ -1,12 +1,27 @@ #!/bin/sh mkdir -p out -dotnet run --project lang/Nub.Lang example out/out.qbe +echo "setup..." -gcc -c -g -fno-stack-protector -fno-builtin std/gc.c -o out/gc.o -nasm -g -felf64 std/runtime.asm -o out/runtime.o +dotnet publish -c Release src/compiler/Nub.Lang > /dev/null + +if [[ $? -ne 0 ]] ; then + printf "\x1b[31mYour compiler is not compiling!\x1b[0m\n" + exit 1 +fi + +set -e + +echo "compiling..." + +nub example out/out.qbe + +gcc -c -g -fno-stack-protector -fno-builtin src/runtime/gc.c -o out/gc.o +nasm -g -felf64 src/runtime/runtime.asm -o out/runtime.o qbe out/out.qbe > out/out.s gcc -c -g out/out.s -o out/out.o gcc -no-pie -nostartfiles -o out/program out/gc.o out/runtime.o out/out.o + +echo "done..." diff --git a/lang/.gitignore b/src/compiler/.gitignore similarity index 100% rename from lang/.gitignore rename to src/compiler/.gitignore diff --git a/lang/.idea/.idea.Nub.Lang/.idea/.gitignore b/src/compiler/.idea/.idea.Nub.Lang/.idea/.gitignore similarity index 100% rename from lang/.idea/.idea.Nub.Lang/.idea/.gitignore rename to src/compiler/.idea/.idea.Nub.Lang/.idea/.gitignore diff --git a/lang/.idea/.idea.Nub.Lang/.idea/.name b/src/compiler/.idea/.idea.Nub.Lang/.idea/.name similarity index 100% rename from lang/.idea/.idea.Nub.Lang/.idea/.name rename to src/compiler/.idea/.idea.Nub.Lang/.idea/.name diff --git a/lang/.idea/.idea.Nub.Lang/.idea/codeStyles/codeStyleConfig.xml b/src/compiler/.idea/.idea.Nub.Lang/.idea/codeStyles/codeStyleConfig.xml similarity index 100% rename from lang/.idea/.idea.Nub.Lang/.idea/codeStyles/codeStyleConfig.xml rename to src/compiler/.idea/.idea.Nub.Lang/.idea/codeStyles/codeStyleConfig.xml diff --git a/lang/.idea/.idea.Nub.Lang/.idea/encodings.xml b/src/compiler/.idea/.idea.Nub.Lang/.idea/encodings.xml similarity index 100% rename from lang/.idea/.idea.Nub.Lang/.idea/encodings.xml rename to src/compiler/.idea/.idea.Nub.Lang/.idea/encodings.xml diff --git a/lang/.idea/.idea.Nub.Lang/.idea/indexLayout.xml b/src/compiler/.idea/.idea.Nub.Lang/.idea/indexLayout.xml similarity index 74% rename from lang/.idea/.idea.Nub.Lang/.idea/indexLayout.xml rename to src/compiler/.idea/.idea.Nub.Lang/.idea/indexLayout.xml index d39e765..4a53b21 100644 --- a/lang/.idea/.idea.Nub.Lang/.idea/indexLayout.xml +++ b/src/compiler/.idea/.idea.Nub.Lang/.idea/indexLayout.xml @@ -2,8 +2,8 @@ - ../example - ../std + ../../example + ../core diff --git a/lang/.idea/.idea.Nub.Lang/.idea/vcs.xml b/src/compiler/.idea/.idea.Nub.Lang/.idea/vcs.xml similarity index 71% rename from lang/.idea/.idea.Nub.Lang/.idea/vcs.xml rename to src/compiler/.idea/.idea.Nub.Lang/.idea/vcs.xml index 6c0b863..64713b8 100644 --- a/lang/.idea/.idea.Nub.Lang/.idea/vcs.xml +++ b/src/compiler/.idea/.idea.Nub.Lang/.idea/vcs.xml @@ -1,6 +1,7 @@ + \ No newline at end of file diff --git a/lang/Nub.Lang.sln b/src/compiler/Nub.Lang.sln similarity index 100% rename from lang/Nub.Lang.sln rename to src/compiler/Nub.Lang.sln diff --git a/lang/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs similarity index 100% rename from lang/Nub.Lang/Backend/Generator.cs rename to src/compiler/Nub.Lang/Backend/Generator.cs diff --git a/lang/Nub.Lang/Frontend/Lexing/IdentifierToken.cs b/src/compiler/Nub.Lang/Frontend/Lexing/IdentifierToken.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Lexing/IdentifierToken.cs rename to src/compiler/Nub.Lang/Frontend/Lexing/IdentifierToken.cs diff --git a/lang/Nub.Lang/Frontend/Lexing/Lexer.cs b/src/compiler/Nub.Lang/Frontend/Lexing/Lexer.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Lexing/Lexer.cs rename to src/compiler/Nub.Lang/Frontend/Lexing/Lexer.cs diff --git a/lang/Nub.Lang/Frontend/Lexing/LiteralToken.cs b/src/compiler/Nub.Lang/Frontend/Lexing/LiteralToken.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Lexing/LiteralToken.cs rename to src/compiler/Nub.Lang/Frontend/Lexing/LiteralToken.cs diff --git a/lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs b/src/compiler/Nub.Lang/Frontend/Lexing/SymbolToken.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs rename to src/compiler/Nub.Lang/Frontend/Lexing/SymbolToken.cs diff --git a/lang/Nub.Lang/Frontend/Lexing/Token.cs b/src/compiler/Nub.Lang/Frontend/Lexing/Token.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Lexing/Token.cs rename to src/compiler/Nub.Lang/Frontend/Lexing/Token.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/BinaryExpressionNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/BinaryExpressionNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/BinaryExpressionNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/BinaryExpressionNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/BlockNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/BlockNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/BlockNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/BlockNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/BreakNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/BreakNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/BreakNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/BreakNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/ContinueNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/ContinueNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/ContinueNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/ContinueNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/DefinitionNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/DefinitionNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/DefinitionNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/DefinitionNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/ExpressionNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/ExpressionNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/ExpressionNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/ExpressionNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/ExternFuncDefinitionNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/ExternFuncDefinitionNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/ExternFuncDefinitionNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/ExternFuncDefinitionNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/FuncCall.cs b/src/compiler/Nub.Lang/Frontend/Parsing/FuncCall.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/FuncCall.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/FuncCall.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/FuncCallExpressionNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/FuncCallExpressionNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/FuncCallExpressionNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/FuncCallExpressionNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/FuncCallStatementNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/FuncCallStatementNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/FuncCallStatementNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/FuncCallStatementNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/IdentifierNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/IdentifierNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/IdentifierNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/IdentifierNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/IfNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/IfNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/IfNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/IfNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/LiteralNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/LiteralNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/LiteralNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/LiteralNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/LocalFuncDefinitionNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/LocalFuncDefinitionNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/LocalFuncDefinitionNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/LocalFuncDefinitionNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/ModuleNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/ModuleNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/ModuleNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/ModuleNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/Node.cs b/src/compiler/Nub.Lang/Frontend/Parsing/Node.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/Node.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/Node.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/Parser.cs b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/Parser.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/ReturnNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/ReturnNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/ReturnNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/ReturnNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/StatementNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/StatementNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/StatementNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/StatementNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/StructDefinitionNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/StructDefinitionNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/StructDefinitionNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/StructDefinitionNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/StructInitializerNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/StructInitializerNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/StructInitializerNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/StructInitializerNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/StructMemberAccessorNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/StructMemberAccessorNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/StructMemberAccessorNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/StructMemberAccessorNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/VariableAssignmentNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/VariableAssignmentNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/VariableAssignmentNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/VariableAssignmentNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/VariableReassignmentNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/VariableReassignmentNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/VariableReassignmentNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/VariableReassignmentNode.cs diff --git a/lang/Nub.Lang/Frontend/Parsing/WhileNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/WhileNode.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Parsing/WhileNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/WhileNode.cs diff --git a/lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs b/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs similarity index 100% rename from lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs rename to src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs diff --git a/lang/Nub.Lang/FuncParameter.cs b/src/compiler/Nub.Lang/FuncParameter.cs similarity index 100% rename from lang/Nub.Lang/FuncParameter.cs rename to src/compiler/Nub.Lang/FuncParameter.cs diff --git a/lang/Nub.Lang/Nub.Lang.csproj b/src/compiler/Nub.Lang/Nub.Lang.csproj similarity index 76% rename from lang/Nub.Lang/Nub.Lang.csproj rename to src/compiler/Nub.Lang/Nub.Lang.csproj index 85b4959..ee51912 100644 --- a/lang/Nub.Lang/Nub.Lang.csproj +++ b/src/compiler/Nub.Lang/Nub.Lang.csproj @@ -1,10 +1,12 @@  + nub Exe net9.0 enable enable + true diff --git a/lang/Nub.Lang/NubType.cs b/src/compiler/Nub.Lang/NubType.cs similarity index 100% rename from lang/Nub.Lang/NubType.cs rename to src/compiler/Nub.Lang/NubType.cs diff --git a/lang/Nub.Lang/Optional.cs b/src/compiler/Nub.Lang/Optional.cs similarity index 100% rename from lang/Nub.Lang/Optional.cs rename to src/compiler/Nub.Lang/Optional.cs diff --git a/lang/Nub.Lang/Program.cs b/src/compiler/Nub.Lang/Program.cs similarity index 96% rename from lang/Nub.Lang/Program.cs rename to src/compiler/Nub.Lang/Program.cs index c43156e..955028a 100644 --- a/lang/Nub.Lang/Program.cs +++ b/src/compiler/Nub.Lang/Program.cs @@ -48,9 +48,9 @@ internal static class Program typer.Populate(); var generator = new Generator(definitions); - var asm = generator.Generate(); + var result = generator.Generate(); - File.WriteAllText(output, asm); + File.WriteAllText(output, result); return 0; } diff --git a/lang/Nub.Lang/StructField.cs b/src/compiler/Nub.Lang/StructField.cs similarity index 100% rename from lang/Nub.Lang/StructField.cs rename to src/compiler/Nub.Lang/StructField.cs diff --git a/lang/Nub.Lang/Variant.cs b/src/compiler/Nub.Lang/Variant.cs similarity index 100% rename from lang/Nub.Lang/Variant.cs rename to src/compiler/Nub.Lang/Variant.cs diff --git a/std/gc.c b/src/runtime/gc.c similarity index 100% rename from std/gc.c rename to src/runtime/gc.c diff --git a/std/runtime.asm b/src/runtime/runtime.asm similarity index 100% rename from std/runtime.asm rename to src/runtime/runtime.asm From df6e926bae385cda80adbde0fee713ee11704cc6 Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 5 May 2025 22:15:08 +0200 Subject: [PATCH 12/32] break/continue working --- build.sh | 2 +- example/program.nub | 5 +- src/compiler/Nub.Lang/Backend/Generator.cs | 66 +++++++++++++++++++--- src/runtime/runtime.asm | 1 + 4 files changed, 64 insertions(+), 10 deletions(-) diff --git a/build.sh b/build.sh index 329b3b7..f5dbbfb 100755 --- a/build.sh +++ b/build.sh @@ -22,6 +22,6 @@ nasm -g -felf64 src/runtime/runtime.asm -o out/runtime.o qbe out/out.qbe > out/out.s gcc -c -g out/out.s -o out/out.o -gcc -no-pie -nostartfiles -o out/program out/gc.o out/runtime.o out/out.o +gcc -nostartfiles -o out/program out/gc.o out/runtime.o out/out.o echo "done..." diff --git a/example/program.nub b/example/program.nub index 5ef2046..82f0a47 100644 --- a/example/program.nub +++ b/example/program.nub @@ -1,6 +1,7 @@ import "c"; global func main() { - let x = "test"; - puts(x); + while true { + continue; + } } diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index e4e5a17..e4af7e7 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -7,8 +7,11 @@ public class Generator { private readonly List _definitions; private readonly StringBuilder _builder = new(); + private readonly Dictionary _prefixIndexes = new(); private readonly Dictionary _variables = new(); private readonly List _strings = []; + private readonly Stack _breakLabels = new(); + private readonly Stack _continueLabels = new(); public Generator(List definitions) { @@ -54,6 +57,7 @@ public class Generator { _builder.Append("export "); } + _builder.Append("function "); if (node.ReturnType.HasValue) { @@ -117,12 +121,12 @@ public class Generator private void GenerateBreak() { - throw new NotImplementedException(); + _builder.AppendLine($" jmp @{_breakLabels.Peek()}"); } private void GenerateContinue() { - throw new NotImplementedException(); + _builder.AppendLine($" jmp @{_continueLabels.Peek()}"); } private void GenerateStatementFuncCall(FuncCallStatementNode funcCall) @@ -140,7 +144,25 @@ public class Generator private void GenerateIf(IfNode ifStatement) { - throw new NotImplementedException(); + var trueLabel = GenName("if_true"); + var endLabel = GenName("if_end"); + var falseLabel = GenName("if_false"); + + var result = GenerateExpression(ifStatement.Condition); + _builder.AppendLine($" jnz {result}, @{trueLabel}, @{falseLabel}"); + _builder.AppendLine($"@{trueLabel}"); + GenerateBlock(ifStatement.Body); + _builder.AppendLine($" jmp @{endLabel}"); + _builder.AppendLine($"@{falseLabel}"); + if (ifStatement.Else.HasValue) + { + ifStatement.Else.Value.Match + ( + GenerateIf, + GenerateBlock + ); + } + _builder.AppendLine($"@{endLabel}"); } private void GenerateReturn(ReturnNode @return) @@ -168,7 +190,23 @@ public class Generator private void GenerateWhile(WhileNode whileStatement) { - throw new NotImplementedException(); + var conditionLabel = GenName("loop_condition"); + var iterationLabel = GenName("loop_iteration"); + var endLabel = GenName("loop_end"); + + _breakLabels.Push(endLabel); + _continueLabels.Push(conditionLabel); + + _builder.AppendLine($" jmp @{conditionLabel}"); + _builder.AppendLine($"@{iterationLabel}"); + GenerateBlock(whileStatement.Body); + _builder.AppendLine($"@{conditionLabel}"); + var result = GenerateExpression(whileStatement.Condition); + _builder.AppendLine($" jnz {result}, @{iterationLabel}, @{endLabel}"); + _builder.AppendLine($"@{endLabel}"); + + _continueLabels.Pop(); + _breakLabels.Pop(); } private string GenerateExpression(ExpressionNode expression) @@ -214,6 +252,16 @@ public class Generator _strings.Add(literal.Literal); return $"$str{_strings.Count}"; } + + if (literal.LiteralType.Equals(NubType.Int64) || literal.LiteralType.Equals(NubType.Int32)) + { + return literal.Literal; + } + + if (literal.LiteralType.Equals(NubType.Bool)) + { + return bool.Parse(literal.Literal) ? "1" : "0"; + } else { throw new NotImplementedException(); @@ -235,12 +283,16 @@ public class Generator var parameters = results.Select(p => $"{QbeTypeName(p.Item2)} {p.Item1}"); - var output = GenName(); + var output = GenName("var"); _builder.AppendLine($" %{output} ={QbeTypeName(funcCall.Type)} call ${funcCall.FuncCall.Name}({string.Join(", ", parameters)})"); return $"%{output}"; } - private int _nameIndex; - private string GenName() => "v" + ++_nameIndex; + private string GenName(string prefix) + { + var index = _prefixIndexes.GetValueOrDefault(prefix, 0); + _prefixIndexes[prefix] = index + 1; + return $"{prefix}_{index}"; + } } \ No newline at end of file diff --git a/src/runtime/runtime.asm b/src/runtime/runtime.asm index 706777d..57a5bc5 100644 --- a/src/runtime/runtime.asm +++ b/src/runtime/runtime.asm @@ -10,6 +10,7 @@ _start: syscall global nub_strcmp + section .text nub_strcmp: xor rdx, rdx From c3502fe68afd39460758ef799b2574c0fc50bf3e Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 5 May 2025 22:29:50 +0200 Subject: [PATCH 13/32] eliminate unreachable code --- example/program.nub | 9 +++++++-- src/compiler/Nub.Lang/Backend/Generator.cs | 7 ++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/example/program.nub b/example/program.nub index 82f0a47..927fca6 100644 --- a/example/program.nub +++ b/example/program.nub @@ -1,7 +1,12 @@ import "c"; global func main() { - while true { - continue; + let x = true; + while x { + if (true) { + puts("level 1"); + } + puts("level 2"); + break; } } diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index e4af7e7..4786246 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -12,6 +12,7 @@ public class Generator private readonly List _strings = []; private readonly Stack _breakLabels = new(); private readonly Stack _continueLabels = new(); + private bool _codeIsReachable = true; public Generator(List definitions) { @@ -80,10 +81,12 @@ public class Generator private void GenerateBlock(BlockNode block) { - foreach (var statement in block.Statements) + foreach (var statement in block.Statements.Where(_ => _codeIsReachable)) { GenerateStatement(statement); } + + _codeIsReachable = true; } private void GenerateStatement(StatementNode statement) @@ -122,11 +125,13 @@ public class Generator private void GenerateBreak() { _builder.AppendLine($" jmp @{_breakLabels.Peek()}"); + _codeIsReachable = false; } private void GenerateContinue() { _builder.AppendLine($" jmp @{_continueLabels.Peek()}"); + _codeIsReachable = false; } private void GenerateStatementFuncCall(FuncCallStatementNode funcCall) From bdf82f99e758cac1e8a8497d19c5c3517954bd9c Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 5 May 2025 22:55:15 +0200 Subject: [PATCH 14/32] ... --- example/program.nub | 20 +++--- src/compiler/Nub.Lang/Backend/Generator.cs | 71 ++++++++++++++++--- .../Frontend/Parsing/StructDefinitionNode.cs | 4 +- .../Parsing/StructMemberAccessorNode.cs | 4 +- .../Frontend/Typing/ExpressionTyper.cs | 20 +++--- 5 files changed, 85 insertions(+), 34 deletions(-) diff --git a/example/program.nub b/example/program.nub index 927fca6..2a0f552 100644 --- a/example/program.nub +++ b/example/program.nub @@ -1,12 +1,14 @@ import "c"; -global func main() { - let x = true; - while x { - if (true) { - puts("level 1"); - } - puts("level 2"); - break; - } +struct Human { + age: int64; + name: string; } + +global func main() { + printName(); +} + +func printName(human: Human) { + puts(human.name); +} \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index 4786246..654f50b 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -8,7 +8,7 @@ public class Generator private readonly List _definitions; private readonly StringBuilder _builder = new(); private readonly Dictionary _prefixIndexes = new(); - private readonly Dictionary _variables = new(); + private readonly Dictionary _variables = new(); private readonly List _strings = []; private readonly Stack _breakLabels = new(); private readonly Stack _continueLabels = new(); @@ -21,9 +21,16 @@ public class Generator public string Generate() { + foreach (var structDefinition in _definitions.OfType()) + { + GenerateStructDefinition(structDefinition); + _builder.AppendLine(); + } + foreach (var funcDefinition in _definitions.OfType()) { GenerateFuncDefinition(funcDefinition); + _builder.AppendLine(); } for (var i = 0; i < _strings.Count; i++) @@ -47,13 +54,22 @@ public class Generator return "w"; } - throw new Exception($"Invalid qbe type {type}"); + return $":{type.Name}"; } private void GenerateFuncDefinition(LocalFuncDefinitionNode node) { _variables.Clear(); - var parameters = node.Parameters.Select(p => $"{QbeTypeName(p.Type)} %{p.Name}"); + + foreach (var parameter in node.Parameters) + { + _variables.Add(parameter.Name, new Variable + { + Identifier = $"%{parameter.Name}", + Type = parameter.Type + }); + } + if (node.Global) { _builder.Append("export "); @@ -68,7 +84,7 @@ public class Generator _builder.Append('$'); _builder.Append(node.Name); - _builder.AppendLine($"({string.Join(", ", parameters)}) {{"); + _builder.AppendLine($"({string.Join(", ", node.Parameters.Select(p => $"{QbeTypeName(p.Type)} %{p.Name}"))}) {{"); _builder.AppendLine("@start"); GenerateBlock(node.Body); if (!node.ReturnType.HasValue) @@ -79,6 +95,12 @@ public class Generator _builder.AppendLine("}"); } + private void GenerateStructDefinition(StructDefinitionNode structDefinition) + { + var fields = structDefinition.Fields.Select(f => QbeTypeName(f.Type)); + _builder.AppendLine($"type :{structDefinition.Name} = {{ {string.Join(", ", fields)} }}"); + } + private void GenerateBlock(BlockNode block) { foreach (var statement in block.Statements.Where(_ => _codeIsReachable)) @@ -167,6 +189,7 @@ public class Generator GenerateBlock ); } + _builder.AppendLine($"@{endLabel}"); } @@ -185,12 +208,22 @@ public class Generator private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment) { - _variables[variableAssignment.Name] = GenerateExpression(variableAssignment.Value); + var result = GenerateExpression(variableAssignment.Value); + _variables[variableAssignment.Name] = new Variable + { + Identifier = result, + Type = variableAssignment.Value.Type + }; } private void GenerateVariableReassignment(VariableReassignmentNode variableReassignment) { - _variables[variableReassignment.Name] = GenerateExpression(variableReassignment.Value); + var result = GenerateExpression(variableReassignment.Value); + _variables[variableReassignment.Name] = new Variable + { + Identifier = result, + Type = variableReassignment.Value.Type + }; } private void GenerateWhile(WhileNode whileStatement) @@ -198,7 +231,7 @@ public class Generator var conditionLabel = GenName("loop_condition"); var iterationLabel = GenName("loop_iteration"); var endLabel = GenName("loop_end"); - + _breakLabels.Push(endLabel); _continueLabels.Push(conditionLabel); @@ -209,7 +242,7 @@ public class Generator var result = GenerateExpression(whileStatement.Condition); _builder.AppendLine($" jnz {result}, @{iterationLabel}, @{endLabel}"); _builder.AppendLine($"@{endLabel}"); - + _continueLabels.Pop(); _breakLabels.Pop(); } @@ -237,7 +270,16 @@ public class Generator private string GenerateStructMemberAccessor(StructMemberAccessorNode structMemberAccessor) { - throw new NotImplementedException(); + var type = _variables.First(x => x.Key == structMemberAccessor.Fields[0]).Value.Type; + var def = _definitions.OfType().FirstOrDefault(s => s.Name == type.Name); + if (def == null) + { + throw new Exception("def is null"); + } + + throw new NotImplementedException("Not finished yet"); + + return $"load{QbeTypeName(structMemberAccessor.Type)} "; } private string GenerateBinaryExpression(BinaryExpressionNode binaryExpression) @@ -247,7 +289,7 @@ public class Generator private string GenerateIdentifier(IdentifierNode identifier) { - return _variables[identifier.Identifier]; + return _variables[identifier.Identifier].Identifier; } private string GenerateLiteral(LiteralNode literal) @@ -300,4 +342,11 @@ public class Generator _prefixIndexes[prefix] = index + 1; return $"{prefix}_{index}"; } -} \ No newline at end of file + + private class Variable + { + public required string Identifier { get; init; } + public required NubType Type { get; init; } + } +} + diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/StructDefinitionNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/StructDefinitionNode.cs index cb7fd0a..dca6eab 100644 --- a/src/compiler/Nub.Lang/Frontend/Parsing/StructDefinitionNode.cs +++ b/src/compiler/Nub.Lang/Frontend/Parsing/StructDefinitionNode.cs @@ -1,7 +1,7 @@ namespace Nub.Lang.Frontend.Parsing; -public class StructDefinitionNode(string name, List members) : DefinitionNode +public class StructDefinitionNode(string name, List fields) : DefinitionNode { public string Name { get; } = name; - public List Members { get; } = members; + public List Fields { get; } = fields; } \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/StructMemberAccessorNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/StructMemberAccessorNode.cs index a265b62..b06eb72 100644 --- a/src/compiler/Nub.Lang/Frontend/Parsing/StructMemberAccessorNode.cs +++ b/src/compiler/Nub.Lang/Frontend/Parsing/StructMemberAccessorNode.cs @@ -1,6 +1,6 @@ namespace Nub.Lang.Frontend.Parsing; -public class StructMemberAccessorNode(List members) : ExpressionNode +public class StructMemberAccessorNode(List fields) : ExpressionNode { - public List Members { get; } = members; + public List Fields { get; } = fields; } \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs b/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs index adc73c2..fd358a0 100644 --- a/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs +++ b/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs @@ -43,7 +43,7 @@ public class ExpressionTyper foreach (var @class in _structDefinitions) { - foreach (var variable in @class.Members) + foreach (var variable in @class.Fields) { if (variable.Value.HasValue) { @@ -265,37 +265,37 @@ public class ExpressionTyper // TODO: Fix this ugly ass code private void GenerateStructMemberAccessorNode(StructMemberAccessorNode structMemberAccessor) { - var variable = _variables.FirstOrDefault(v => v.Name == structMemberAccessor.Members[0]); + var variable = _variables.FirstOrDefault(v => v.Name == structMemberAccessor.Fields[0]); if (variable == null) { - throw new Exception($"Variable {structMemberAccessor.Members[0]} is not defined"); + throw new Exception($"Variable {structMemberAccessor.Fields[0]} is not defined"); } var definition = _structDefinitions.FirstOrDefault(sd => sd.Name == variable.Type.Name); if (definition == null) { - throw new Exception($"Struct {structMemberAccessor.Members[0]} is not defined"); + throw new Exception($"Struct {structMemberAccessor.Fields[0]} is not defined"); } - for (var i = 1; i < structMemberAccessor.Members.Count - 1; i++) + for (var i = 1; i < structMemberAccessor.Fields.Count - 1; i++) { - var member = definition.Members.FirstOrDefault(m => m.Name == structMemberAccessor.Members[i]); + var member = definition.Fields.FirstOrDefault(m => m.Name == structMemberAccessor.Fields[i]); if (member == null) { - throw new Exception($"Member {structMemberAccessor.Members[i]} does not exist on struct {definition.Name}"); + throw new Exception($"Member {structMemberAccessor.Fields[i]} does not exist on struct {definition.Name}"); } definition = _structDefinitions.FirstOrDefault(sd => sd.Name == member.Type.Name); if (definition == null) { - throw new Exception($"Struct {structMemberAccessor.Members[i]} is not defined"); + throw new Exception($"Struct {structMemberAccessor.Fields[i]} is not defined"); } } - var tmp = definition.Members.FirstOrDefault(m => m.Name == structMemberAccessor.Members.Last()); + var tmp = definition.Fields.FirstOrDefault(m => m.Name == structMemberAccessor.Fields.Last()); if (tmp == null) { - throw new Exception($"Member {structMemberAccessor.Members.Last()} does not exist on struct {definition.Name}"); + throw new Exception($"Member {structMemberAccessor.Fields.Last()} does not exist on struct {definition.Name}"); } structMemberAccessor.Type = tmp.Type; From 1429c3a99358f7502502392f9d3649cef1f22211 Mon Sep 17 00:00:00 2001 From: nub31 Date: Thu, 8 May 2025 18:57:32 +0200 Subject: [PATCH 15/32] ... --- .../{StructMemberAccessorNode.cs => StructFieldAccessorNode.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/compiler/Nub.Lang/Frontend/Parsing/{StructMemberAccessorNode.cs => StructFieldAccessorNode.cs} (54%) diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/StructMemberAccessorNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/StructFieldAccessorNode.cs similarity index 54% rename from src/compiler/Nub.Lang/Frontend/Parsing/StructMemberAccessorNode.cs rename to src/compiler/Nub.Lang/Frontend/Parsing/StructFieldAccessorNode.cs index b06eb72..3297940 100644 --- a/src/compiler/Nub.Lang/Frontend/Parsing/StructMemberAccessorNode.cs +++ b/src/compiler/Nub.Lang/Frontend/Parsing/StructFieldAccessorNode.cs @@ -1,6 +1,6 @@ namespace Nub.Lang.Frontend.Parsing; -public class StructMemberAccessorNode(List fields) : ExpressionNode +public class StructFieldAccessorNode(List fields) : ExpressionNode { public List Fields { get; } = fields; } \ No newline at end of file From 626b2fef0c8415541696f1acec12b8c8e37864f7 Mon Sep 17 00:00:00 2001 From: nub31 Date: Thu, 8 May 2025 18:57:38 +0200 Subject: [PATCH 16/32] ... --- example/program.nub | 12 ++- src/compiler/Nub.Lang/Backend/Generator.cs | 102 ++++++++++++++---- .../Nub.Lang/Frontend/Parsing/Parser.cs | 19 ++-- .../Parsing/StructFieldAccessorNode.cs | 5 +- .../Frontend/Typing/ExpressionTyper.cs | 50 ++++----- 5 files changed, 124 insertions(+), 64 deletions(-) diff --git a/example/program.nub b/example/program.nub index 2a0f552..1d6f058 100644 --- a/example/program.nub +++ b/example/program.nub @@ -1,12 +1,20 @@ import "c"; struct Human { - age: int64; + age: int64 = 0; name: string; } global func main() { - printName(); + while true { + let dad = new Human + { + name = "John"; + }; + + printName(dad); + } + } func printName(human: Human) { diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index 654f50b..6bb766c 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -44,17 +44,22 @@ public class Generator private string QbeTypeName(NubType type) { - if (type.Equals(NubType.Int64) || type.Equals(NubType.String)) - { - return "l"; - } - if (type.Equals(NubType.Int32) || type.Equals(NubType.Bool)) { return "w"; } - return $":{type.Name}"; + return "l"; + } + + private int QbeTypeSize(NubType type) + { + if (type.Equals(NubType.Int32) || type.Equals(NubType.Bool)) + { + return 4; + } + + return 8; } private void GenerateFuncDefinition(LocalFuncDefinitionNode node) @@ -261,25 +266,49 @@ public class Generator return GenerateLiteral(literal); case StructInitializerNode structInitializer: return GenerateStructInitializer(structInitializer); - case StructMemberAccessorNode structMemberAccessor: - return GenerateStructMemberAccessor(structMemberAccessor); + case StructFieldAccessorNode structMemberAccessor: + return GenerateStructFieldAccessor(structMemberAccessor); default: throw new ArgumentOutOfRangeException(nameof(expression)); } } - private string GenerateStructMemberAccessor(StructMemberAccessorNode structMemberAccessor) + private string GenerateStructFieldAccessor(StructFieldAccessorNode structFieldAccessor) { - var type = _variables.First(x => x.Key == structMemberAccessor.Fields[0]).Value.Type; - var def = _definitions.OfType().FirstOrDefault(s => s.Name == type.Name); - if (def == null) - { - throw new Exception("def is null"); - } + var structType = structFieldAccessor.Struct.Type; + var structDefinition = _definitions + .OfType() + .FirstOrDefault(s => s.Name == structType.Name); - throw new NotImplementedException("Not finished yet"); + if (structDefinition == null) + { + throw new Exception($"Struct {structType.Name} is not defined"); + } + + var @struct = GenerateExpression(structFieldAccessor.Struct); + + var fieldIndex = -1; + for (var i = 0; i < structDefinition.Fields.Count; i++) + { + if (structDefinition.Fields[i].Name == structFieldAccessor.Field) + { + fieldIndex = i; + break; + } + } + + if (fieldIndex == -1) + { + throw new Exception($"Field {structFieldAccessor.Field} is not defined in struct {structType.Name}"); + } + + var offsetLabel = GenName("offset"); + _builder.AppendLine($" %{offsetLabel} ={QbeTypeName(structFieldAccessor.Type)} add {@struct}, {fieldIndex * QbeTypeSize(structFieldAccessor.Type)}"); - return $"load{QbeTypeName(structMemberAccessor.Type)} "; + var outputLabel = GenName("field"); + _builder.AppendLine($" %{outputLabel} ={QbeTypeName(structFieldAccessor.Type)} load{QbeTypeName(structFieldAccessor.Type)} %{offsetLabel}"); + + return $"%{outputLabel}"; } private string GenerateBinaryExpression(BinaryExpressionNode binaryExpression) @@ -317,7 +346,44 @@ public class Generator private string GenerateStructInitializer(StructInitializerNode structInitializer) { - throw new NotImplementedException(); + var structDefinition = _definitions.OfType() + .FirstOrDefault(s => s.Name == structInitializer.StructType.Name); + + if (structDefinition == null) + { + throw new Exception($"Struct {structInitializer.StructType.Name} is not defined"); + } + + var structVar = GenName("struct"); + + var size = structDefinition.Fields.Sum(x => QbeTypeSize(x.Type)); + _builder.AppendLine($" %{structVar} =l alloc8 {size}"); + + for (var i = 0; i < structDefinition.Fields.Count; i++) + { + var field = structDefinition.Fields[i]; + + if (structInitializer.Initializers.TryGetValue(field.Name, out var fieldValue)) + { + var var = GenerateExpression(fieldValue); + var offsetLabel = GenName("offset"); + _builder.AppendLine($" %{offsetLabel} =l add %{structVar}, {i * QbeTypeSize(field.Type)}"); + _builder.AppendLine($" store{QbeTypeName(field.Type)} {var}, %{offsetLabel}"); + } + else if (field.Value.HasValue) + { + var var = GenerateExpression(field.Value.Value); + var offsetLabel = GenName("offset"); + _builder.AppendLine($" %{offsetLabel} =l add %{structVar}, {i * QbeTypeSize(field.Type)}"); + _builder.AppendLine($" store{QbeTypeName(field.Type)} {var}, %{offsetLabel}"); + } + else + { + throw new Exception($"Field {field.Name} on struct {structInitializer.StructType.Name} is not initialized"); + } + } + + return $"%{structVar}"; } private string GenerateExpressionFuncCall(FuncCallExpressionNode funcCall) diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs index 2fdf890..c37f2ed 100644 --- a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs @@ -396,7 +396,9 @@ public class Parser return new StructInitializerNode(type, initializers); } default: + { throw new Exception($"Unknown symbol: {symbolToken.Symbol}"); + } } } default: @@ -421,18 +423,15 @@ public class Parser case Symbol.Period: { Next(); - List members = - [ - identifier.Value, - ExpectIdentifier().Value - ]; - - while (TryExpectSymbol(Symbol.Period)) + ExpressionNode result = new IdentifierNode(identifier.Value); + + do { - members.Add(ExpectIdentifier().Value); - } + var field = ExpectIdentifier(); + result = new StructFieldAccessorNode(result, field.Value); + } while (TryExpectSymbol(Symbol.Period)); - return new StructMemberAccessorNode(members); + return result; } case Symbol.OpenParen: { diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/StructFieldAccessorNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/StructFieldAccessorNode.cs index 3297940..cd7e285 100644 --- a/src/compiler/Nub.Lang/Frontend/Parsing/StructFieldAccessorNode.cs +++ b/src/compiler/Nub.Lang/Frontend/Parsing/StructFieldAccessorNode.cs @@ -1,6 +1,7 @@ namespace Nub.Lang.Frontend.Parsing; -public class StructFieldAccessorNode(List fields) : ExpressionNode +public class StructFieldAccessorNode(ExpressionNode @struct, string field) : ExpressionNode { - public List Fields { get; } = fields; + public ExpressionNode Struct { get; } = @struct; + public string Field { get; } = field; } \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs b/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs index fd358a0..d637940 100644 --- a/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs +++ b/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs @@ -179,8 +179,8 @@ public class ExpressionTyper case StructInitializerNode structInitializer: PopulateStructInitializer(structInitializer); break; - case StructMemberAccessorNode structMemberAccessor: - GenerateStructMemberAccessorNode(structMemberAccessor); + case StructFieldAccessorNode structMemberAccessor: + PopulateStructMemberAccessorNode(structMemberAccessor); break; default: throw new ArgumentOutOfRangeException(nameof(expression)); @@ -262,43 +262,29 @@ public class ExpressionTyper structInitializer.Type = structInitializer.StructType; } - // TODO: Fix this ugly ass code - private void GenerateStructMemberAccessorNode(StructMemberAccessorNode structMemberAccessor) + private void PopulateStructMemberAccessorNode(StructFieldAccessorNode structFieldAccessor) { - var variable = _variables.FirstOrDefault(v => v.Name == structMemberAccessor.Fields[0]); - if (variable == null) + PopulateExpression(structFieldAccessor.Struct); + + var structType = structFieldAccessor.Struct.Type; + if (structType == null) { - throw new Exception($"Variable {structMemberAccessor.Fields[0]} is not defined"); + throw new Exception($"Cannot access field on non-struct type: {structFieldAccessor.Struct}"); } - - var definition = _structDefinitions.FirstOrDefault(sd => sd.Name == variable.Type.Name); - if (definition == null) + + var structDefinition = _structDefinitions.FirstOrDefault(s => s.Name == structType.Name); + if (structDefinition == null) { - throw new Exception($"Struct {structMemberAccessor.Fields[0]} is not defined"); + throw new Exception($"Struct {structType.Name} is not defined"); } - - for (var i = 1; i < structMemberAccessor.Fields.Count - 1; i++) + + var field = structDefinition.Fields.FirstOrDefault(f => f.Name == structFieldAccessor.Field); + if (field == null) { - var member = definition.Fields.FirstOrDefault(m => m.Name == structMemberAccessor.Fields[i]); - if (member == null) - { - throw new Exception($"Member {structMemberAccessor.Fields[i]} does not exist on struct {definition.Name}"); - } - - definition = _structDefinitions.FirstOrDefault(sd => sd.Name == member.Type.Name); - if (definition == null) - { - throw new Exception($"Struct {structMemberAccessor.Fields[i]} is not defined"); - } + throw new Exception($"Field {structFieldAccessor.Field} is not defined in struct {structType.Name}"); } - - var tmp = definition.Fields.FirstOrDefault(m => m.Name == structMemberAccessor.Fields.Last()); - if (tmp == null) - { - throw new Exception($"Member {structMemberAccessor.Fields.Last()} does not exist on struct {definition.Name}"); - } - - structMemberAccessor.Type = tmp.Type; + + structFieldAccessor.Type = field.Type; } private class Variable(string name, NubType type) From 340f1e3448c3bced6f571fadb6dd106b31a616e3 Mon Sep 17 00:00:00 2001 From: nub31 Date: Sun, 11 May 2025 17:49:24 +0200 Subject: [PATCH 17/32] binary expressions --- debug.sh | 1 + example/program.nub | 19 +- run.sh | 1 + src/compiler/Nub.Lang/Backend/Generator.cs | 238 +++++++++++++++++++-- 4 files changed, 228 insertions(+), 31 deletions(-) diff --git a/debug.sh b/debug.sh index 3a13bf1..17f8778 100755 --- a/debug.sh +++ b/debug.sh @@ -1,4 +1,5 @@ #!/bin/sh +set -e ./clean.sh ./build.sh gdb -tui ./out/program diff --git a/example/program.nub b/example/program.nub index 1d6f058..25b51f0 100644 --- a/example/program.nub +++ b/example/program.nub @@ -1,22 +1,7 @@ import "c"; -struct Human { - age: int64 = 0; - name: string; -} - global func main() { - while true { - let dad = new Human - { - name = "John"; - }; - - printName(dad); + if 3 / 3 == 1 { + puts("uwu"); } - -} - -func printName(human: Human) { - puts(human.name); } \ No newline at end of file diff --git a/run.sh b/run.sh index 385090b..8ddafc7 100755 --- a/run.sh +++ b/run.sh @@ -1,4 +1,5 @@ #!/bin/sh +set -e ./clean.sh ./build.sh ./out/program diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index 6bb766c..1c1b676 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -74,7 +74,7 @@ public class Generator Type = parameter.Type }); } - + if (node.Global) { _builder.Append("export "); @@ -284,9 +284,9 @@ public class Generator { throw new Exception($"Struct {structType.Name} is not defined"); } - + var @struct = GenerateExpression(structFieldAccessor.Struct); - + var fieldIndex = -1; for (var i = 0; i < structDefinition.Fields.Count; i++) { @@ -296,24 +296,235 @@ public class Generator break; } } - + if (fieldIndex == -1) { throw new Exception($"Field {structFieldAccessor.Field} is not defined in struct {structType.Name}"); } - + var offsetLabel = GenName("offset"); _builder.AppendLine($" %{offsetLabel} ={QbeTypeName(structFieldAccessor.Type)} add {@struct}, {fieldIndex * QbeTypeSize(structFieldAccessor.Type)}"); - + var outputLabel = GenName("field"); _builder.AppendLine($" %{outputLabel} ={QbeTypeName(structFieldAccessor.Type)} load{QbeTypeName(structFieldAccessor.Type)} %{offsetLabel}"); - + return $"%{outputLabel}"; } private string GenerateBinaryExpression(BinaryExpressionNode binaryExpression) { - throw new NotImplementedException(); + var left = GenerateExpression(binaryExpression.Left); + var right = GenerateExpression(binaryExpression.Right); + var outputLabel = GenName(); + + switch (binaryExpression.Operator) + { + case BinaryExpressionOperator.Equal: + { + if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + { + _builder.AppendLine($" %{outputLabel} =w ceql {left}, {right}"); + return $"%{outputLabel}"; + } + + if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + { + _builder.AppendLine($" %{outputLabel} =w ceqw {left}, {right}"); + return $"%{outputLabel}"; + } + + if (binaryExpression.Left.Type.Equals(NubType.String) && binaryExpression.Right.Type.Equals(NubType.String)) + { + _builder.AppendLine($" %{outputLabel} =w call $nub_strcmp(l {left}, l {right})"); + return $"%{outputLabel}"; + } + + if (binaryExpression.Left.Type.Equals(NubType.Bool) && binaryExpression.Right.Type.Equals(NubType.Bool)) + { + _builder.AppendLine($" %{outputLabel} =w ceqw {left}, {right}"); + return $"%{outputLabel}"; + } + break; + } + case BinaryExpressionOperator.NotEqual: + { + if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + { + _builder.AppendLine($" %{outputLabel} =w cnel {left}, {right}"); + return $"%{outputLabel}"; + } + + if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + { + _builder.AppendLine($" %{outputLabel} =w cnew {left}, {right}"); + return $"%{outputLabel}"; + } + + if (binaryExpression.Left.Type.Equals(NubType.String) && binaryExpression.Right.Type.Equals(NubType.String)) + { + _builder.AppendLine($" %{outputLabel} =w call $nub_strcmp(l {left}, l {right})"); + _builder.AppendLine($" %{outputLabel} =w xor %{outputLabel}, 1"); + return $"%{outputLabel}"; + } + + if (binaryExpression.Left.Type.Equals(NubType.Bool) && binaryExpression.Right.Type.Equals(NubType.Bool)) + { + _builder.AppendLine($" %{outputLabel} =w cnew {left}, {right}"); + return $"%{outputLabel}"; + } + break; + } + case BinaryExpressionOperator.GreaterThan: + { + if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + { + _builder.AppendLine($" %{outputLabel} =w csgtl {left}, {right}"); + return $"%{outputLabel}"; + } + + if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + { + _builder.AppendLine($" %{outputLabel} =w csgtw {left}, {right}"); + return $"%{outputLabel}"; + } + + if (binaryExpression.Left.Type.Equals(NubType.Bool) && binaryExpression.Right.Type.Equals(NubType.Bool)) + { + _builder.AppendLine($" %{outputLabel} =w csgtw {left}, {right}"); + return $"%{outputLabel}"; + } + break; + } + case BinaryExpressionOperator.GreaterThanOrEqual: + { + if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + { + _builder.AppendLine($" %{outputLabel} =w csgel {left}, {right}"); + return $"%{outputLabel}"; + } + + if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + { + _builder.AppendLine($" %{outputLabel} =w csgew {left}, {right}"); + return $"%{outputLabel}"; + } + + if (binaryExpression.Left.Type.Equals(NubType.Bool) && binaryExpression.Right.Type.Equals(NubType.Bool)) + { + _builder.AppendLine($" %{outputLabel} =w csgew {left}, {right}"); + return $"%{outputLabel}"; + } + break; + } + case BinaryExpressionOperator.LessThan: + { + if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + { + _builder.AppendLine($" %{outputLabel} =w csltl {left}, {right}"); + return $"%{outputLabel}"; + } + + if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + { + _builder.AppendLine($" %{outputLabel} =w csltw {left}, {right}"); + return $"%{outputLabel}"; + } + + if (binaryExpression.Left.Type.Equals(NubType.Bool) && binaryExpression.Right.Type.Equals(NubType.Bool)) + { + _builder.AppendLine($" %{outputLabel} =w csltw {left}, {right}"); + return $"%{outputLabel}"; + } + break; + } + case BinaryExpressionOperator.LessThanOrEqual: + { + if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + { + _builder.AppendLine($" %{outputLabel} =w cslel {left}, {right}"); + return $"%{outputLabel}"; + } + + if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + { + _builder.AppendLine($" %{outputLabel} =w cslew {left}, {right}"); + return $"%{outputLabel}"; + } + + if (binaryExpression.Left.Type.Equals(NubType.Bool) && binaryExpression.Right.Type.Equals(NubType.Bool)) + { + _builder.AppendLine($" %{outputLabel} =w cslew {left}, {right}"); + return $"%{outputLabel}"; + } + break; + } + case BinaryExpressionOperator.Plus: + { + if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + { + _builder.AppendLine($" %{outputLabel} =l add {left}, {right}"); + return $"%{outputLabel}"; + } + + if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + { + _builder.AppendLine($" %{outputLabel} =w add {left}, {right}"); + return $"%{outputLabel}"; + } + break; + } + case BinaryExpressionOperator.Minus: + { + if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + { + _builder.AppendLine($" %{outputLabel} =l sub {left}, {right}"); + return $"%{outputLabel}"; + } + + if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + { + _builder.AppendLine($" %{outputLabel} =w sub {left}, {right}"); + return $"%{outputLabel}"; + } + break; + } + case BinaryExpressionOperator.Multiply: + { + if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + { + _builder.AppendLine($" %{outputLabel} =l mul {left}, {right}"); + return $"%{outputLabel}"; + } + + if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + { + _builder.AppendLine($" %{outputLabel} =w mul {left}, {right}"); + return $"%{outputLabel}"; + } + break; + } + case BinaryExpressionOperator.Divide: + { + if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + { + _builder.AppendLine($" %{outputLabel} =l div {left}, {right}"); + return $"%{outputLabel}"; + } + + if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + { + _builder.AppendLine($" %{outputLabel} =w div {left}, {right}"); + return $"%{outputLabel}"; + } + break; + } + default: + { + throw new ArgumentOutOfRangeException(); + } + } + + throw new NotSupportedException($"Binary operator {binaryExpression.Operator} for types left: {binaryExpression.Left.Type}, right: {binaryExpression.Right.Type} not supported"); } private string GenerateIdentifier(IdentifierNode identifier) @@ -362,7 +573,7 @@ public class Generator for (var i = 0; i < structDefinition.Fields.Count; i++) { var field = structDefinition.Fields[i]; - + if (structInitializer.Initializers.TryGetValue(field.Name, out var fieldValue)) { var var = GenerateExpression(fieldValue); @@ -396,23 +607,22 @@ public class Generator var parameters = results.Select(p => $"{QbeTypeName(p.Item2)} {p.Item1}"); - var output = GenName("var"); + var output = GenName(); _builder.AppendLine($" %{output} ={QbeTypeName(funcCall.Type)} call ${funcCall.FuncCall.Name}({string.Join(", ", parameters)})"); return $"%{output}"; } - private string GenName(string prefix) + private string GenName(string prefix = "var") { var index = _prefixIndexes.GetValueOrDefault(prefix, 0); _prefixIndexes[prefix] = index + 1; return $"{prefix}_{index}"; } - + private class Variable { public required string Identifier { get; init; } public required NubType Type { get; init; } } -} - +} \ No newline at end of file From 98669aabcc3df2252447f089ef0863843684f4c9 Mon Sep 17 00:00:00 2001 From: nub31 Date: Sun, 11 May 2025 18:08:03 +0200 Subject: [PATCH 18/32] ... --- example/program.nub | 4 ++++ src/compiler/Nub.Lang/Backend/Generator.cs | 16 ++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/example/program.nub b/example/program.nub index 25b51f0..a963944 100644 --- a/example/program.nub +++ b/example/program.nub @@ -4,4 +4,8 @@ global func main() { if 3 / 3 == 1 { puts("uwu"); } + + while 1 == 1 { + puts("loop"); + } } \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index 1c1b676..c90087b 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -176,9 +176,9 @@ public class Generator private void GenerateIf(IfNode ifStatement) { - var trueLabel = GenName("if_true"); - var endLabel = GenName("if_end"); - var falseLabel = GenName("if_false"); + var trueLabel = GenName("true"); + var falseLabel = GenName("false"); + var endLabel = GenName("endif"); var result = GenerateExpression(ifStatement.Condition); _builder.AppendLine($" jnz {result}, @{trueLabel}, @{falseLabel}"); @@ -233,9 +233,9 @@ public class Generator private void GenerateWhile(WhileNode whileStatement) { - var conditionLabel = GenName("loop_condition"); - var iterationLabel = GenName("loop_iteration"); - var endLabel = GenName("loop_end"); + var conditionLabel = GenName("condition"); + var iterationLabel = GenName("iteration"); + var endLabel = GenName("endloop"); _breakLabels.Push(endLabel); _continueLabels.Push(conditionLabel); @@ -613,11 +613,11 @@ public class Generator return $"%{output}"; } - private string GenName(string prefix = "var") + private string GenName(string prefix = "v") { var index = _prefixIndexes.GetValueOrDefault(prefix, 0); _prefixIndexes[prefix] = index + 1; - return $"{prefix}_{index}"; + return $"{prefix}{index}"; } private class Variable From 66fd6e3beb0231eef7a5981049b8a23ebc5e267e Mon Sep 17 00:00:00 2001 From: nub31 Date: Sun, 11 May 2025 18:15:29 +0200 Subject: [PATCH 19/32] ... --- example/program.nub | 3 ++- src/compiler/Nub.Lang/Backend/Generator.cs | 6 ++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/example/program.nub b/example/program.nub index a963944..5dd006a 100644 --- a/example/program.nub +++ b/example/program.nub @@ -6,6 +6,7 @@ global func main() { } while 1 == 1 { - puts("loop"); + puts("test"); + break; } } \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index c90087b..5b73c77 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -549,10 +549,8 @@ public class Generator { return bool.Parse(literal.Literal) ? "1" : "0"; } - else - { - throw new NotImplementedException(); - } + + throw new NotSupportedException($"Literal {literal.LiteralType} is not supported"); } private string GenerateStructInitializer(StructInitializerNode structInitializer) From 403adefa6a57878c4932e8123cdca16132f1f749 Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 12 May 2025 20:47:46 +0200 Subject: [PATCH 20/32] variadics --- example/c/bindings.nub | 3 +- example/program.nub | 9 +-- run.sh | 1 - src/compiler/Nub.Lang/Backend/Generator.cs | 62 ++++++++++++++----- .../Nub.Lang/Frontend/Lexing/Lexer.cs | 1 - .../Nub.Lang/Frontend/Lexing/SymbolToken.cs | 1 - .../Nub.Lang/Frontend/Parsing/Parser.cs | 57 +++++++---------- .../Parsing/VariableReassignmentNode.cs | 7 --- .../Frontend/Typing/ExpressionTyper.cs | 9 +-- src/compiler/Nub.Lang/FuncParameter.cs | 3 +- src/compiler/Nub.Lang/NubType.cs | 24 +++---- src/runtime/runtime.asm | 2 +- 12 files changed, 84 insertions(+), 95 deletions(-) delete mode 100644 src/compiler/Nub.Lang/Frontend/Parsing/VariableReassignmentNode.cs diff --git a/example/c/bindings.nub b/example/c/bindings.nub index a9d3cdb..3c4ba44 100644 --- a/example/c/bindings.nub +++ b/example/c/bindings.nub @@ -1 +1,2 @@ -extern func puts(str: string); \ No newline at end of file +extern func puts(str: string); +extern func printf(fmt: string, ...args: any); \ No newline at end of file diff --git a/example/program.nub b/example/program.nub index 5dd006a..46dcf7e 100644 --- a/example/program.nub +++ b/example/program.nub @@ -1,12 +1,5 @@ import "c"; global func main() { - if 3 / 3 == 1 { - puts("uwu"); - } - - while 1 == 1 { - puts("test"); - break; - } + printf("something %s\n", "your mom"); } \ No newline at end of file diff --git a/run.sh b/run.sh index 8ddafc7..385090b 100755 --- a/run.sh +++ b/run.sh @@ -1,5 +1,4 @@ #!/bin/sh -set -e ./clean.sh ./build.sh ./out/program diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index 5b73c77..9c528a8 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -85,16 +85,33 @@ public class Generator { _builder.Append($"{QbeTypeName(node.ReturnType.Value)} "); } + else + { + _builder.Append("l "); + } _builder.Append('$'); _builder.Append(node.Name); - _builder.AppendLine($"({string.Join(", ", node.Parameters.Select(p => $"{QbeTypeName(p.Type)} %{p.Name}"))}) {{"); + var parameterStrings = new List(); + foreach (var parameter in node.Parameters) + { + if (parameter.Variadic) + { + parameterStrings.Add("..."); + } + else + { + parameterStrings.Add($"{QbeTypeName(parameter.Type)} %{parameter.Name}"); + } + } + + _builder.AppendLine($"({string.Join(", ", parameterStrings)}) {{"); _builder.AppendLine("@start"); GenerateBlock(node.Body); if (!node.ReturnType.HasValue) { - _builder.AppendLine(" ret"); + _builder.AppendLine(" ret 0"); } _builder.AppendLine("}"); @@ -138,9 +155,6 @@ public class Generator case VariableAssignmentNode variableAssignment: GenerateVariableAssignment(variableAssignment); break; - case VariableReassignmentNode variableReassignment: - GenerateVariableReassignment(variableReassignment); - break; case WhileNode whileStatement: GenerateWhile(whileStatement); break; @@ -163,15 +177,39 @@ public class Generator private void GenerateStatementFuncCall(FuncCallStatementNode funcCall) { + var parameterDefinition = _definitions + .OfType() + .FirstOrDefault(d => d.Name == funcCall.FuncCall.Name) + ?.Parameters; + + parameterDefinition ??= _definitions + .OfType() + .FirstOrDefault(d => d.Name == funcCall.FuncCall.Name) + ?.Parameters; + + if (parameterDefinition == null) + { + throw new Exception($"Unknown function {funcCall.FuncCall}"); + } + var results = new List<(string, NubType)>(); foreach (var parameter in funcCall.FuncCall.Parameters) { results.Add((GenerateExpression(parameter), parameter.Type)); } - var parameters = results.Select(p => $"{QbeTypeName(p.Item2)} {p.Item1}"); + var parameterStrings = new List(); + for (var i = 0; i < results.Count; i++) + { + if (parameterDefinition.Count > i && parameterDefinition[i].Variadic) + { + parameterStrings.Add("..."); + } + + parameterStrings.Add($"{QbeTypeName(results[i].Item2)} {results[i].Item1}"); + } - _builder.AppendLine($" call ${funcCall.FuncCall.Name}({string.Join(", ", parameters)})"); + _builder.AppendLine($" call ${funcCall.FuncCall.Name}({string.Join(", ", parameterStrings)})"); } private void GenerateIf(IfNode ifStatement) @@ -221,16 +259,6 @@ public class Generator }; } - private void GenerateVariableReassignment(VariableReassignmentNode variableReassignment) - { - var result = GenerateExpression(variableReassignment.Value); - _variables[variableReassignment.Name] = new Variable - { - Identifier = result, - Type = variableReassignment.Value.Type - }; - } - private void GenerateWhile(WhileNode whileStatement) { var conditionLabel = GenName("condition"); diff --git a/src/compiler/Nub.Lang/Frontend/Lexing/Lexer.cs b/src/compiler/Nub.Lang/Frontend/Lexing/Lexer.cs index a3def22..ed15a4d 100644 --- a/src/compiler/Nub.Lang/Frontend/Lexing/Lexer.cs +++ b/src/compiler/Nub.Lang/Frontend/Lexing/Lexer.cs @@ -8,7 +8,6 @@ public class Lexer ["global"] = Symbol.Global, ["extern"] = Symbol.Extern, ["import"] = Symbol.Import, - ["let"] = Symbol.Let, ["if"] = Symbol.If, ["else"] = Symbol.Else, ["while"] = Symbol.While, diff --git a/src/compiler/Nub.Lang/Frontend/Lexing/SymbolToken.cs b/src/compiler/Nub.Lang/Frontend/Lexing/SymbolToken.cs index a73c376..ccaa7ec 100644 --- a/src/compiler/Nub.Lang/Frontend/Lexing/SymbolToken.cs +++ b/src/compiler/Nub.Lang/Frontend/Lexing/SymbolToken.cs @@ -13,7 +13,6 @@ public enum Symbol Global, Func, Return, - Let, If, Else, While, diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs index c37f2ed..59e8f8b 100644 --- a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs @@ -68,14 +68,14 @@ public class Parser var returnType = Optional.Empty(); if (TryExpectSymbol(Symbol.Colon)) { - returnType = ParseTypeInstance(); + returnType = ParseType(); } var body = ParseBlock(); return new LocalFuncDefinitionNode(name.Value, parameters, body, returnType, false); } - + private LocalFuncDefinitionNode ParseGlobalFuncDefinition() { ExpectSymbol(Symbol.Func); @@ -94,7 +94,7 @@ public class Parser var returnType = Optional.Empty(); if (TryExpectSymbol(Symbol.Colon)) { - returnType = ParseTypeInstance(); + returnType = ParseType(); } var body = ParseBlock(); @@ -120,7 +120,7 @@ public class Parser var returnType = Optional.Empty(); if (TryExpectSymbol(Symbol.Colon)) { - returnType = ParseTypeInstance(); + returnType = ParseType(); } ExpectSymbol(Symbol.Semicolon); @@ -140,7 +140,7 @@ public class Parser { var variableName = ExpectIdentifier().Value; ExpectSymbol(Symbol.Colon); - var variableType = ParseTypeInstance(); + var variableType = ParseType(); var variableValue = Optional.Empty(); @@ -159,11 +159,19 @@ public class Parser private FuncParameter ParseFuncParameter() { + var variadic = false; + if (TryExpectSymbol(Symbol.Period)) + { + ExpectSymbol(Symbol.Period); + ExpectSymbol(Symbol.Period); + variadic = true; + } + var name = ExpectIdentifier(); ExpectSymbol(Symbol.Colon); - var type = ParseTypeInstance(); + var type = ParseType(); - return new FuncParameter(name.Value, type); + return new FuncParameter(name.Value, type, variadic); } private StatementNode ParseStatement() @@ -193,7 +201,7 @@ public class Parser { var value = ParseExpression(); ExpectSymbol(Symbol.Semicolon); - return new VariableReassignmentNode(identifier.Value, value); + return new VariableAssignmentNode(identifier.Value, value); } default: { @@ -206,7 +214,6 @@ public class Parser return symbol.Symbol switch { Symbol.Return => ParseReturn(), - Symbol.Let => ParseVariableAssignment(), Symbol.If => ParseIf(), Symbol.While => ParseWhile(), Symbol.Break => ParseBreak(), @@ -233,16 +240,6 @@ public class Parser return new ReturnNode(value); } - private VariableAssignmentNode ParseVariableAssignment() - { - var name = ExpectIdentifier().Value; - ExpectSymbol(Symbol.Assign); - var value = ParseExpression(); - ExpectSymbol(Symbol.Semicolon); - - return new VariableAssignmentNode(name, value); - } - private IfNode ParseIf() { var condition = ParseExpression(); @@ -381,7 +378,7 @@ public class Parser } case Symbol.New: { - var type = ParseTypeInstance(); + var type = ParseType(); Dictionary initializers = []; ExpectSymbol(Symbol.OpenBrace); while (!TryExpectSymbol(Symbol.CloseBrace)) @@ -424,11 +421,11 @@ public class Parser { Next(); ExpressionNode result = new IdentifierNode(identifier.Value); - + do { var field = ExpectIdentifier(); - result = new StructFieldAccessorNode(result, field.Value); + result = new StructFieldAccessorNode(result, field.Value); } while (TryExpectSymbol(Symbol.Period)); return result; @@ -466,22 +463,10 @@ public class Parser return new BlockNode(statements); } - private NubType ParseTypeInstance() + private NubType ParseType() { - var parameters = new List(); var name = ExpectIdentifier().Value; - - if (TryExpectSymbol(Symbol.LessThan)) - { - do - { - parameters.Add(ParseTypeInstance()); - } while (TryExpectSymbol(Symbol.Comma)); - - ExpectSymbol(Symbol.GreaterThan); - } - - return new NubType(name, parameters.ToArray()); + return new NubType(name); } private Token ExpectToken() diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/VariableReassignmentNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/VariableReassignmentNode.cs deleted file mode 100644 index 74fa9a4..0000000 --- a/src/compiler/Nub.Lang/Frontend/Parsing/VariableReassignmentNode.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Nub.Lang.Frontend.Parsing; - -public class VariableReassignmentNode(string name, ExpressionNode value) : StatementNode -{ - public string Name { get; } = name; - public ExpressionNode Value { get; } = value; -} \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs b/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs index d637940..63e8ebc 100644 --- a/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs +++ b/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs @@ -102,9 +102,6 @@ public class ExpressionTyper case VariableAssignmentNode variableAssignment: PopulateVariableAssignment(variableAssignment); break; - case VariableReassignmentNode variableReassignment: - PopulateVariableReassignment(variableReassignment); - break; case WhileNode whileStatement: PopulateWhileStatement(whileStatement); break; @@ -149,9 +146,9 @@ public class ExpressionTyper _variables.Push(new Variable(variableAssignment.Name, variableAssignment.Value.Type)); } - private void PopulateVariableReassignment(VariableReassignmentNode variableReassignment) + private void PopulateVariableReassignment(VariableAssignmentNode variableAssignment) { - PopulateExpression(variableReassignment.Value); + PopulateExpression(variableAssignment.Value); } private void PopulateWhileStatement(WhileNode whileStatement) @@ -200,7 +197,7 @@ public class ExpressionTyper case BinaryExpressionOperator.LessThan: case BinaryExpressionOperator.LessThanOrEqual: { - binaryExpression.Type = new NubType("bool", []); + binaryExpression.Type = new NubType("bool"); break; } case BinaryExpressionOperator.Plus: diff --git a/src/compiler/Nub.Lang/FuncParameter.cs b/src/compiler/Nub.Lang/FuncParameter.cs index f6c49de..6f19992 100644 --- a/src/compiler/Nub.Lang/FuncParameter.cs +++ b/src/compiler/Nub.Lang/FuncParameter.cs @@ -1,9 +1,10 @@ namespace Nub.Lang; -public class FuncParameter(string name, NubType type) +public class FuncParameter(string name, NubType type, bool variadic) { public string Name { get; } = name; public NubType Type { get; } = type; + public bool Variadic { get; } = variadic; public override string ToString() => $"{Name}: {Type}"; } \ No newline at end of file diff --git a/src/compiler/Nub.Lang/NubType.cs b/src/compiler/Nub.Lang/NubType.cs index 3ced0a5..866881e 100644 --- a/src/compiler/Nub.Lang/NubType.cs +++ b/src/compiler/Nub.Lang/NubType.cs @@ -2,37 +2,31 @@ public sealed class NubType { - public NubType(string name, NubType[] generics) + public NubType(string name) { Name = name; - Generics = generics; } public string Name { get; } - public NubType[] Generics { get; } - public static NubType Int64 => new("int64", []); - public static NubType Int32 => new("int32", []); - public static NubType Bool => new("bool", []); - public static NubType String => new("string", []); + public static NubType Int64 => new("i64"); + public static NubType Int32 => new("i32"); + public static NubType Bool => new("bool"); + public static NubType String => new("string"); + public static NubType Any => new("any"); public override bool Equals(object? obj) { - if (obj is not NubType item) - { - return false; - } - - return Name.Equals(item.Name) && Generics.SequenceEqual(item.Generics); + return obj is NubType item && Name.Equals(item.Name); } public override int GetHashCode() { - return HashCode.Combine(Name, Generics); + return HashCode.Combine(Name); } public override string ToString() { - return $"{Name}<{string.Join(", ", Generics.Select(x => x.ToString()))}>"; + return $"{Name}"; } } \ No newline at end of file diff --git a/src/runtime/runtime.asm b/src/runtime/runtime.asm index 57a5bc5..aa8559a 100644 --- a/src/runtime/runtime.asm +++ b/src/runtime/runtime.asm @@ -5,8 +5,8 @@ section .text _start: call gc_init call main + mov rdi, rax mov rax, 60 - mov rdi, 0 syscall global nub_strcmp From 2c3fa531a41e62b1cf02afa2eb8acdd5f3b274ce Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 12 May 2025 21:09:50 +0200 Subject: [PATCH 21/32] ... --- src/runtime/runtime.asm | 43 +++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/runtime/runtime.asm b/src/runtime/runtime.asm index aa8559a..f710165 100644 --- a/src/runtime/runtime.asm +++ b/src/runtime/runtime.asm @@ -1,31 +1,36 @@ global _start -extern main, gc_init +extern main section .text _start: - call gc_init - call main - mov rdi, rax - mov rax, 60 + ; Extract argc and argv from the stack + mov rdi, [rsp] ; rdi = argc + lea rsi, [rsp + 8] ; rsi = argv (pointer to array of strings) + + ; Call main(argc, argv) + call main ; main returns int in rax + + ; Exit with main's return value + mov rdi, rax ; exit code + mov rax, 60 ; syscall: exit syscall global nub_strcmp -section .text nub_strcmp: - xor rdx, rdx + xor rdx, rdx .loop: - mov al, [rsi + rdx] - mov bl, [rdi + rdx] - inc rdx - cmp al, bl - jne .not_equal - cmp al, 0 - je .equal - jmp .loop + mov al, [rsi + rdx] + mov bl, [rdi + rdx] + inc rdx + cmp al, bl + jne .not_equal + cmp al, 0 + je .equal + jmp .loop .not_equal: - mov rax, 0 - ret + mov rax, 0 + ret .equal: - mov rax, 1 - ret \ No newline at end of file + mov rax, 1 + ret \ No newline at end of file From b4a80bb7e176a3053791e82deeaee553b45e0409 Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 12 May 2025 21:10:51 +0200 Subject: [PATCH 22/32] ... --- build.sh | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/build.sh b/build.sh index f5dbbfb..4ab9e35 100755 --- a/build.sh +++ b/build.sh @@ -1,27 +1,21 @@ #!/bin/sh +set -e + mkdir -p out echo "setup..." dotnet publish -c Release src/compiler/Nub.Lang > /dev/null -if [[ $? -ne 0 ]] ; then - printf "\x1b[31mYour compiler is not compiling!\x1b[0m\n" - exit 1 -fi - -set -e - echo "compiling..." nub example out/out.qbe -gcc -c -g -fno-stack-protector -fno-builtin src/runtime/gc.c -o out/gc.o nasm -g -felf64 src/runtime/runtime.asm -o out/runtime.o qbe out/out.qbe > out/out.s gcc -c -g out/out.s -o out/out.o -gcc -nostartfiles -o out/program out/gc.o out/runtime.o out/out.o +gcc -nostartfiles -o out/program out/runtime.o out/out.o echo "done..." From 90ef9fb8e84f39868938aefc1cebf4d771ee9584 Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 12 May 2025 21:41:00 +0200 Subject: [PATCH 23/32] var... --- example/program.nub | 2 +- .../.idea/.idea.Nub.Lang/.idea/vcs.xml | 1 - src/compiler/Nub.Lang/Backend/Generator.cs | 116 +++++--- .../Nub.Lang/Frontend/Lexing/Lexer.cs | 6 +- .../Nub.Lang/Frontend/Parsing/Parser.cs | 9 +- .../Frontend/Typing/ExpressionTyper.cs | 2 +- src/compiler/Nub.Lang/NubType.cs | 115 +++++++- src/runtime/gc.c | 264 ------------------ 8 files changed, 186 insertions(+), 329 deletions(-) delete mode 100644 src/runtime/gc.c diff --git a/example/program.nub b/example/program.nub index 46dcf7e..4d02d13 100644 --- a/example/program.nub +++ b/example/program.nub @@ -1,4 +1,4 @@ -import "c"; +import c; global func main() { printf("something %s\n", "your mom"); diff --git a/src/compiler/.idea/.idea.Nub.Lang/.idea/vcs.xml b/src/compiler/.idea/.idea.Nub.Lang/.idea/vcs.xml index 64713b8..b2bdec2 100644 --- a/src/compiler/.idea/.idea.Nub.Lang/.idea/vcs.xml +++ b/src/compiler/.idea/.idea.Nub.Lang/.idea/vcs.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index 9c528a8..517b9fc 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -44,22 +44,64 @@ public class Generator private string QbeTypeName(NubType type) { - if (type.Equals(NubType.Int32) || type.Equals(NubType.Bool)) + if (type is NubPrimitiveType primitiveType) { - return "w"; + switch (primitiveType.Kind) + { + case PrimitiveTypeKind.I64: + case PrimitiveTypeKind.U64: + case PrimitiveTypeKind.String: + case PrimitiveTypeKind.Any: + return "l"; + case PrimitiveTypeKind.I32: + case PrimitiveTypeKind.U32: + case PrimitiveTypeKind.I16: + case PrimitiveTypeKind.U16: + case PrimitiveTypeKind.I8: + case PrimitiveTypeKind.U8: + case PrimitiveTypeKind.Bool: + return "w"; + case PrimitiveTypeKind.F64: + return "d"; + case PrimitiveTypeKind.F32: + return "s"; + default: + throw new ArgumentOutOfRangeException(); + } } - return "l"; + throw new NotImplementedException(); } private int QbeTypeSize(NubType type) { - if (type.Equals(NubType.Int32) || type.Equals(NubType.Bool)) + if (type is NubPrimitiveType primitiveType) { - return 4; + switch (primitiveType.Kind) + { + case PrimitiveTypeKind.I64: + case PrimitiveTypeKind.U64: + case PrimitiveTypeKind.String: + case PrimitiveTypeKind.Any: + return 8; + case PrimitiveTypeKind.I32: + case PrimitiveTypeKind.U32: + case PrimitiveTypeKind.I16: + case PrimitiveTypeKind.U16: + case PrimitiveTypeKind.I8: + case PrimitiveTypeKind.U8: + case PrimitiveTypeKind.Bool: + return 4; + case PrimitiveTypeKind.F64: + return 8; + case PrimitiveTypeKind.F32: + return 4; + default: + throw new ArgumentOutOfRangeException(); + } } - return 8; + throw new NotImplementedException(); } private void GenerateFuncDefinition(LocalFuncDefinitionNode node) @@ -349,25 +391,25 @@ public class Generator { case BinaryExpressionOperator.Equal: { - if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) { _builder.AppendLine($" %{outputLabel} =w ceql {left}, {right}"); return $"%{outputLabel}"; } - if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w ceqw {left}, {right}"); return $"%{outputLabel}"; } - if (binaryExpression.Left.Type.Equals(NubType.String) && binaryExpression.Right.Type.Equals(NubType.String)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.String)) { _builder.AppendLine($" %{outputLabel} =w call $nub_strcmp(l {left}, l {right})"); return $"%{outputLabel}"; } - if (binaryExpression.Left.Type.Equals(NubType.Bool) && binaryExpression.Right.Type.Equals(NubType.Bool)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool)) { _builder.AppendLine($" %{outputLabel} =w ceqw {left}, {right}"); return $"%{outputLabel}"; @@ -376,26 +418,26 @@ public class Generator } case BinaryExpressionOperator.NotEqual: { - if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) { _builder.AppendLine($" %{outputLabel} =w cnel {left}, {right}"); return $"%{outputLabel}"; } - if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w cnew {left}, {right}"); return $"%{outputLabel}"; } - if (binaryExpression.Left.Type.Equals(NubType.String) && binaryExpression.Right.Type.Equals(NubType.String)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.String)) { _builder.AppendLine($" %{outputLabel} =w call $nub_strcmp(l {left}, l {right})"); _builder.AppendLine($" %{outputLabel} =w xor %{outputLabel}, 1"); return $"%{outputLabel}"; } - if (binaryExpression.Left.Type.Equals(NubType.Bool) && binaryExpression.Right.Type.Equals(NubType.Bool)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool)) { _builder.AppendLine($" %{outputLabel} =w cnew {left}, {right}"); return $"%{outputLabel}"; @@ -404,19 +446,19 @@ public class Generator } case BinaryExpressionOperator.GreaterThan: { - if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) { _builder.AppendLine($" %{outputLabel} =w csgtl {left}, {right}"); return $"%{outputLabel}"; } - if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w csgtw {left}, {right}"); return $"%{outputLabel}"; } - if (binaryExpression.Left.Type.Equals(NubType.Bool) && binaryExpression.Right.Type.Equals(NubType.Bool)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool)) { _builder.AppendLine($" %{outputLabel} =w csgtw {left}, {right}"); return $"%{outputLabel}"; @@ -425,19 +467,19 @@ public class Generator } case BinaryExpressionOperator.GreaterThanOrEqual: { - if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) { _builder.AppendLine($" %{outputLabel} =w csgel {left}, {right}"); return $"%{outputLabel}"; } - if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w csgew {left}, {right}"); return $"%{outputLabel}"; } - if (binaryExpression.Left.Type.Equals(NubType.Bool) && binaryExpression.Right.Type.Equals(NubType.Bool)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool)) { _builder.AppendLine($" %{outputLabel} =w csgew {left}, {right}"); return $"%{outputLabel}"; @@ -446,19 +488,19 @@ public class Generator } case BinaryExpressionOperator.LessThan: { - if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) { _builder.AppendLine($" %{outputLabel} =w csltl {left}, {right}"); return $"%{outputLabel}"; } - if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w csltw {left}, {right}"); return $"%{outputLabel}"; } - if (binaryExpression.Left.Type.Equals(NubType.Bool) && binaryExpression.Right.Type.Equals(NubType.Bool)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool)) { _builder.AppendLine($" %{outputLabel} =w csltw {left}, {right}"); return $"%{outputLabel}"; @@ -467,19 +509,19 @@ public class Generator } case BinaryExpressionOperator.LessThanOrEqual: { - if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) { _builder.AppendLine($" %{outputLabel} =w cslel {left}, {right}"); return $"%{outputLabel}"; } - if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w cslew {left}, {right}"); return $"%{outputLabel}"; } - if (binaryExpression.Left.Type.Equals(NubType.Bool) && binaryExpression.Right.Type.Equals(NubType.Bool)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool)) { _builder.AppendLine($" %{outputLabel} =w cslew {left}, {right}"); return $"%{outputLabel}"; @@ -488,13 +530,13 @@ public class Generator } case BinaryExpressionOperator.Plus: { - if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) { _builder.AppendLine($" %{outputLabel} =l add {left}, {right}"); return $"%{outputLabel}"; } - if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w add {left}, {right}"); return $"%{outputLabel}"; @@ -503,13 +545,13 @@ public class Generator } case BinaryExpressionOperator.Minus: { - if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) { _builder.AppendLine($" %{outputLabel} =l sub {left}, {right}"); return $"%{outputLabel}"; } - if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w sub {left}, {right}"); return $"%{outputLabel}"; @@ -518,13 +560,13 @@ public class Generator } case BinaryExpressionOperator.Multiply: { - if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) { _builder.AppendLine($" %{outputLabel} =l mul {left}, {right}"); return $"%{outputLabel}"; } - if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w mul {left}, {right}"); return $"%{outputLabel}"; @@ -533,13 +575,13 @@ public class Generator } case BinaryExpressionOperator.Divide: { - if (binaryExpression.Left.Type.Equals(NubType.Int64) && binaryExpression.Right.Type.Equals(NubType.Int64)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) { _builder.AppendLine($" %{outputLabel} =l div {left}, {right}"); return $"%{outputLabel}"; } - if (binaryExpression.Left.Type.Equals(NubType.Int32) && binaryExpression.Right.Type.Equals(NubType.Int32)) + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w div {left}, {right}"); return $"%{outputLabel}"; @@ -562,18 +604,18 @@ public class Generator private string GenerateLiteral(LiteralNode literal) { - if (literal.LiteralType.Equals(NubType.String)) + if (literal.LiteralType.Equals(NubPrimitiveType.String)) { _strings.Add(literal.Literal); return $"$str{_strings.Count}"; } - if (literal.LiteralType.Equals(NubType.Int64) || literal.LiteralType.Equals(NubType.Int32)) + if (literal.LiteralType.Equals(NubPrimitiveType.I64) || literal.LiteralType.Equals(NubPrimitiveType.I32)) { return literal.Literal; } - if (literal.LiteralType.Equals(NubType.Bool)) + if (literal.LiteralType.Equals(NubPrimitiveType.Bool)) { return bool.Parse(literal.Literal) ? "1" : "0"; } diff --git a/src/compiler/Nub.Lang/Frontend/Lexing/Lexer.cs b/src/compiler/Nub.Lang/Frontend/Lexing/Lexer.cs index ed15a4d..cbf544e 100644 --- a/src/compiler/Nub.Lang/Frontend/Lexing/Lexer.cs +++ b/src/compiler/Nub.Lang/Frontend/Lexing/Lexer.cs @@ -86,7 +86,7 @@ public class Lexer if (buffer is "true" or "false") { - return new LiteralToken(NubType.Bool, buffer); + return new LiteralToken(NubPrimitiveType.Bool, buffer); } return new IdentifierToken(buffer); @@ -103,7 +103,7 @@ public class Lexer current = Peek(); } - return new LiteralToken(NubType.Int64, buffer); + return new LiteralToken(NubPrimitiveType.I64, buffer); } // TODO: Revisit this @@ -148,7 +148,7 @@ public class Lexer buffer += current.Value; } - return new LiteralToken(NubType.String, buffer); + return new LiteralToken(NubPrimitiveType.String, buffer); } if (char.IsWhiteSpace(current.Value)) diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs index 59e8f8b..45567a4 100644 --- a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs @@ -20,12 +20,7 @@ public class Parser { if (TryExpectSymbol(Symbol.Import)) { - var name = ExpectLiteral(); - if (!name.Type.Equals(NubType.String)) - { - throw new Exception("Import statements must have a string literal value"); - } - + var name = ExpectIdentifier(); TryExpectSymbol(Symbol.Semicolon); imports.Add(name.Value); } @@ -466,7 +461,7 @@ public class Parser private NubType ParseType() { var name = ExpectIdentifier().Value; - return new NubType(name); + return NubType.Parse(name); } private Token ExpectToken() diff --git a/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs b/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs index 63e8ebc..14e2f64 100644 --- a/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs +++ b/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs @@ -197,7 +197,7 @@ public class ExpressionTyper case BinaryExpressionOperator.LessThan: case BinaryExpressionOperator.LessThanOrEqual: { - binaryExpression.Type = new NubType("bool"); + binaryExpression.Type = NubPrimitiveType.Bool; break; } case BinaryExpressionOperator.Plus: diff --git a/src/compiler/Nub.Lang/NubType.cs b/src/compiler/Nub.Lang/NubType.cs index 866881e..6e580a3 100644 --- a/src/compiler/Nub.Lang/NubType.cs +++ b/src/compiler/Nub.Lang/NubType.cs @@ -1,32 +1,117 @@ -namespace Nub.Lang; +using System.Diagnostics.CodeAnalysis; -public sealed class NubType +namespace Nub.Lang; + +public abstract class NubType { - public NubType(string name) + protected NubType(string name) { Name = name; } public string Name { get; } - public static NubType Int64 => new("i64"); - public static NubType Int32 => new("i32"); - public static NubType Bool => new("bool"); - public static NubType String => new("string"); - public static NubType Any => new("any"); - - public override bool Equals(object? obj) + public static NubType Parse(string s) { - return obj is NubType item && Name.Equals(item.Name); + if (NubPrimitiveType.TryParse(s, out var kind)) + { + return new NubPrimitiveType(kind.Value); + } + + return new NubCustomType(s); } - public override int GetHashCode() + public override bool Equals(object? obj) => obj is NubType item && Name.Equals(item.Name); + public override int GetHashCode() => HashCode.Combine(Name); + public override string ToString() => Name; +} + +public class NubCustomType(string name) : NubType(name); + +public class NubPrimitiveType : NubType +{ + public NubPrimitiveType(PrimitiveTypeKind kind) : base(KindToString(kind)) { - return HashCode.Combine(Name); + Kind = kind; } - public override string ToString() + public PrimitiveTypeKind Kind { get; } + + public static NubPrimitiveType I64 => new(PrimitiveTypeKind.I64); + public static NubPrimitiveType I32 => new(PrimitiveTypeKind.I32); + public static NubPrimitiveType I16 => new(PrimitiveTypeKind.I16); + public static NubPrimitiveType I8 => new(PrimitiveTypeKind.I8); + + public static NubPrimitiveType U64 => new(PrimitiveTypeKind.U64); + public static NubPrimitiveType U32 => new(PrimitiveTypeKind.U32); + public static NubPrimitiveType U16 => new(PrimitiveTypeKind.U16); + public static NubPrimitiveType U8 => new(PrimitiveTypeKind.U8); + + public static NubPrimitiveType F64 => new(PrimitiveTypeKind.F64); + public static NubPrimitiveType F32 => new(PrimitiveTypeKind.F32); + + public static NubPrimitiveType Bool => new(PrimitiveTypeKind.Bool); + public static NubPrimitiveType String => new(PrimitiveTypeKind.String); + public static NubPrimitiveType Any => new(PrimitiveTypeKind.Any); + + public static bool TryParse(string s, [NotNullWhen(true)] out PrimitiveTypeKind? kind) { - return $"{Name}"; + kind = s switch + { + "i64" => PrimitiveTypeKind.I64, + "i32" => PrimitiveTypeKind.I32, + "i16" => PrimitiveTypeKind.I16, + "i8" => PrimitiveTypeKind.I8, + "u64" => PrimitiveTypeKind.U64, + "u32" => PrimitiveTypeKind.U32, + "u16" => PrimitiveTypeKind.U16, + "u8" => PrimitiveTypeKind.U8, + "f64" => PrimitiveTypeKind.F64, + "f32" => PrimitiveTypeKind.F32, + "bool" => PrimitiveTypeKind.Bool, + "string" => PrimitiveTypeKind.String, + "any" => PrimitiveTypeKind.Any, + _ => null + }; + + return kind != null; } + + public static string KindToString(PrimitiveTypeKind kind) + { + return kind switch + { + PrimitiveTypeKind.I64 => "i64", + PrimitiveTypeKind.I32 => "i32", + PrimitiveTypeKind.I16 => "i16", + PrimitiveTypeKind.I8 => "i8", + PrimitiveTypeKind.U64 => "u64", + PrimitiveTypeKind.U32 => "u32", + PrimitiveTypeKind.U16 => "u16", + PrimitiveTypeKind.U8 => "u8", + PrimitiveTypeKind.F64 => "f64", + PrimitiveTypeKind.F32 => "f32", + PrimitiveTypeKind.Bool => "bool", + PrimitiveTypeKind.String => "string", + PrimitiveTypeKind.Any => "any", + _ => throw new ArgumentOutOfRangeException(nameof(kind), kind, null) + }; + } +} + +public enum PrimitiveTypeKind +{ + I64, + I32, + I16, + I8, + U64, + U32, + U16, + U8, + F64, + F32, + Bool, + String, + Any } \ No newline at end of file diff --git a/src/runtime/gc.c b/src/runtime/gc.c deleted file mode 100644 index f2623de..0000000 --- a/src/runtime/gc.c +++ /dev/null @@ -1,264 +0,0 @@ -#include -#include -#include -#include - -#define MINIMUM_THRESHOLD (1024 * 1024 * 8) -#define MINIMUM_BLOCK_SIZE 4096 - -typedef struct alloc_block { - uint64_t mark; - uint64_t size; - struct alloc_block* next; -} alloc_block_t; - -typedef struct free_block { - uint64_t size; - struct free_block* next; -} free_block_t; - -static alloc_block_t* alloc_list_head = NULL; -static free_block_t* free_list_head = NULL; -static void* stack_start = NULL; -static int64_t free_list_size = 0; -static int64_t mark_count = 0; - -/* Bytes allocated since last collect */ -static int64_t bytes_allocated = 0; -/* Threshold for next collect */ -static int64_t trigger_threshold = MINIMUM_THRESHOLD; - -static void* sys_mmap(size_t size); -static void* get_sp(void); -static void gc_collect(void); -static void gc_mark(void* ptr); -static void gc_mark_stack(void); -static void gc_sweep(void); -static int64_t max(int64_t a, int64_t b); -static void insert_into_free(free_block_t* block); -static void merge(free_block_t* block); - -void gc_init(void) { - stack_start = get_sp(); -} - -/* Allocate memory with garbage collection */ -void* gc_alloc(int64_t size) { - size += sizeof(alloc_block_t); // Adjust for metadata size - - if (bytes_allocated > trigger_threshold) { - gc_collect(); - } - - bytes_allocated += size; - - // Search free list for a suitable block - free_block_t* current = free_list_head; - free_block_t* prev = NULL; - - while (current != NULL) { - if (current->size >= size) { - // Found a suitable block - break; - } - prev = current; - current = current->next; - } - - if (current == NULL) { - // No suitable block found, allocate a new one - int64_t alloc_size = max(size, MINIMUM_BLOCK_SIZE); - void* memory = sys_mmap(alloc_size); - - free_block_t* new_block = (free_block_t*)memory; - new_block->size = alloc_size - sizeof(free_block_t); - new_block->next = NULL; - - insert_into_free(new_block); - current = new_block; - - // Recalculate prev - if (current == free_list_head) { - prev = NULL; - } else { - prev = free_list_head; - while (prev->next != current) { - prev = prev->next; - } - } - } - - // Use the block - alloc_block_t* result; - - if (current->size > size) { - // Block is larger than needed, split it - result = (alloc_block_t*)((char*)current + current->size + sizeof(free_block_t) - size); - current->size -= size; - } else { - // Use the entire block - result = (alloc_block_t*)current; - - // Remove block from free list - if (prev == NULL) { - free_list_head = current->next; - } else { - prev->next = current->next; - } - - free_list_size--; - } - - // Initialize metadata - result->mark = 0; - result->size = size - sizeof(alloc_block_t); - result->next = alloc_list_head; - alloc_list_head = result; - - // Return pointer to usable memory - return (void*)(result + 1); -} - -/* Run garbage collection */ -static void gc_collect(void) { - gc_mark_stack(); - gc_sweep(); - trigger_threshold = max(bytes_allocated * 2, MINIMUM_THRESHOLD); - bytes_allocated = 0; -} - -static void gc_mark_stack(void) { - mark_count = 0; - - void** current = get_sp(); - void** end = (void**)stack_start; - - while (current < end) { - gc_mark(*current); - current++; - } -} - -/* Mark a single object and recursively mark its contents */ -static void gc_mark(void* ptr) { - if (ptr == NULL) { - return; - } - - alloc_block_t* block = alloc_list_head; - while (block != NULL) { - void* block_data = (void*)(block + 1); - if (block_data == ptr) { - if (block->mark == 0) { - mark_count++; - block->mark = 1; - - void** p = (void**)block_data; - void** end = (void**)((char*)block_data + block->size); - while (p < end) { - gc_mark(*p); - p++; - } - } - return; - } - block = block->next; - } -} - -static void gc_sweep(void) { - alloc_block_t* current = alloc_list_head; - alloc_block_t* prev = NULL; - - while (current != NULL) { - if (current->mark == 0) { - alloc_block_t* next = current->next; - - if (prev == NULL) { - alloc_list_head = next; - } else { - prev->next = next; - } - - bytes_allocated -= (current->size + sizeof(alloc_block_t)); - - free_block_t* free_block = (free_block_t*)current; - free_block->size = current->size + sizeof(alloc_block_t) - sizeof(free_block_t); - free_block->next = NULL; - - insert_into_free(free_block); - - current = next; - } else { - current->mark = 0; - prev = current; - current = current->next; - } - } -} - -/* Insert a block into the free list, maintaining address order */ -static void insert_into_free(free_block_t* block) { - if (free_list_head == NULL || block < free_list_head) { - // Insert at head - block->next = free_list_head; - free_list_head = block; - free_list_size++; - merge(block); - return; - } - - // Find insertion point - free_block_t* current = free_list_head; - while (current->next != NULL && current->next < block) { - current = current->next; - } - - // Insert after current - block->next = current->next; - current->next = block; - free_list_size++; - - // Try to merge adjacent blocks - merge(current); -} - -static void merge(free_block_t* block) { - while (block->next != NULL) { - char* block_end = (char*)block + block->size + sizeof(free_block_t); - if (block_end == (char*)block->next) { - free_list_size--; - block->size += block->next->size + sizeof(free_block_t); - block->next = block->next->next; - } else { - break; - } - } -} - -static void* sys_mmap(size_t size) { - void* result = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - if (result == MAP_FAILED) { - perror("[sys_mmap] mmap failed"); - exit(1); - } - - return result; -} - -static int64_t max(int64_t a, int64_t b) { - if (a > b) { - return a; - } else { - return b; - } -} - -void* get_sp(void) { - volatile unsigned long var = 0; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wreturn-local-addr" - return (void*)((unsigned long)&var + 4); -#pragma GCC diagnostic pop -} \ No newline at end of file From 0a9442c044a0ed8a4516d713454a003b5e0af17e Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 12 May 2025 21:46:45 +0200 Subject: [PATCH 24/32] ... --- example/program.nub | 13 ++- src/compiler/Nub.Lang/Backend/Generator.cs | 114 ++++++++++++--------- 2 files changed, 78 insertions(+), 49 deletions(-) diff --git a/example/program.nub b/example/program.nub index 4d02d13..11c8189 100644 --- a/example/program.nub +++ b/example/program.nub @@ -1,5 +1,16 @@ import c; +struct Human { + age: i64; + name: string; +} + global func main() { - printf("something %s\n", "your mom"); + me = new Human + { + age = 21; + name = "Oliver"; + }; + + printf("My name is %s and i am %d years old\n", me.name, me.age); } \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index 517b9fc..9a7fd4d 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -42,66 +42,84 @@ public class Generator return _builder.ToString(); } - private string QbeTypeName(NubType type) + private static string QbeTypeName(NubType type) { - if (type is NubPrimitiveType primitiveType) + switch (type) { - switch (primitiveType.Kind) + case NubPrimitiveType primitiveType: { - case PrimitiveTypeKind.I64: - case PrimitiveTypeKind.U64: - case PrimitiveTypeKind.String: - case PrimitiveTypeKind.Any: - return "l"; - case PrimitiveTypeKind.I32: - case PrimitiveTypeKind.U32: - case PrimitiveTypeKind.I16: - case PrimitiveTypeKind.U16: - case PrimitiveTypeKind.I8: - case PrimitiveTypeKind.U8: - case PrimitiveTypeKind.Bool: - return "w"; - case PrimitiveTypeKind.F64: - return "d"; - case PrimitiveTypeKind.F32: - return "s"; - default: - throw new ArgumentOutOfRangeException(); + switch (primitiveType.Kind) + { + case PrimitiveTypeKind.I64: + case PrimitiveTypeKind.U64: + case PrimitiveTypeKind.String: + case PrimitiveTypeKind.Any: + return "l"; + case PrimitiveTypeKind.I32: + case PrimitiveTypeKind.U32: + case PrimitiveTypeKind.I16: + case PrimitiveTypeKind.U16: + case PrimitiveTypeKind.I8: + case PrimitiveTypeKind.U8: + case PrimitiveTypeKind.Bool: + return "w"; + case PrimitiveTypeKind.F64: + return "d"; + case PrimitiveTypeKind.F32: + return "s"; + default: + throw new ArgumentOutOfRangeException(); + } + } + case NubCustomType nubCustomType: + { + return "l"; + } + default: + { + throw new NotImplementedException(); } } - - throw new NotImplementedException(); } - private int QbeTypeSize(NubType type) + private static int QbeTypeSize(NubType type) { - if (type is NubPrimitiveType primitiveType) + switch (type) { - switch (primitiveType.Kind) + case NubPrimitiveType primitiveType: { - case PrimitiveTypeKind.I64: - case PrimitiveTypeKind.U64: - case PrimitiveTypeKind.String: - case PrimitiveTypeKind.Any: - return 8; - case PrimitiveTypeKind.I32: - case PrimitiveTypeKind.U32: - case PrimitiveTypeKind.I16: - case PrimitiveTypeKind.U16: - case PrimitiveTypeKind.I8: - case PrimitiveTypeKind.U8: - case PrimitiveTypeKind.Bool: - return 4; - case PrimitiveTypeKind.F64: - return 8; - case PrimitiveTypeKind.F32: - return 4; - default: - throw new ArgumentOutOfRangeException(); + switch (primitiveType.Kind) + { + case PrimitiveTypeKind.I64: + case PrimitiveTypeKind.U64: + case PrimitiveTypeKind.String: + case PrimitiveTypeKind.Any: + return 8; + case PrimitiveTypeKind.I32: + case PrimitiveTypeKind.U32: + case PrimitiveTypeKind.I16: + case PrimitiveTypeKind.U16: + case PrimitiveTypeKind.I8: + case PrimitiveTypeKind.U8: + case PrimitiveTypeKind.Bool: + return 4; + case PrimitiveTypeKind.F64: + return 8; + case PrimitiveTypeKind.F32: + return 4; + default: + throw new ArgumentOutOfRangeException(); + } + } + case NubCustomType nubCustomType: + { + return 8; + } + default: + { + throw new NotImplementedException(); } } - - throw new NotImplementedException(); } private void GenerateFuncDefinition(LocalFuncDefinitionNode node) From ec06c5eaf5a2e57af20db3ec9426a46b8b7e7750 Mon Sep 17 00:00:00 2001 From: nub31 Date: Wed, 14 May 2025 19:06:26 +0200 Subject: [PATCH 25/32] Fucntion types --- example/program.nub | 15 +-- src/compiler/Nub.Lang/Backend/Generator.cs | 129 +++++++++++++++++++-- 2 files changed, 124 insertions(+), 20 deletions(-) diff --git a/example/program.nub b/example/program.nub index 11c8189..2a9d023 100644 --- a/example/program.nub +++ b/example/program.nub @@ -1,16 +1,13 @@ import c; struct Human { - age: i64; - name: string; + age: i8; } global func main() { - me = new Human - { - age = 21; - name = "Oliver"; - }; - - printf("My name is %s and i am %d years old\n", me.name, me.age); + printf("%d\n", addbyte(10, 300)); +} + +func addbyte(a: i32, sb: i8): i32 { + return a + sb; } \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index 9a7fd4d..0604fac 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -42,7 +42,7 @@ public class Generator return _builder.ToString(); } - private static string QbeTypeName(NubType type) + private static string SQT(NubType type) { switch (type) { @@ -82,6 +82,93 @@ public class Generator } } + private static string EQT(NubType type) + { + switch (type) + { + case NubPrimitiveType primitiveType: + { + switch (primitiveType.Kind) + { + case PrimitiveTypeKind.I64: + case PrimitiveTypeKind.U64: + case PrimitiveTypeKind.String: + case PrimitiveTypeKind.Any: + return "l"; + case PrimitiveTypeKind.I32: + case PrimitiveTypeKind.U32: + return "w"; + case PrimitiveTypeKind.I16: + case PrimitiveTypeKind.U16: + return "h"; + case PrimitiveTypeKind.I8: + case PrimitiveTypeKind.U8: + return "b"; + case PrimitiveTypeKind.Bool: + case PrimitiveTypeKind.F64: + return "d"; + case PrimitiveTypeKind.F32: + return "s"; + default: + throw new ArgumentOutOfRangeException(); + } + } + case NubCustomType nubCustomType: + { + return ":" + nubCustomType.Name; + } + default: + { + throw new NotImplementedException(); + } + } + } + + private static string FQT(NubType type) + { + switch (type) + { + case NubPrimitiveType primitiveType: + { + switch (primitiveType.Kind) + { + case PrimitiveTypeKind.I64: + case PrimitiveTypeKind.U64: + case PrimitiveTypeKind.String: + case PrimitiveTypeKind.Any: + return "l"; + case PrimitiveTypeKind.I32: + case PrimitiveTypeKind.U32: + return "w"; + case PrimitiveTypeKind.I16: + return "sh"; + case PrimitiveTypeKind.U16: + return "uh"; + case PrimitiveTypeKind.I8: + return "sb"; + case PrimitiveTypeKind.U8: + return "ub"; + case PrimitiveTypeKind.Bool: + return "b"; + case PrimitiveTypeKind.F64: + return "d"; + case PrimitiveTypeKind.F32: + return "s"; + default: + throw new ArgumentOutOfRangeException(); + } + } + case NubCustomType nubCustomType: + { + return ":" + nubCustomType.Name; + } + default: + { + throw new NotImplementedException(); + } + } + } + private static int QbeTypeSize(NubType type) { switch (type) @@ -143,7 +230,7 @@ public class Generator _builder.Append("function "); if (node.ReturnType.HasValue) { - _builder.Append($"{QbeTypeName(node.ReturnType.Value)} "); + _builder.Append($"{FQT(node.ReturnType.Value)} "); } else { @@ -162,12 +249,32 @@ public class Generator } else { - parameterStrings.Add($"{QbeTypeName(parameter.Type)} %{parameter.Name}"); + parameterStrings.Add($"{FQT(parameter.Type)} %{parameter.Name}"); } } _builder.AppendLine($"({string.Join(", ", parameterStrings)}) {{"); _builder.AppendLine("@start"); + + foreach (var parameter in node.Parameters) + { + switch (FQT(parameter.Type)) + { + case "sb": + _builder.AppendLine($" %{parameter.Name} =w extsb %{parameter.Name}"); + break; + case "ub": + _builder.AppendLine($" %{parameter.Name} =w extub %{parameter.Name}"); + break; + case "sh": + _builder.AppendLine($" %{parameter.Name} =w extsh %{parameter.Name}"); + break; + case "uh": + _builder.AppendLine($" %{parameter.Name} =w extuh %{parameter.Name}"); + break; + } + } + GenerateBlock(node.Body); if (!node.ReturnType.HasValue) { @@ -179,7 +286,7 @@ public class Generator private void GenerateStructDefinition(StructDefinitionNode structDefinition) { - var fields = structDefinition.Fields.Select(f => QbeTypeName(f.Type)); + var fields = structDefinition.Fields.Select(f => EQT(f.Type)); _builder.AppendLine($"type :{structDefinition.Name} = {{ {string.Join(", ", fields)} }}"); } @@ -266,7 +373,7 @@ public class Generator parameterStrings.Add("..."); } - parameterStrings.Add($"{QbeTypeName(results[i].Item2)} {results[i].Item1}"); + parameterStrings.Add($"{SQT(results[i].Item2)} {results[i].Item1}"); } _builder.AppendLine($" call ${funcCall.FuncCall.Name}({string.Join(", ", parameterStrings)})"); @@ -391,10 +498,10 @@ public class Generator } var offsetLabel = GenName("offset"); - _builder.AppendLine($" %{offsetLabel} ={QbeTypeName(structFieldAccessor.Type)} add {@struct}, {fieldIndex * QbeTypeSize(structFieldAccessor.Type)}"); + _builder.AppendLine($" %{offsetLabel} =l add {@struct}, {fieldIndex * QbeTypeSize(structFieldAccessor.Type)}"); var outputLabel = GenName("field"); - _builder.AppendLine($" %{outputLabel} ={QbeTypeName(structFieldAccessor.Type)} load{QbeTypeName(structFieldAccessor.Type)} %{offsetLabel}"); + _builder.AppendLine($" %{outputLabel} ={SQT(structFieldAccessor.Type)} load{SQT(structFieldAccessor.Type)} %{offsetLabel}"); return $"%{outputLabel}"; } @@ -665,14 +772,14 @@ public class Generator var var = GenerateExpression(fieldValue); var offsetLabel = GenName("offset"); _builder.AppendLine($" %{offsetLabel} =l add %{structVar}, {i * QbeTypeSize(field.Type)}"); - _builder.AppendLine($" store{QbeTypeName(field.Type)} {var}, %{offsetLabel}"); + _builder.AppendLine($" store{SQT(field.Type)} {var}, %{offsetLabel}"); } else if (field.Value.HasValue) { var var = GenerateExpression(field.Value.Value); var offsetLabel = GenName("offset"); _builder.AppendLine($" %{offsetLabel} =l add %{structVar}, {i * QbeTypeSize(field.Type)}"); - _builder.AppendLine($" store{QbeTypeName(field.Type)} {var}, %{offsetLabel}"); + _builder.AppendLine($" store{SQT(field.Type)} {var}, %{offsetLabel}"); } else { @@ -691,10 +798,10 @@ public class Generator results.Add((GenerateExpression(parameter), parameter.Type)); } - var parameters = results.Select(p => $"{QbeTypeName(p.Item2)} {p.Item1}"); + var parameters = results.Select(p => $"{SQT(p.Item2)} {p.Item1}"); var output = GenName(); - _builder.AppendLine($" %{output} ={QbeTypeName(funcCall.Type)} call ${funcCall.FuncCall.Name}({string.Join(", ", parameters)})"); + _builder.AppendLine($" %{output} ={SQT(funcCall.Type)} call ${funcCall.FuncCall.Name}({string.Join(", ", parameters)})"); return $"%{output}"; } From 86878e5fd259a8c1d8d6f582ed9ca79cd3bda8e5 Mon Sep 17 00:00:00 2001 From: nub31 Date: Wed, 14 May 2025 19:36:53 +0200 Subject: [PATCH 26/32] ... --- example/program.nub | 5 +++-- src/compiler/Nub.Lang/Backend/Generator.cs | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/example/program.nub b/example/program.nub index 2a9d023..fd27633 100644 --- a/example/program.nub +++ b/example/program.nub @@ -4,8 +4,9 @@ struct Human { age: i8; } -global func main() { - printf("%d\n", addbyte(10, 300)); +global func main(argc: i64, argv: i64) { + printf("args: %d, starts at %p\n", argc, argv); + printf("10 + 300 = %d\n", addbyte(10, 300)); } func addbyte(a: i32, sb: i8): i32 { diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index 0604fac..3c83b4e 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -184,12 +184,14 @@ public class Generator return 8; case PrimitiveTypeKind.I32: case PrimitiveTypeKind.U32: + return 4; case PrimitiveTypeKind.I16: case PrimitiveTypeKind.U16: + return 2; case PrimitiveTypeKind.I8: case PrimitiveTypeKind.U8: case PrimitiveTypeKind.Bool: - return 4; + return 1; case PrimitiveTypeKind.F64: return 8; case PrimitiveTypeKind.F32: From b8103c11adee785a5d2dcfcf088a28f8f0f9539d Mon Sep 17 00:00:00 2001 From: nub31 Date: Wed, 14 May 2025 21:47:04 +0200 Subject: [PATCH 27/32] ... --- src/compiler/Nub.Lang/Backend/Generator.cs | 74 ++++++++++++++-------- src/compiler/Nub.Lang/Nub.Lang.csproj | 1 - src/compiler/Nub.Lang/NubType.cs | 17 +++-- 3 files changed, 58 insertions(+), 34 deletions(-) diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index 3c83b4e..91184b3 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -123,7 +123,7 @@ public class Generator } } } - + private static string FQT(NubType type) { switch (type) @@ -254,10 +254,10 @@ public class Generator parameterStrings.Add($"{FQT(parameter.Type)} %{parameter.Name}"); } } - + _builder.AppendLine($"({string.Join(", ", parameterStrings)}) {{"); _builder.AppendLine("@start"); - + foreach (var parameter in node.Parameters) { switch (FQT(parameter.Type)) @@ -276,7 +276,7 @@ public class Generator break; } } - + GenerateBlock(node.Body); if (!node.ReturnType.HasValue) { @@ -350,17 +350,17 @@ public class Generator .OfType() .FirstOrDefault(d => d.Name == funcCall.FuncCall.Name) ?.Parameters; - + parameterDefinition ??= _definitions .OfType() .FirstOrDefault(d => d.Name == funcCall.FuncCall.Name) ?.Parameters; - + if (parameterDefinition == null) { throw new Exception($"Unknown function {funcCall.FuncCall}"); } - + var results = new List<(string, NubType)>(); foreach (var parameter in funcCall.FuncCall.Parameters) { @@ -374,7 +374,7 @@ public class Generator { parameterStrings.Add("..."); } - + parameterStrings.Add($"{SQT(results[i].Item2)} {results[i].Item1}"); } @@ -513,7 +513,7 @@ public class Generator var left = GenerateExpression(binaryExpression.Left); var right = GenerateExpression(binaryExpression.Right); var outputLabel = GenName(); - + switch (binaryExpression.Operator) { case BinaryExpressionOperator.Equal: @@ -523,7 +523,7 @@ public class Generator _builder.AppendLine($" %{outputLabel} =w ceql {left}, {right}"); return $"%{outputLabel}"; } - + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w ceqw {left}, {right}"); @@ -535,12 +535,13 @@ public class Generator _builder.AppendLine($" %{outputLabel} =w call $nub_strcmp(l {left}, l {right})"); return $"%{outputLabel}"; } - + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool)) { _builder.AppendLine($" %{outputLabel} =w ceqw {left}, {right}"); return $"%{outputLabel}"; } + break; } case BinaryExpressionOperator.NotEqual: @@ -550,7 +551,7 @@ public class Generator _builder.AppendLine($" %{outputLabel} =w cnel {left}, {right}"); return $"%{outputLabel}"; } - + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w cnew {left}, {right}"); @@ -563,12 +564,13 @@ public class Generator _builder.AppendLine($" %{outputLabel} =w xor %{outputLabel}, 1"); return $"%{outputLabel}"; } - + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool)) { _builder.AppendLine($" %{outputLabel} =w cnew {left}, {right}"); return $"%{outputLabel}"; } + break; } case BinaryExpressionOperator.GreaterThan: @@ -578,18 +580,19 @@ public class Generator _builder.AppendLine($" %{outputLabel} =w csgtl {left}, {right}"); return $"%{outputLabel}"; } - + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w csgtw {left}, {right}"); return $"%{outputLabel}"; } - + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool)) { _builder.AppendLine($" %{outputLabel} =w csgtw {left}, {right}"); return $"%{outputLabel}"; } + break; } case BinaryExpressionOperator.GreaterThanOrEqual: @@ -599,18 +602,19 @@ public class Generator _builder.AppendLine($" %{outputLabel} =w csgel {left}, {right}"); return $"%{outputLabel}"; } - + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w csgew {left}, {right}"); return $"%{outputLabel}"; } - + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool)) { _builder.AppendLine($" %{outputLabel} =w csgew {left}, {right}"); return $"%{outputLabel}"; } + break; } case BinaryExpressionOperator.LessThan: @@ -620,18 +624,19 @@ public class Generator _builder.AppendLine($" %{outputLabel} =w csltl {left}, {right}"); return $"%{outputLabel}"; } - + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w csltw {left}, {right}"); return $"%{outputLabel}"; } - + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool)) { _builder.AppendLine($" %{outputLabel} =w csltw {left}, {right}"); return $"%{outputLabel}"; } + break; } case BinaryExpressionOperator.LessThanOrEqual: @@ -641,18 +646,19 @@ public class Generator _builder.AppendLine($" %{outputLabel} =w cslel {left}, {right}"); return $"%{outputLabel}"; } - + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w cslew {left}, {right}"); return $"%{outputLabel}"; } - + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool)) { _builder.AppendLine($" %{outputLabel} =w cslew {left}, {right}"); return $"%{outputLabel}"; } + break; } case BinaryExpressionOperator.Plus: @@ -662,12 +668,13 @@ public class Generator _builder.AppendLine($" %{outputLabel} =l add {left}, {right}"); return $"%{outputLabel}"; } - + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w add {left}, {right}"); return $"%{outputLabel}"; } + break; } case BinaryExpressionOperator.Minus: @@ -677,12 +684,13 @@ public class Generator _builder.AppendLine($" %{outputLabel} =l sub {left}, {right}"); return $"%{outputLabel}"; } - + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w sub {left}, {right}"); return $"%{outputLabel}"; } + break; } case BinaryExpressionOperator.Multiply: @@ -692,12 +700,13 @@ public class Generator _builder.AppendLine($" %{outputLabel} =l mul {left}, {right}"); return $"%{outputLabel}"; } - + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w mul {left}, {right}"); return $"%{outputLabel}"; } + break; } case BinaryExpressionOperator.Divide: @@ -707,12 +716,13 @@ public class Generator _builder.AppendLine($" %{outputLabel} =l div {left}, {right}"); return $"%{outputLabel}"; } - + if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) { _builder.AppendLine($" %{outputLabel} =w div {left}, {right}"); return $"%{outputLabel}"; } + break; } default: @@ -721,7 +731,8 @@ public class Generator } } - throw new NotSupportedException($"Binary operator {binaryExpression.Operator} for types left: {binaryExpression.Left.Type}, right: {binaryExpression.Right.Type} not supported"); + throw new NotSupportedException( + $"Binary operator {binaryExpression.Operator} for types left: {binaryExpression.Left.Type}, right: {binaryExpression.Right.Type} not supported"); } private string GenerateIdentifier(IdentifierNode identifier) @@ -750,6 +761,17 @@ public class Generator throw new NotSupportedException($"Literal {literal.LiteralType} is not supported"); } + private string GenerateCast(string input, NubType inputType, string output, NubType outputType) + { + if (outputType is not NubPrimitiveType primitiveOutputType || inputType is not NubPrimitiveType primitiveInputType) + { + throw new NotSupportedException("Casting is only supported for primitive types"); + } + + // var instruction = + return "" + } + private string GenerateStructInitializer(StructInitializerNode structInitializer) { var structDefinition = _definitions.OfType() diff --git a/src/compiler/Nub.Lang/Nub.Lang.csproj b/src/compiler/Nub.Lang/Nub.Lang.csproj index ee51912..865633d 100644 --- a/src/compiler/Nub.Lang/Nub.Lang.csproj +++ b/src/compiler/Nub.Lang/Nub.Lang.csproj @@ -6,7 +6,6 @@ net9.0 enable enable - true diff --git a/src/compiler/Nub.Lang/NubType.cs b/src/compiler/Nub.Lang/NubType.cs index 6e580a3..c69f582 100644 --- a/src/compiler/Nub.Lang/NubType.cs +++ b/src/compiler/Nub.Lang/NubType.cs @@ -81,16 +81,19 @@ public class NubPrimitiveType : NubType { return kind switch { - PrimitiveTypeKind.I64 => "i64", - PrimitiveTypeKind.I32 => "i32", - PrimitiveTypeKind.I16 => "i16", PrimitiveTypeKind.I8 => "i8", - PrimitiveTypeKind.U64 => "u64", - PrimitiveTypeKind.U32 => "u32", - PrimitiveTypeKind.U16 => "u16", + PrimitiveTypeKind.I16 => "i16", + PrimitiveTypeKind.I32 => "i32", + PrimitiveTypeKind.I64 => "i64", + PrimitiveTypeKind.U8 => "u8", - PrimitiveTypeKind.F64 => "f64", + PrimitiveTypeKind.U16 => "u16", + PrimitiveTypeKind.U32 => "u32", + PrimitiveTypeKind.U64 => "u64", + PrimitiveTypeKind.F32 => "f32", + PrimitiveTypeKind.F64 => "f64", + PrimitiveTypeKind.Bool => "bool", PrimitiveTypeKind.String => "string", PrimitiveTypeKind.Any => "any", From e0bbb7478e308295560f3fa33c92180bb89e56da Mon Sep 17 00:00:00 2001 From: nub31 Date: Fri, 16 May 2025 19:32:24 +0200 Subject: [PATCH 28/32] ... --- build.sh | 2 +- example/program.nub | 19 +- src/compiler/Nub.Lang/Backend/Generator.cs | 414 +++++++++++++++++---- src/compiler/Nub.Lang/Nub.Lang.csproj | 1 + 4 files changed, 353 insertions(+), 83 deletions(-) diff --git a/build.sh b/build.sh index 4ab9e35..88e5648 100755 --- a/build.sh +++ b/build.sh @@ -5,7 +5,7 @@ mkdir -p out echo "setup..." -dotnet publish -c Release src/compiler/Nub.Lang > /dev/null +dotnet publish -c Release src/compiler/Nub.Lang echo "compiling..." diff --git a/example/program.nub b/example/program.nub index fd27633..883fac5 100644 --- a/example/program.nub +++ b/example/program.nub @@ -1,14 +1,19 @@ import c; -struct Human { - age: i8; -} - global func main(argc: i64, argv: i64) { printf("args: %d, starts at %p\n", argc, argv); - printf("10 + 300 = %d\n", addbyte(10, 300)); + + a = 128; + b = 32768; + c = 2147483648; + d = 9223372036850000000; + + x = test(a, b, c, d); + + printf("%d\n", x); } -func addbyte(a: i32, sb: i8): i32 { - return a + sb; +func test(a: i8, b: i16, c: i32, d: i64): i64 { + printf("a: %d, b: %d, c: %d, d: %d\n", a, b, c, d); + return 12; } \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index 91184b3..325d0ce 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -53,8 +53,9 @@ public class Generator case PrimitiveTypeKind.I64: case PrimitiveTypeKind.U64: case PrimitiveTypeKind.String: - case PrimitiveTypeKind.Any: return "l"; + case PrimitiveTypeKind.Any: + throw new NotSupportedException("Cannot convert 'any' type to QBE type"); case PrimitiveTypeKind.I32: case PrimitiveTypeKind.U32: case PrimitiveTypeKind.I16: @@ -93,8 +94,9 @@ public class Generator case PrimitiveTypeKind.I64: case PrimitiveTypeKind.U64: case PrimitiveTypeKind.String: - case PrimitiveTypeKind.Any: return "l"; + case PrimitiveTypeKind.Any: + throw new NotSupportedException("Cannot convert any to QBE type"); case PrimitiveTypeKind.I32: case PrimitiveTypeKind.U32: return "w"; @@ -135,8 +137,9 @@ public class Generator case PrimitiveTypeKind.I64: case PrimitiveTypeKind.U64: case PrimitiveTypeKind.String: - case PrimitiveTypeKind.Any: return "l"; + case PrimitiveTypeKind.Any: + throw new NotSupportedException("Cannot convert any to QBE type"); case PrimitiveTypeKind.I32: case PrimitiveTypeKind.U32: return "w"; @@ -215,15 +218,6 @@ public class Generator { _variables.Clear(); - foreach (var parameter in node.Parameters) - { - _variables.Add(parameter.Name, new Variable - { - Identifier = $"%{parameter.Name}", - Type = parameter.Type - }); - } - if (node.Global) { _builder.Append("export "); @@ -260,21 +254,33 @@ public class Generator foreach (var parameter in node.Parameters) { + var parameterName = parameter.Name; + switch (FQT(parameter.Type)) { case "sb": - _builder.AppendLine($" %{parameter.Name} =w extsb %{parameter.Name}"); + parameterName = GenName("c"); + _builder.AppendLine($" %{parameterName} =w extsb %{parameter.Name}"); break; case "ub": - _builder.AppendLine($" %{parameter.Name} =w extub %{parameter.Name}"); + parameterName = GenName("c"); + _builder.AppendLine($" %{parameterName} =w extub %{parameter.Name}"); break; case "sh": - _builder.AppendLine($" %{parameter.Name} =w extsh %{parameter.Name}"); + parameterName = GenName("c"); + _builder.AppendLine($" %{parameterName} =w extsh %{parameter.Name}"); break; case "uh": - _builder.AppendLine($" %{parameter.Name} =w extuh %{parameter.Name}"); + parameterName = GenName("c"); + _builder.AppendLine($" %{parameterName} =w extuh %{parameter.Name}"); break; } + + _variables.Add(parameter.Name, new Variable + { + Identifier = $"%{parameterName}", + Type = parameter.Type + }); } GenerateBlock(node.Body); @@ -292,16 +298,6 @@ public class Generator _builder.AppendLine($"type :{structDefinition.Name} = {{ {string.Join(", ", fields)} }}"); } - private void GenerateBlock(BlockNode block) - { - foreach (var statement in block.Statements.Where(_ => _codeIsReachable)) - { - GenerateStatement(statement); - } - - _codeIsReachable = true; - } - private void GenerateStatement(StatementNode statement) { switch (statement) @@ -332,6 +328,67 @@ public class Generator } } + private string GenerateFuncCall(FuncCall funcCall) + { + var parameterDefinitions = _definitions + .OfType() + .FirstOrDefault(d => d.Name == funcCall.Name) + ?.Parameters; + + parameterDefinitions ??= _definitions + .OfType() + .FirstOrDefault(d => d.Name == funcCall.Name) + ?.Parameters; + + if (parameterDefinitions == null) + { + throw new Exception($"Unknown function {funcCall}"); + } + + var parameterStrings = new List(); + + for (var i = 0; i < funcCall.Parameters.Count; i++) + { + if (i < parameterDefinitions.Count && parameterDefinitions[i].Variadic) + { + parameterStrings.Add("..."); + } + + NubType expectedType; + if (i < parameterDefinitions.Count) + { + expectedType = parameterDefinitions[i].Type; + } + else if (parameterDefinitions[^1].Variadic) + { + expectedType = parameterDefinitions[^1].Type; + } + else + { + throw new Exception($"Parameters for func {funcCall} does not not match"); + } + + var parameter = funcCall.Parameters[i]; + var parameterOutput = GenerateExpression(parameter); + var result = GenerateTypeConversion(parameterOutput, parameter.Type, expectedType); + + var qbeParameterType = SQT(expectedType.Equals(NubPrimitiveType.Any) ? parameter.Type : expectedType); + parameterStrings.Add($"{qbeParameterType} {result}"); + } + + return $"call ${funcCall.Name}({string.Join(", ", parameterStrings)})"; + } + + private void GenerateBlock(BlockNode block) + { + foreach (var statement in block.Statements.Where(_ => _codeIsReachable)) + { + GenerateStatement(statement); + } + + _codeIsReachable = true; + } + private void GenerateBreak() { _builder.AppendLine($" jmp @{_breakLabels.Peek()}"); @@ -346,39 +403,7 @@ public class Generator private void GenerateStatementFuncCall(FuncCallStatementNode funcCall) { - var parameterDefinition = _definitions - .OfType() - .FirstOrDefault(d => d.Name == funcCall.FuncCall.Name) - ?.Parameters; - - parameterDefinition ??= _definitions - .OfType() - .FirstOrDefault(d => d.Name == funcCall.FuncCall.Name) - ?.Parameters; - - if (parameterDefinition == null) - { - throw new Exception($"Unknown function {funcCall.FuncCall}"); - } - - var results = new List<(string, NubType)>(); - foreach (var parameter in funcCall.FuncCall.Parameters) - { - results.Add((GenerateExpression(parameter), parameter.Type)); - } - - var parameterStrings = new List(); - for (var i = 0; i < results.Count; i++) - { - if (parameterDefinition.Count > i && parameterDefinition[i].Variadic) - { - parameterStrings.Add("..."); - } - - parameterStrings.Add($"{SQT(results[i].Item2)} {results[i].Item1}"); - } - - _builder.AppendLine($" call ${funcCall.FuncCall.Name}({string.Join(", ", parameterStrings)})"); + _builder.AppendLine($" {GenerateFuncCall(funcCall.FuncCall)}"); } private void GenerateIf(IfNode ifStatement) @@ -748,7 +773,7 @@ public class Generator return $"$str{_strings.Count}"; } - if (literal.LiteralType.Equals(NubPrimitiveType.I64) || literal.LiteralType.Equals(NubPrimitiveType.I32)) + if (literal.LiteralType.Equals(NubPrimitiveType.I64)) { return literal.Literal; } @@ -761,15 +786,263 @@ public class Generator throw new NotSupportedException($"Literal {literal.LiteralType} is not supported"); } - private string GenerateCast(string input, NubType inputType, string output, NubType outputType) + private string GenerateTypeConversion(string input, NubType inputType, NubType outputType) { + if (inputType.Equals(outputType)) + { + return input; + } + if (outputType is not NubPrimitiveType primitiveOutputType || inputType is not NubPrimitiveType primitiveInputType) { throw new NotSupportedException("Casting is only supported for primitive types"); } - // var instruction = - return "" + if (primitiveOutputType.Kind == PrimitiveTypeKind.Any) return input; + if (primitiveOutputType.Kind == PrimitiveTypeKind.Bool) + { + throw new NotSupportedException("Cannot cast any type to a bool"); + } + + var outputLabel = GenName("c"); + + switch (primitiveInputType.Kind) + { + case PrimitiveTypeKind.I64: + switch (primitiveOutputType.Kind) + { + case PrimitiveTypeKind.I64: + case PrimitiveTypeKind.U64: + case PrimitiveTypeKind.I32: + case PrimitiveTypeKind.U32: + case PrimitiveTypeKind.I16: + case PrimitiveTypeKind.U16: + case PrimitiveTypeKind.I8: + case PrimitiveTypeKind.U8: + return input; + case PrimitiveTypeKind.F64: + _builder.AppendLine($" %{outputLabel} =d sltof {input}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.F32: + _builder.AppendLine($" %{outputLabel} =s sltof {input}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.String: + default: + throw new ArgumentOutOfRangeException(); + } + case PrimitiveTypeKind.I32: + switch (primitiveOutputType.Kind) + { + case PrimitiveTypeKind.I64: + case PrimitiveTypeKind.U64: + _builder.AppendLine($" %{outputLabel} =l extsw {input}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.I32: + case PrimitiveTypeKind.U32: + case PrimitiveTypeKind.I16: + case PrimitiveTypeKind.U16: + case PrimitiveTypeKind.I8: + case PrimitiveTypeKind.U8: + return input; + case PrimitiveTypeKind.F64: + var extLabel = GenName("ext"); + _builder.AppendLine($" %{extLabel} =l extsw {input}"); + _builder.AppendLine($" %{outputLabel} =d sltof {extLabel}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.F32: + _builder.AppendLine($" %{outputLabel} =s swtof {input}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.String: + default: + throw new ArgumentOutOfRangeException(); + } + case PrimitiveTypeKind.I16: + switch (primitiveOutputType.Kind) + { + case PrimitiveTypeKind.I64: + case PrimitiveTypeKind.U64: + _builder.AppendLine($" %{outputLabel} =l extsh {input}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.I32: + case PrimitiveTypeKind.U32: + _builder.AppendLine($" %{outputLabel} =w extsh {input}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.I16: + case PrimitiveTypeKind.U16: + case PrimitiveTypeKind.I8: + case PrimitiveTypeKind.U8: + return input; + case PrimitiveTypeKind.F64: + { + var extLabel = GenName("ext"); + _builder.AppendLine($" %{extLabel} =l extsh {input}"); + _builder.AppendLine($" %{outputLabel} =d sltof {extLabel}"); + return $"%{outputLabel}"; + } + case PrimitiveTypeKind.F32: + { + var extLabel = GenName("ext"); + _builder.AppendLine($" %{extLabel} =w extsh {input}"); + _builder.AppendLine($" %{outputLabel} =s swtof {extLabel}"); + return $"%{outputLabel}"; + } + case PrimitiveTypeKind.String: + default: + throw new ArgumentOutOfRangeException(); + } + case PrimitiveTypeKind.I8: + switch (primitiveOutputType.Kind) + { + case PrimitiveTypeKind.I64: + case PrimitiveTypeKind.U64: + _builder.AppendLine($" %{outputLabel} =l extsb {input}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.I32: + case PrimitiveTypeKind.U32: + case PrimitiveTypeKind.I16: + case PrimitiveTypeKind.U16: + _builder.AppendLine($" %{outputLabel} =w extsb {input}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.I8: + case PrimitiveTypeKind.U8: + return input; + case PrimitiveTypeKind.F64: + { + var extLabel = GenName("ext"); + _builder.AppendLine($" %{extLabel} =l extsb {input}"); + _builder.AppendLine($" %{outputLabel} =d sltof {extLabel}"); + return $"%{outputLabel}"; + } + case PrimitiveTypeKind.F32: + { + var extLabel = GenName("ext"); + _builder.AppendLine($" %{extLabel} =w extsb {input}"); + _builder.AppendLine($" %{outputLabel} =s swtof {extLabel}"); + return $"%{outputLabel}"; + } + case PrimitiveTypeKind.String: + default: + throw new ArgumentOutOfRangeException(); + } + case PrimitiveTypeKind.U64: + switch (primitiveOutputType.Kind) + { + case PrimitiveTypeKind.I64: + case PrimitiveTypeKind.U64: + case PrimitiveTypeKind.I32: + case PrimitiveTypeKind.U32: + case PrimitiveTypeKind.I16: + case PrimitiveTypeKind.U16: + case PrimitiveTypeKind.I8: + case PrimitiveTypeKind.U8: + return input; + case PrimitiveTypeKind.F64: + _builder.AppendLine($" %{outputLabel} =d ultof {input}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.F32: + _builder.AppendLine($" %{outputLabel} =s ultof {input}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.String: + default: + throw new ArgumentOutOfRangeException(); + } + case PrimitiveTypeKind.U32: + switch (primitiveOutputType.Kind) + { + case PrimitiveTypeKind.I64: + case PrimitiveTypeKind.U64: + _builder.AppendLine($" %{outputLabel} =l extuw {input}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.I32: + case PrimitiveTypeKind.U32: + case PrimitiveTypeKind.I16: + case PrimitiveTypeKind.U16: + case PrimitiveTypeKind.I8: + case PrimitiveTypeKind.U8: + return input; + case PrimitiveTypeKind.F64: + var extLabel = GenName("ext"); + _builder.AppendLine($" %{extLabel} =l extuw {input}"); + _builder.AppendLine($" %{outputLabel} =d ultof {extLabel}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.F32: + _builder.AppendLine($" %{outputLabel} =s uwtof {input}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.String: + default: + throw new ArgumentOutOfRangeException(); + } + case PrimitiveTypeKind.U16: + switch (primitiveOutputType.Kind) + { + case PrimitiveTypeKind.I64: + case PrimitiveTypeKind.U64: + _builder.AppendLine($" %{outputLabel} =l extuh {input}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.I32: + case PrimitiveTypeKind.U32: + _builder.AppendLine($" %{outputLabel} =w extuh {input}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.I16: + case PrimitiveTypeKind.U16: + case PrimitiveTypeKind.I8: + case PrimitiveTypeKind.U8: + return input; + case PrimitiveTypeKind.F64: + { + var extLabel = GenName("ext"); + _builder.AppendLine($" %{extLabel} =l extuh {input}"); + _builder.AppendLine($" %{outputLabel} =d ultof {extLabel}"); + return $"%{outputLabel}"; + } + case PrimitiveTypeKind.F32: + { + var extLabel = GenName("ext"); + _builder.AppendLine($" %{extLabel} =w extuh {input}"); + _builder.AppendLine($" %{outputLabel} =s uwtof {extLabel}"); + return $"%{outputLabel}"; + } + case PrimitiveTypeKind.String: + default: + throw new ArgumentOutOfRangeException(); + } + case PrimitiveTypeKind.U8: + switch (primitiveOutputType.Kind) + { + case PrimitiveTypeKind.I64: + case PrimitiveTypeKind.U64: + _builder.AppendLine($" %{outputLabel} =l extub {input}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.I32: + case PrimitiveTypeKind.U32: + case PrimitiveTypeKind.I16: + case PrimitiveTypeKind.U16: + _builder.AppendLine($" %{outputLabel} =w extub {input}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.I8: + case PrimitiveTypeKind.U8: + return input; + case PrimitiveTypeKind.F64: + { + var extLabel = GenName("ext"); + _builder.AppendLine($" %{extLabel} =l extub {input}"); + _builder.AppendLine($" %{outputLabel} =d ultof {extLabel}"); + return $"%{outputLabel}"; + } + case PrimitiveTypeKind.F32: + { + var extLabel = GenName("ext"); + _builder.AppendLine($" %{extLabel} =w extub {input}"); + _builder.AppendLine($" %{outputLabel} =s uwtof {extLabel}"); + return $"%{outputLabel}"; + } + case PrimitiveTypeKind.String: + default: + throw new ArgumentOutOfRangeException(); + } + default: + throw new NotSupportedException($"Casting from {primitiveInputType.Kind} to {primitiveOutputType.Kind} is not supported"); + } } private string GenerateStructInitializer(StructInitializerNode structInitializer) @@ -816,18 +1089,9 @@ public class Generator private string GenerateExpressionFuncCall(FuncCallExpressionNode funcCall) { - var results = new List<(string, NubType)>(); - foreach (var parameter in funcCall.FuncCall.Parameters) - { - results.Add((GenerateExpression(parameter), parameter.Type)); - } - - var parameters = results.Select(p => $"{SQT(p.Item2)} {p.Item1}"); - - var output = GenName(); - _builder.AppendLine($" %{output} ={SQT(funcCall.Type)} call ${funcCall.FuncCall.Name}({string.Join(", ", parameters)})"); - - return $"%{output}"; + var outputLabel = GenName(); + _builder.AppendLine($" %{outputLabel} ={SQT(funcCall.Type)} {GenerateFuncCall(funcCall.FuncCall)}"); + return $"%{outputLabel}"; } private string GenName(string prefix = "v") diff --git a/src/compiler/Nub.Lang/Nub.Lang.csproj b/src/compiler/Nub.Lang/Nub.Lang.csproj index 865633d..ee51912 100644 --- a/src/compiler/Nub.Lang/Nub.Lang.csproj +++ b/src/compiler/Nub.Lang/Nub.Lang.csproj @@ -6,6 +6,7 @@ net9.0 enable enable + true From 2a4401bab6be6799b8ae73cc1a66a85da8605cab Mon Sep 17 00:00:00 2001 From: nub31 Date: Fri, 16 May 2025 20:27:42 +0200 Subject: [PATCH 29/32] float literals --- example/program.nub | 15 +-- src/compiler/Nub.Lang/Backend/Generator.cs | 108 ++++++++++++++---- .../Nub.Lang/Frontend/Lexing/Lexer.cs | 35 +++++- 3 files changed, 121 insertions(+), 37 deletions(-) diff --git a/example/program.nub b/example/program.nub index 883fac5..2bb9c3e 100644 --- a/example/program.nub +++ b/example/program.nub @@ -2,18 +2,9 @@ import c; global func main(argc: i64, argv: i64) { printf("args: %d, starts at %p\n", argc, argv); - - a = 128; - b = 32768; - c = 2147483648; - d = 9223372036850000000; - - x = test(a, b, c, d); - - printf("%d\n", x); + test(12.1); } -func test(a: i8, b: i16, c: i32, d: i64): i64 { - printf("a: %d, b: %d, c: %d, d: %d\n", a, b, c, d); - return 12; +func test(a: f64) { + printf("%f\n", a); } \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index 325d0ce..625decc 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -1,4 +1,5 @@ -using System.Text; +using System.Globalization; +using System.Text; using Nub.Lang.Frontend.Parsing; namespace Nub.Lang.Backend; @@ -96,7 +97,7 @@ public class Generator case PrimitiveTypeKind.String: return "l"; case PrimitiveTypeKind.Any: - throw new NotSupportedException("Cannot convert any to QBE type"); + throw new NotSupportedException("Cannot convert 'any' type to QBE type"); case PrimitiveTypeKind.I32: case PrimitiveTypeKind.U32: return "w"; @@ -139,7 +140,7 @@ public class Generator case PrimitiveTypeKind.String: return "l"; case PrimitiveTypeKind.Any: - throw new NotSupportedException("Cannot convert any to QBE type"); + throw new NotSupportedException("Cannot convert 'any' type to QBE type"); case PrimitiveTypeKind.I32: case PrimitiveTypeKind.U32: return "w"; @@ -403,7 +404,8 @@ public class Generator private void GenerateStatementFuncCall(FuncCallStatementNode funcCall) { - _builder.AppendLine($" {GenerateFuncCall(funcCall.FuncCall)}"); + var call = GenerateFuncCall(funcCall.FuncCall); + _builder.AppendLine($" {call}"); } private void GenerateIf(IfNode ifStatement) @@ -778,6 +780,13 @@ public class Generator return literal.Literal; } + if (literal.LiteralType.Equals(NubPrimitiveType.F64)) + { + var value = double.Parse(literal.Literal, CultureInfo.InvariantCulture); + var bits = BitConverter.DoubleToInt64Bits(value); + return bits.ToString(); + } + if (literal.LiteralType.Equals(NubPrimitiveType.Bool)) { return bool.Parse(literal.Literal) ? "1" : "0"; @@ -788,7 +797,7 @@ public class Generator private string GenerateTypeConversion(string input, NubType inputType, NubType outputType) { - if (inputType.Equals(outputType)) + if (inputType.Equals(outputType) || outputType.Equals(NubPrimitiveType.Any)) { return input; } @@ -798,12 +807,6 @@ public class Generator throw new NotSupportedException("Casting is only supported for primitive types"); } - if (primitiveOutputType.Kind == PrimitiveTypeKind.Any) return input; - if (primitiveOutputType.Kind == PrimitiveTypeKind.Bool) - { - throw new NotSupportedException("Cannot cast any type to a bool"); - } - var outputLabel = GenName("c"); switch (primitiveInputType.Kind) @@ -818,6 +821,7 @@ public class Generator case PrimitiveTypeKind.I16: case PrimitiveTypeKind.U16: case PrimitiveTypeKind.I8: + case PrimitiveTypeKind.Any: case PrimitiveTypeKind.U8: return input; case PrimitiveTypeKind.F64: @@ -827,8 +831,9 @@ public class Generator _builder.AppendLine($" %{outputLabel} =s sltof {input}"); return $"%{outputLabel}"; case PrimitiveTypeKind.String: + case PrimitiveTypeKind.Bool: default: - throw new ArgumentOutOfRangeException(); + throw new NotSupportedException($"Casting from {primitiveInputType.Kind} to {primitiveOutputType.Kind} is not supported"); } case PrimitiveTypeKind.I32: switch (primitiveOutputType.Kind) @@ -853,8 +858,10 @@ public class Generator _builder.AppendLine($" %{outputLabel} =s swtof {input}"); return $"%{outputLabel}"; case PrimitiveTypeKind.String: + case PrimitiveTypeKind.Bool: + case PrimitiveTypeKind.Any: default: - throw new ArgumentOutOfRangeException(); + throw new NotSupportedException($"Casting from {primitiveInputType.Kind} to {primitiveOutputType.Kind} is not supported"); } case PrimitiveTypeKind.I16: switch (primitiveOutputType.Kind) @@ -887,8 +894,10 @@ public class Generator return $"%{outputLabel}"; } case PrimitiveTypeKind.String: + case PrimitiveTypeKind.Bool: + case PrimitiveTypeKind.Any: default: - throw new ArgumentOutOfRangeException(); + throw new NotSupportedException($"Casting from {primitiveInputType.Kind} to {primitiveOutputType.Kind} is not supported"); } case PrimitiveTypeKind.I8: switch (primitiveOutputType.Kind) @@ -921,8 +930,10 @@ public class Generator return $"%{outputLabel}"; } case PrimitiveTypeKind.String: + case PrimitiveTypeKind.Bool: + case PrimitiveTypeKind.Any: default: - throw new ArgumentOutOfRangeException(); + throw new NotSupportedException($"Casting from {primitiveInputType.Kind} to {primitiveOutputType.Kind} is not supported"); } case PrimitiveTypeKind.U64: switch (primitiveOutputType.Kind) @@ -943,8 +954,10 @@ public class Generator _builder.AppendLine($" %{outputLabel} =s ultof {input}"); return $"%{outputLabel}"; case PrimitiveTypeKind.String: + case PrimitiveTypeKind.Bool: + case PrimitiveTypeKind.Any: default: - throw new ArgumentOutOfRangeException(); + throw new NotSupportedException($"Casting from {primitiveInputType.Kind} to {primitiveOutputType.Kind} is not supported"); } case PrimitiveTypeKind.U32: switch (primitiveOutputType.Kind) @@ -969,8 +982,10 @@ public class Generator _builder.AppendLine($" %{outputLabel} =s uwtof {input}"); return $"%{outputLabel}"; case PrimitiveTypeKind.String: + case PrimitiveTypeKind.Bool: + case PrimitiveTypeKind.Any: default: - throw new ArgumentOutOfRangeException(); + throw new NotSupportedException($"Casting from {primitiveInputType.Kind} to {primitiveOutputType.Kind} is not supported"); } case PrimitiveTypeKind.U16: switch (primitiveOutputType.Kind) @@ -1003,8 +1018,10 @@ public class Generator return $"%{outputLabel}"; } case PrimitiveTypeKind.String: + case PrimitiveTypeKind.Bool: + case PrimitiveTypeKind.Any: default: - throw new ArgumentOutOfRangeException(); + throw new NotSupportedException($"Casting from {primitiveInputType.Kind} to {primitiveOutputType.Kind} is not supported"); } case PrimitiveTypeKind.U8: switch (primitiveOutputType.Kind) @@ -1037,9 +1054,59 @@ public class Generator return $"%{outputLabel}"; } case PrimitiveTypeKind.String: + case PrimitiveTypeKind.Bool: + case PrimitiveTypeKind.Any: default: - throw new ArgumentOutOfRangeException(); + throw new NotSupportedException($"Casting from {primitiveInputType.Kind} to {primitiveOutputType.Kind} is not supported"); } + case PrimitiveTypeKind.F64: + switch (primitiveOutputType.Kind) + { + case PrimitiveTypeKind.F64: + return input; + case PrimitiveTypeKind.F32: + _builder.AppendLine($" %{outputLabel} =s dtos {input}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.I64: + case PrimitiveTypeKind.I32: + case PrimitiveTypeKind.I16: + case PrimitiveTypeKind.I8: + case PrimitiveTypeKind.U64: + case PrimitiveTypeKind.U32: + case PrimitiveTypeKind.U16: + case PrimitiveTypeKind.U8: + case PrimitiveTypeKind.Bool: + case PrimitiveTypeKind.String: + case PrimitiveTypeKind.Any: + default: + throw new NotSupportedException($"Casting from {primitiveInputType.Kind} to {primitiveOutputType.Kind} is not supported"); + } + + case PrimitiveTypeKind.F32: + switch (primitiveOutputType.Kind) + { + case PrimitiveTypeKind.F64: + _builder.AppendLine($" %{outputLabel} =d stord {input}"); + return $"%{outputLabel}"; + case PrimitiveTypeKind.F32: + return input; + case PrimitiveTypeKind.I64: + case PrimitiveTypeKind.I32: + case PrimitiveTypeKind.I16: + case PrimitiveTypeKind.I8: + case PrimitiveTypeKind.U64: + case PrimitiveTypeKind.U32: + case PrimitiveTypeKind.U16: + case PrimitiveTypeKind.U8: + case PrimitiveTypeKind.Bool: + case PrimitiveTypeKind.String: + case PrimitiveTypeKind.Any: + default: + throw new NotSupportedException($"Casting from {primitiveInputType.Kind} to {primitiveOutputType.Kind} is not supported"); + } + case PrimitiveTypeKind.Bool: + case PrimitiveTypeKind.String: + case PrimitiveTypeKind.Any: default: throw new NotSupportedException($"Casting from {primitiveInputType.Kind} to {primitiveOutputType.Kind} is not supported"); } @@ -1090,7 +1157,8 @@ public class Generator private string GenerateExpressionFuncCall(FuncCallExpressionNode funcCall) { var outputLabel = GenName(); - _builder.AppendLine($" %{outputLabel} ={SQT(funcCall.Type)} {GenerateFuncCall(funcCall.FuncCall)}"); + var call = GenerateFuncCall(funcCall.FuncCall); + _builder.AppendLine($" %{outputLabel} ={SQT(funcCall.Type)} {call}"); return $"%{outputLabel}"; } diff --git a/src/compiler/Nub.Lang/Frontend/Lexing/Lexer.cs b/src/compiler/Nub.Lang/Frontend/Lexing/Lexer.cs index cbf544e..be654a8 100644 --- a/src/compiler/Nub.Lang/Frontend/Lexing/Lexer.cs +++ b/src/compiler/Nub.Lang/Frontend/Lexing/Lexer.cs @@ -94,16 +94,41 @@ public class Lexer if (char.IsDigit(current.Value)) { + var isFloat = false; var buffer = string.Empty; - while (current.HasValue && char.IsDigit(current.Value)) + while (current.HasValue) { - buffer += current.Value; - Next(); + if (current.Value == '.') + { + if (isFloat) + { + throw new Exception("More than one period found in float literal"); + } + isFloat = true; + buffer += current.Value; + Next(); current = Peek(); + } + else if (char.IsDigit(current.Value)) + { + buffer += current.Value; + Next(); + current = Peek(); + } + else if (current.Value == 'f') + { + isFloat = true; + Next(); + break; + } + else + { + break; + } } - - return new LiteralToken(NubPrimitiveType.I64, buffer); + + return new LiteralToken(isFloat ? NubPrimitiveType.F64 : NubPrimitiveType.I64, buffer); } // TODO: Revisit this From 57ccd0a38acdebceece5da0c22e682d2ce490afc Mon Sep 17 00:00:00 2001 From: nub31 Date: Fri, 16 May 2025 20:46:38 +0200 Subject: [PATCH 30/32] modifiers --- .../Nub.Lang/Frontend/Lexing/Lexer.cs | 13 ++- .../Nub.Lang/Frontend/Lexing/ModifierToken.cs | 12 +++ .../Nub.Lang/Frontend/Lexing/SymbolToken.cs | 2 - .../Nub.Lang/Frontend/Parsing/Parser.cs | 101 +++++++----------- 4 files changed, 64 insertions(+), 64 deletions(-) create mode 100644 src/compiler/Nub.Lang/Frontend/Lexing/ModifierToken.cs diff --git a/src/compiler/Nub.Lang/Frontend/Lexing/Lexer.cs b/src/compiler/Nub.Lang/Frontend/Lexing/Lexer.cs index be654a8..e36c490 100644 --- a/src/compiler/Nub.Lang/Frontend/Lexing/Lexer.cs +++ b/src/compiler/Nub.Lang/Frontend/Lexing/Lexer.cs @@ -5,8 +5,6 @@ public class Lexer private static readonly Dictionary Keywords = new() { ["func"] = Symbol.Func, - ["global"] = Symbol.Global, - ["extern"] = Symbol.Extern, ["import"] = Symbol.Import, ["if"] = Symbol.If, ["else"] = Symbol.Else, @@ -17,6 +15,12 @@ public class Lexer ["new"] = Symbol.New, ["struct"] = Symbol.Struct, }; + + private static readonly Dictionary Modifers = new() + { + ["global"] = Modifier.Global, + ["extern"] = Modifier.Extern, + }; private static readonly Dictionary Chians = new() { @@ -84,6 +88,11 @@ public class Lexer return new SymbolToken(keywordSymbol); } + if (Modifers.TryGetValue(buffer, out var modifer)) + { + return new ModifierToken(modifer); + } + if (buffer is "true" or "false") { return new LiteralToken(NubPrimitiveType.Bool, buffer); diff --git a/src/compiler/Nub.Lang/Frontend/Lexing/ModifierToken.cs b/src/compiler/Nub.Lang/Frontend/Lexing/ModifierToken.cs new file mode 100644 index 0000000..3446034 --- /dev/null +++ b/src/compiler/Nub.Lang/Frontend/Lexing/ModifierToken.cs @@ -0,0 +1,12 @@ +namespace Nub.Lang.Frontend.Lexing; + +public class ModifierToken(Modifier symbol) : Token +{ + public Modifier Modifier { get; } = symbol; +} + +public enum Modifier +{ + Extern, + Global +} \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Frontend/Lexing/SymbolToken.cs b/src/compiler/Nub.Lang/Frontend/Lexing/SymbolToken.cs index ccaa7ec..068b52f 100644 --- a/src/compiler/Nub.Lang/Frontend/Lexing/SymbolToken.cs +++ b/src/compiler/Nub.Lang/Frontend/Lexing/SymbolToken.cs @@ -9,8 +9,6 @@ public enum Symbol { Whitespace, Import, - Extern, - Global, Func, Return, If, diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs index 45567a4..b35a595 100644 --- a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs @@ -35,18 +35,23 @@ public class Parser private DefinitionNode ParseDefinition() { + List modifiers = []; + + while (TryExpectModifier(out var modifier)) + { + modifiers.Add(modifier); + } + var keyword = ExpectSymbol(); return keyword.Symbol switch { - Symbol.Func => ParseFuncDefinition(), - Symbol.Global => ParseGlobalFuncDefinition(), - Symbol.Extern => ParseExternFuncDefinition(), - Symbol.Struct => ParseStruct(), + Symbol.Func => ParseFuncDefinition(modifiers), + Symbol.Struct => ParseStruct(modifiers), _ => throw new Exception("Unexpected symbol: " + keyword.Symbol) }; } - private LocalFuncDefinitionNode ParseFuncDefinition() + private DefinitionNode ParseFuncDefinition(List modifiers) { var name = ExpectIdentifier(); List parameters = []; @@ -66,64 +71,27 @@ public class Parser returnType = ParseType(); } + if (modifiers.Remove(Modifier.Extern)) + { + if (modifiers.Count != 0) + { + throw new Exception($"Modifiers: {string.Join(", ", modifiers)} is not valid for an extern function"); + } + ExpectSymbol(Symbol.Semicolon); + return new ExternFuncDefinitionNode(name.Value, parameters, returnType); + } + var body = ParseBlock(); - - return new LocalFuncDefinitionNode(name.Value, parameters, body, returnType, false); + var global = modifiers.Remove(Modifier.Global); + + if (modifiers.Count != 0) + { + throw new Exception($"Modifiers: {string.Join(", ", modifiers)} is not valid for a local function"); + } + return new LocalFuncDefinitionNode(name.Value, parameters, body, returnType, global); } - private LocalFuncDefinitionNode ParseGlobalFuncDefinition() - { - ExpectSymbol(Symbol.Func); - var name = ExpectIdentifier(); - List parameters = []; - ExpectSymbol(Symbol.OpenParen); - if (!TryExpectSymbol(Symbol.CloseParen)) - { - while (!TryExpectSymbol(Symbol.CloseParen)) - { - parameters.Add(ParseFuncParameter()); - TryExpectSymbol(Symbol.Comma); - } - } - - var returnType = Optional.Empty(); - if (TryExpectSymbol(Symbol.Colon)) - { - returnType = ParseType(); - } - - var body = ParseBlock(); - - return new LocalFuncDefinitionNode(name.Value, parameters, body, returnType, true); - } - - private ExternFuncDefinitionNode ParseExternFuncDefinition() - { - ExpectSymbol(Symbol.Func); - var name = ExpectIdentifier(); - List parameters = []; - ExpectSymbol(Symbol.OpenParen); - if (!TryExpectSymbol(Symbol.CloseParen)) - { - while (!TryExpectSymbol(Symbol.CloseParen)) - { - parameters.Add(ParseFuncParameter()); - TryExpectSymbol(Symbol.Comma); - } - } - - var returnType = Optional.Empty(); - if (TryExpectSymbol(Symbol.Colon)) - { - returnType = ParseType(); - } - - ExpectSymbol(Symbol.Semicolon); - - return new ExternFuncDefinitionNode(name.Value, parameters, returnType); - } - - private StructDefinitionNode ParseStruct() + private StructDefinitionNode ParseStruct(List modifiers) { var name = ExpectIdentifier().Value; @@ -503,6 +471,19 @@ public class Parser return result; } + private bool TryExpectModifier(out Modifier modifier) + { + if (Peek() is { HasValue: true, Value: ModifierToken modifierToken }) + { + modifier = modifierToken.Modifier; + Next(); + return true; + } + + modifier = default; + return false; + } + private IdentifierToken ExpectIdentifier() { var token = ExpectToken(); From ecd0e01aacf0d9d77544a3eb9bbd9e830a64ddeb Mon Sep 17 00:00:00 2001 From: nub31 Date: Fri, 16 May 2025 21:09:05 +0200 Subject: [PATCH 31/32] ... --- example/c/bindings.nub | 4 +-- example/program.nub | 9 +++--- src/compiler/Nub.Lang/Backend/Generator.cs | 30 ++++++++----------- .../Nub.Lang/Frontend/Parsing/Parser.cs | 25 ++-------------- 4 files changed, 22 insertions(+), 46 deletions(-) diff --git a/example/c/bindings.nub b/example/c/bindings.nub index 3c4ba44..9738661 100644 --- a/example/c/bindings.nub +++ b/example/c/bindings.nub @@ -1,2 +1,2 @@ -extern func puts(str: string); -extern func printf(fmt: string, ...args: any); \ No newline at end of file +extern func puts(str: string) +extern func printf(fmt: string, ...args: any) \ No newline at end of file diff --git a/example/program.nub b/example/program.nub index 2bb9c3e..8f3a332 100644 --- a/example/program.nub +++ b/example/program.nub @@ -1,10 +1,11 @@ -import c; +import c global func main(argc: i64, argv: i64) { - printf("args: %d, starts at %p\n", argc, argv); - test(12.1); + printf("args: %d, starts at %p\n", argc, argv) + test(12.1) + return 23 } func test(a: f64) { - printf("%f\n", a); + printf("%f\n", a) } \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index 625decc..f45d86a 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -229,7 +229,7 @@ public class Generator { _builder.Append($"{FQT(node.ReturnType.Value)} "); } - else + else if (!node.ReturnType.HasValue && node.Name == "main") { _builder.Append("l "); } @@ -237,18 +237,7 @@ public class Generator _builder.Append('$'); _builder.Append(node.Name); - var parameterStrings = new List(); - foreach (var parameter in node.Parameters) - { - if (parameter.Variadic) - { - parameterStrings.Add("..."); - } - else - { - parameterStrings.Add($"{FQT(parameter.Type)} %{parameter.Name}"); - } - } + var parameterStrings = node.Parameters.Select(parameter => parameter.Variadic ? "..." : $"{FQT(parameter.Type)} %{parameter.Name}"); _builder.AppendLine($"({string.Join(", ", parameterStrings)}) {{"); _builder.AppendLine("@start"); @@ -285,9 +274,17 @@ public class Generator } GenerateBlock(node.Body); - if (!node.ReturnType.HasValue) + + if (node.Body.Statements.Last() is not ReturnNode) { - _builder.AppendLine(" ret 0"); + if (!node.ReturnType.HasValue && node.Name == "main") + { + _builder.AppendLine(" ret 0"); + } + else if (!node.ReturnType.HasValue) + { + _builder.AppendLine(" ret"); + } } _builder.AppendLine("}"); @@ -758,8 +755,7 @@ public class Generator } } - throw new NotSupportedException( - $"Binary operator {binaryExpression.Operator} for types left: {binaryExpression.Left.Type}, right: {binaryExpression.Right.Type} not supported"); + throw new NotSupportedException($"Binary operator {binaryExpression.Operator} for types left: {binaryExpression.Left.Type}, right: {binaryExpression.Right.Type} not supported"); } private string GenerateIdentifier(IdentifierNode identifier) diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs index b35a595..280926b 100644 --- a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs @@ -21,7 +21,6 @@ public class Parser if (TryExpectSymbol(Symbol.Import)) { var name = ExpectIdentifier(); - TryExpectSymbol(Symbol.Semicolon); imports.Add(name.Value); } else @@ -77,7 +76,6 @@ public class Parser { throw new Exception($"Modifiers: {string.Join(", ", modifiers)} is not valid for an extern function"); } - ExpectSymbol(Symbol.Semicolon); return new ExternFuncDefinitionNode(name.Value, parameters, returnType); } @@ -112,8 +110,6 @@ public class Parser variableValue = ParseExpression(); } - ExpectSymbol(Symbol.Semicolon); - variables.Add(new StructField(variableName, variableType, variableValue)); } @@ -156,14 +152,11 @@ public class Parser TryExpectSymbol(Symbol.Comma); } - ExpectSymbol(Symbol.Semicolon); - return new FuncCallStatementNode(new FuncCall(identifier.Value, parameters)); } case Symbol.Assign: { var value = ParseExpression(); - ExpectSymbol(Symbol.Semicolon); return new VariableAssignmentNode(identifier.Value, value); } default: @@ -179,8 +172,8 @@ public class Parser Symbol.Return => ParseReturn(), Symbol.If => ParseIf(), Symbol.While => ParseWhile(), - Symbol.Break => ParseBreak(), - Symbol.Continue => ParseContinue(), + Symbol.Break => new BreakNode(), + Symbol.Continue => new ContinueNode(), _ => throw new Exception($"Unexpected symbol {symbol.Symbol}") }; } @@ -197,7 +190,6 @@ public class Parser if (!TryExpectSymbol(Symbol.Semicolon)) { value = ParseExpression(); - ExpectSymbol(Symbol.Semicolon); } return new ReturnNode(value); @@ -226,18 +218,6 @@ public class Parser return new WhileNode(condition, body); } - private BreakNode ParseBreak() - { - ExpectSymbol(Symbol.Semicolon); - return new BreakNode(); - } - - private ContinueNode ParseContinue() - { - ExpectSymbol(Symbol.Semicolon); - return new ContinueNode(); - } - private ExpressionNode ParseExpression(int precedence = 0) { var left = ParsePrimaryExpression(); @@ -349,7 +329,6 @@ public class Parser var name = ExpectIdentifier().Value; ExpectSymbol(Symbol.Assign); var value = ParseExpression(); - TryExpectSymbol(Symbol.Semicolon); initializers.Add(name, value); } From 1676806ef023dfdc776470fd99fd0c13a00190f3 Mon Sep 17 00:00:00 2001 From: nub31 Date: Fri, 16 May 2025 21:27:27 +0200 Subject: [PATCH 32/32] explicit variable types --- example/program.nub | 7 ++----- src/compiler/Nub.Lang/Backend/Generator.cs | 20 +++++++++++++++++-- .../Nub.Lang/Frontend/Parsing/Parser.cs | 9 ++++++++- .../Parsing/VariableAssignmentNode.cs | 3 ++- .../Frontend/Typing/ExpressionTyper.cs | 2 +- 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/example/program.nub b/example/program.nub index 8f3a332..eb398cf 100644 --- a/example/program.nub +++ b/example/program.nub @@ -2,10 +2,7 @@ import c global func main(argc: i64, argv: i64) { printf("args: %d, starts at %p\n", argc, argv) - test(12.1) - return 23 -} -func test(a: f64) { - printf("%f\n", a) + x: i8 = 320000 + printf("%d\n", x) } \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index f45d86a..9e72769 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -14,6 +14,7 @@ public class Generator private readonly Stack _breakLabels = new(); private readonly Stack _continueLabels = new(); private bool _codeIsReachable = true; + private LocalFuncDefinitionNode? _currentFuncDefininition; public Generator(List definitions) { @@ -217,6 +218,7 @@ public class Generator private void GenerateFuncDefinition(LocalFuncDefinitionNode node) { + _currentFuncDefininition = node; _variables.Clear(); if (node.Global) @@ -288,6 +290,7 @@ public class Generator } _builder.AppendLine("}"); + _currentFuncDefininition = null; } private void GenerateStructDefinition(StructDefinitionNode structDefinition) @@ -433,8 +436,14 @@ public class Generator { if (@return.Value.HasValue) { + if (!_currentFuncDefininition!.ReturnType.HasValue) + { + throw new Exception("Cannot return a value when function does not have a return value"); + } + var result = GenerateExpression(@return.Value.Value); - _builder.AppendLine($" ret {result}"); + var converted = GenerateTypeConversion(result, @return.Value.Value.Type, _currentFuncDefininition.ReturnType.Value); + _builder.AppendLine($" ret {converted}"); } else { @@ -445,10 +454,17 @@ public class Generator private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment) { var result = GenerateExpression(variableAssignment.Value); + var variableType = variableAssignment.Value.Type; + + if (variableAssignment.ExplicitType.HasValue) + { + result = GenerateTypeConversion(result, variableType, variableAssignment.ExplicitType.Value); + variableType = variableAssignment.ExplicitType.Value; + } _variables[variableAssignment.Name] = new Variable { Identifier = result, - Type = variableAssignment.Value.Type + Type = variableType }; } diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs index 280926b..952efed 100644 --- a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs @@ -157,7 +157,14 @@ public class Parser case Symbol.Assign: { var value = ParseExpression(); - return new VariableAssignmentNode(identifier.Value, value); + return new VariableAssignmentNode(identifier.Value, Optional.Empty(), value); + } + case Symbol.Colon: + { + var type = ParseType(); + ExpectSymbol(Symbol.Assign); + var value = ParseExpression(); + return new VariableAssignmentNode(identifier.Value,type, value); } default: { diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/VariableAssignmentNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/VariableAssignmentNode.cs index e76cb63..2964d04 100644 --- a/src/compiler/Nub.Lang/Frontend/Parsing/VariableAssignmentNode.cs +++ b/src/compiler/Nub.Lang/Frontend/Parsing/VariableAssignmentNode.cs @@ -1,7 +1,8 @@ namespace Nub.Lang.Frontend.Parsing; -public class VariableAssignmentNode(string name, ExpressionNode value) : StatementNode +public class VariableAssignmentNode(string name, Optional explicitType, ExpressionNode value) : StatementNode { public string Name { get; } = name; + public Optional ExplicitType { get; } = explicitType; public ExpressionNode Value { get; } = value; } \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs b/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs index 14e2f64..09fb4b3 100644 --- a/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs +++ b/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs @@ -143,7 +143,7 @@ public class ExpressionTyper private void PopulateVariableAssignment(VariableAssignmentNode variableAssignment) { PopulateExpression(variableAssignment.Value); - _variables.Push(new Variable(variableAssignment.Name, variableAssignment.Value.Type)); + _variables.Push(new Variable(variableAssignment.Name, variableAssignment.ExplicitType.HasValue ? variableAssignment.ExplicitType.Value : variableAssignment.Value.Type)); } private void PopulateVariableReassignment(VariableAssignmentNode variableAssignment)