From b67d33455d1dc00131e1f097615e9ba921fab225 Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 5 May 2025 16:05:27 +0200 Subject: [PATCH] ... --- 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