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