Merge pull request #1 from NUB31/qbe

Qbe
This commit is contained in:
Oliver Stene
2025-05-16 21:32:39 +02:00
committed by GitHub
79 changed files with 1617 additions and 1951 deletions

View File

@@ -1,15 +1,21 @@
#!/bin/sh
set -e
mkdir -p out
dotnet run --project lang/Nub.Lang example out/out.asm
echo "setup..."
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
dotnet publish -c Release src/compiler/Nub.Lang
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
echo "compiling..."
nasm -g -felf64 out/out.asm -o out/out.o
nub example out/out.qbe
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
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/runtime.o out/out.o
echo "done..."

View File

@@ -1,4 +1,5 @@
#!/bin/sh
set -e
./clean.sh
./build.sh
gdb -tui ./out/program

2
example/c/bindings.nub Normal file
View File

@@ -0,0 +1,2 @@
extern func puts(str: string)
extern func printf(fmt: string, ...args: any)

View File

@@ -1 +0,0 @@
extern func arr_size(array: Array<Any>): int64;

View File

@@ -1,38 +0,0 @@
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));
}
func print(value1: int64) {
print(itoa(value1));
}
func print(value2: bool) {
if value2 {
print("true");
} else {
print("false");
}
}
func println() {
print("\n");
}
func println(msg: String) {
print(msg);
println();
}
func println(value3: bool) {
print(value3);
println();
}
func println(value4: int64) {
print(value4);
println();
}

View File

@@ -1,2 +0,0 @@
extern func str_len(msg: String): int64;
extern func itoa(value: int64): String;

View File

@@ -1,16 +1,8 @@
import "core";
import c
struct Human {
let name: String;
let age: int64;
}
global func main(argc: i64, argv: i64) {
printf("args: %d, starts at %p\n", argc, argv)
func main() {
while true {
let x = new Human
{
name = "test",
age = 34958743
};
}
x: i8 = 320000
printf("%d\n", x)
}

View File

@@ -1,736 +0,0 @@
using System.Text;
using Nub.Lang.Frontend.Parsing;
namespace Nub.Lang.Backend.Custom;
public class Generator
{
private const string Entrypoint = "main";
private const bool ZeroBasedIndexing = false;
private readonly List<DefinitionNode> _definitions;
private readonly SymbolTable _symbolTable;
private readonly StringBuilder _builder;
private readonly LabelFactory _labelFactory;
private readonly Stack<(string StartLabel, string EndLabel)> _loops;
public Generator(List<DefinitionNode> definitions)
{
_definitions = definitions;
_builder = new StringBuilder();
_labelFactory = new LabelFactory();
_symbolTable = new SymbolTable(_labelFactory);
_loops = [];
foreach (var globalVariableDefinition in definitions.OfType<GlobalVariableDefinitionNode>())
{
_symbolTable.DefineGlobalVariable(globalVariableDefinition);
}
foreach (var funcDefinitionNode in definitions.OfType<ExternFuncDefinitionNode>())
{
_symbolTable.DefineFunc(funcDefinitionNode);
}
foreach (var funcDefinitionNode in definitions.OfType<LocalFuncDefinitionNode>())
{
_symbolTable.DefineFunc(funcDefinitionNode);
}
}
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<ExternFuncDefinitionNode>())
{
_builder.AppendLine($"extern {externFuncDefinition.Name}");
}
_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<LocalFuncDefinitionNode>())
{
_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<string, string> completed = [];
foreach (var globalVariableDefinition in _definitions.OfType<GlobalVariableDefinitionNode>())
{
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<string, string> completed)
{
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.Type is not PrimitiveType primitiveType)
{
throw new NotSupportedException("Global variable literals must be of a primitive type");
}
return primitiveType.Kind switch
{
PrimitiveTypeKind.Bool => bool.Parse(literal.Literal) ? "1" : "0",
PrimitiveTypeKind.Int64 or PrimitiveTypeKind.Int32 => $"{literal.Literal}",
_ => throw new ArgumentOutOfRangeException()
};
}
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)
{
foreach (var statement in block.Statements)
{
GenerateStatement(statement, func);
}
}
private void GenerateStatement(StatementNode statement, LocalFunc func)
{
switch (statement)
{
case ArrayIndexAssignmentNode arrayIndexAssignment:
GenerateArrayIndexAssignment(arrayIndexAssignment, func);
break;
case BreakNode:
GenerateBreak();
break;
case ContinueNode:
GenerateContinue();
break;
case FuncCallStatementNode funcCallStatement:
GenerateFuncCall(funcCallStatement.FuncCall, func);
break;
case IfNode ifStatement:
GenerateIf(ifStatement, func);
break;
case ReturnNode @return:
GenerateReturn(@return, func);
break;
case SyscallStatementNode syscallStatement:
GenerateSyscall(syscallStatement.Syscall, func);
break;
case VariableAssignmentNode variableAssignment:
GenerateVariableAssignment(variableAssignment, func);
break;
case VariableReassignmentNode variableReassignment:
GenerateVariableReassignment(variableReassignment, func);
break;
case WhileNode whileStatement:
GenerateWhile(whileStatement, func);
break;
default:
throw new ArgumentOutOfRangeException(nameof(statement));
}
}
private void GenerateBreak()
{
_builder.AppendLine($" jmp {_loops.Peek().EndLabel}");
}
private void GenerateContinue()
{
_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();
GenerateIf(ifStatement, endLabel, func);
_builder.AppendLine($"{endLabel}:");
}
private void GenerateIf(IfNode ifStatement, string endLabel, LocalFunc 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)
{
if (@return.Value.HasValue)
{
GenerateExpression(@return.Value.Value, func);
}
_builder.AppendLine($" jmp {func.EndLabel}");
}
private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment, LocalFunc 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)
{
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)
{
switch (expression)
{
case ArrayIndexAccessNode arrayIndexAccess:
GenerateArrayIndexAccess(arrayIndexAccess, func);
break;
case ArrayInitializerNode arrayInitializer:
GenerateArrayInitializer(arrayInitializer);
break;
case BinaryExpressionNode binaryExpression:
GenerateBinaryExpression(binaryExpression, func);
break;
case FuncCallExpressionNode funcCallExpression:
GenerateFuncCall(funcCallExpression.FuncCall, func);
break;
case IdentifierNode identifier:
GenerateIdentifier(identifier, func);
break;
case LiteralNode literal:
GenerateLiteral(literal);
break;
case StructInitializerNode structInitializer:
GenerateStructInitializer(structInitializer, func);
break;
case StructMemberAccessorNode structMemberAccessor:
GenerateStructMemberAccessor(structMemberAccessor, func);
break;
case SyscallExpressionNode syscallExpression:
GenerateSyscall(syscallExpression.Syscall, func);
break;
default:
throw new ArgumentOutOfRangeException(nameof(expression));
}
}
private void GenerateStructMemberAccessor(StructMemberAccessorNode structMemberAccessor, LocalFunc func)
{
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;
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<StructDefinitionNode>().FirstOrDefault(sd => sd.Name == prevMemberStructType.Name);
if (structDefinition == null)
{
throw new Exception($"Struct {prevMemberStructType} 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]}");
}
var offset = structDefinition.Members.IndexOf(member);
_builder.AppendLine($" mov rax, [rax + {offset * 8}]");
prevMemberType = member.Type;
}
}
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);
_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(Type type)
{
switch (type)
{
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));
}
}
private void GenerateBinaryAddition(Type type)
{
if (type is not PrimitiveType primitiveType)
{
throw new InvalidOperationException("Addition can only be done on primitive types");
}
switch (primitiveType.Kind)
{
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}");
}
}
private void GenerateBinarySubtraction(Type type)
{
if (type is not PrimitiveType primitiveType)
{
throw new InvalidOperationException("Subtraction can only be done on primitive types");
}
switch (primitiveType.Kind)
{
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}");
}
}
private void GenerateBinaryMultiplication(Type type)
{
if (type is not PrimitiveType primitiveType)
{
throw new InvalidOperationException("Multiplication can only be done on primitive types");
}
switch (primitiveType.Kind)
{
case PrimitiveTypeKind.Int64:
_builder.AppendLine(" imul rcx");
break;
case PrimitiveTypeKind.Int32:
_builder.AppendLine(" imul ecx");
break;
default:
throw new InvalidOperationException($"Invalid type {primitiveType.Kind}");
}
}
private void GenerateBinaryDivision(Type type)
{
if (type is not PrimitiveType primitiveType)
{
throw new InvalidOperationException("Division can only be done on primitive types");
}
switch (primitiveType.Kind)
{
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}");
}
}
private void GenerateIdentifier(IdentifierNode identifier, LocalFunc 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)
{
switch (literal.Type)
{
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();
}
}
private void GenerateStructInitializer(StructInitializerNode structInitializer, LocalFunc func)
{
var structDefinition = _definitions
.OfType<StructDefinitionNode>()
.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");
}
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");
}
}

View File

@@ -1,7 +0,0 @@
namespace Nub.Lang.Backend.Custom;
public class LabelFactory
{
private int _index;
public string Create() => $"label{++_index}";
}

View File

@@ -1,258 +0,0 @@
using Nub.Lang.Frontend.Parsing;
namespace Nub.Lang.Backend.Custom;
public class SymbolTable
{
private readonly List<Func> _funcDefinitions = [];
private readonly List<GlobalVariable> _globalVariables = [];
private readonly LabelFactory _labelFactory;
public readonly Dictionary<string, string> Strings = [];
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)
{
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<Variable>(ResolveFuncVariables(localFuncDefinition)).ToList()));
}
private static List<LocalVariable> ResolveFuncVariables(LocalFuncDefinitionNode localFuncDefinition)
{
var offset = 0;
List<LocalVariable> 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<LocalVariable> variables, int offset)
{
foreach (var statement in block.Statements)
{
switch (statement)
{
case IfNode ifStatement:
{
offset = ResolveBlockVariables(ifStatement.Body, variables, offset);
if (ifStatement.Else.HasValue)
{
ifStatement.Else.Value.Match
(
elseIfStatement => offset = ResolveBlockVariables(elseIfStatement.Body, variables, offset),
elseStatement => offset = ResolveBlockVariables(elseStatement, variables, offset)
);
}
break;
}
case WhileNode whileStatement:
{
offset = ResolveBlockVariables(whileStatement.Body, variables, offset);
break;
}
case VariableAssignmentNode variableAssignment:
{
offset += 8;
variables.Add(new LocalVariable(variableAssignment.Name, variableAssignment.Value.Type, offset));
break;
}
}
}
return offset;
}
public Func ResolveFunc(string name, List<Type> parameterTypes)
{
var func = _funcDefinitions.FirstOrDefault(f => f.SignatureMatches(name, parameterTypes));
if (func == null)
{
throw new Exception($"Func {name}({string.Join(", ", parameterTypes)}) is not defined");
}
return func;
}
public LocalFunc ResolveLocalFunc(string name, List<Type> parameterTypes)
{
var func = ResolveFunc(name, parameterTypes);
if (func is not LocalFunc localFunc)
{
throw new Exception($"Func {func} is not a local func");
}
return localFunc;
}
public ExternFunc ResolveExternFunc(string name, List<Type> 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 variable;
}
}
public abstract class Variable(string name, Type type)
{
public string Name { get; } = name;
public Type Type { get; } = type;
public override string ToString() => $"{Name}: {Type}";
}
public class LocalVariable(string name, Type type, int offset) : Variable(name, type)
{
public int Offset { get; } = offset;
}
public class GlobalVariable(string name, Type type, string identifier) : Variable(name, type)
{
public string Identifier { get; } = identifier;
}
public abstract class Func
{
protected Func(string name, string startLabel, List<FuncParameter> parameters, Optional<Type> returnType)
{
Name = name;
Parameters = parameters;
ReturnType = returnType;
StartLabel = startLabel;
}
public string Name { get; }
public string StartLabel { get; }
public List<FuncParameter> Parameters { get; }
public Optional<Type> ReturnType { get; }
public bool SignatureMatches(string name, List<Type> 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;
}
return true;
}
public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}";
}
public class ExternFunc : Func
{
public ExternFunc(string name, string startLabel, List<FuncParameter> parameters, Optional<Type> returnType) : base(name, startLabel, parameters, returnType)
{
}
}
public class LocalFunc : Func
{
public LocalFunc(string name, string startLabel, string endLabel, List<FuncParameter> parameters, Optional<Type> returnType, List<Variable> variables) : base(name, startLabel, parameters, returnType)
{
EndLabel = endLabel;
Variables = variables;
}
public string EndLabel { get; }
public List<Variable> Variables { get; }
public int StackAllocation => Variables.OfType<LocalVariable>().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;
}
}

View File

@@ -1,7 +0,0 @@
namespace Nub.Lang.Frontend.Lexing;
public class LiteralToken(Type type, string value) : Token
{
public Type Type { get; } = type;
public string Value { get; } = value;
}

View File

@@ -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}]";
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -1,7 +0,0 @@
namespace Nub.Lang.Frontend.Parsing;
public class LiteralNode(string literal, Type type) : ExpressionNode
{
public string Literal { get; } = literal;
public Type LiteralType { get; } = type;
}

View File

@@ -1,7 +0,0 @@
namespace Nub.Lang.Frontend.Parsing;
public class StructDefinitionNode(string name, List<StructMember> members) : DefinitionNode
{
public string Name { get; } = name;
public List<StructMember> Members { get; } = members;
}

View File

@@ -1,7 +0,0 @@
namespace Nub.Lang.Frontend.Parsing;
public class StructInitializerNode(StructType structType, Dictionary<string, ExpressionNode> initializers) : ExpressionNode
{
public StructType StructType { get; } = structType;
public Dictionary<string, ExpressionNode> Initializers { get; } = initializers;
}

View File

@@ -1,6 +0,0 @@
namespace Nub.Lang.Frontend.Parsing;
public class StructMemberAccessorNode(List<string> members) : ExpressionNode
{
public List<string> Members { get; } = members;
}

View File

@@ -1,6 +0,0 @@
namespace Nub.Lang.Frontend.Parsing;
public class Syscall(List<ExpressionNode> parameters)
{
public List<ExpressionNode> Parameters { get; } = parameters;
}

View File

@@ -1,6 +0,0 @@
namespace Nub.Lang.Frontend.Parsing;
public class SyscallExpressionNode(Syscall syscall) : ExpressionNode
{
public Syscall Syscall { get; } = syscall;
}

View File

@@ -1,6 +0,0 @@
namespace Nub.Lang.Frontend.Parsing;
public class SyscallStatementNode(Syscall syscall) : StatementNode
{
public Syscall Syscall { get; } = syscall;
}

View File

@@ -1,7 +0,0 @@
namespace Nub.Lang.Frontend.Parsing;
public class VariableAssignmentNode(string name, ExpressionNode value) : StatementNode
{
public string Name { get; } = name;
public ExpressionNode Value { get; } = value;
}

View File

@@ -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;
}

View File

@@ -1,9 +0,0 @@
namespace Nub.Lang;
public class FuncParameter(string name, Type type)
{
public string Name { get; } = name;
public Type Type { get; } = type;
public override string ToString() => $"{Name}: {Type}";
}

View File

@@ -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;
}

View File

@@ -2,7 +2,8 @@
<project version="4">
<component name="UserContentModel">
<attachedFolders>
<Path>../std</Path>
<Path>../../example</Path>
<Path>../core</Path>
</attachedFolders>
<explicitIncludes />
<explicitExcludes />

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>

File diff suppressed because it is too large Load Diff

View File

@@ -5,9 +5,7 @@ public class Lexer
private static readonly Dictionary<string, Symbol> Keywords = new()
{
["func"] = Symbol.Func,
["extern"] = Symbol.Extern,
["import"] = Symbol.Import,
["let"] = Symbol.Let,
["if"] = Symbol.If,
["else"] = Symbol.Else,
["while"] = Symbol.While,
@@ -18,6 +16,12 @@ public class Lexer
["struct"] = Symbol.Struct,
};
private static readonly Dictionary<string, Modifier> Modifers = new()
{
["global"] = Modifier.Global,
["extern"] = Modifier.Extern,
};
private static readonly Dictionary<char[], Symbol> Chians = new()
{
[['=', '=']] = Symbol.Equal,
@@ -84,9 +88,14 @@ 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(new PrimitiveType(PrimitiveTypeKind.Bool), buffer);
return new LiteralToken(NubPrimitiveType.Bool, buffer);
}
return new IdentifierToken(buffer);
@@ -94,16 +103,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)
{
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(new PrimitiveType(PrimitiveTypeKind.Int64), buffer);
return new LiteralToken(isFloat ? NubPrimitiveType.F64 : NubPrimitiveType.I64, buffer);
}
// TODO: Revisit this
@@ -148,7 +182,7 @@ public class Lexer
buffer += current.Value;
}
return new LiteralToken(new StringType(), buffer);
return new LiteralToken(NubPrimitiveType.String, buffer);
}
if (char.IsWhiteSpace(current.Value))

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Frontend.Lexing;
public class LiteralToken(NubType type, string value) : Token
{
public NubType Type { get; } = type;
public string Value { get; } = value;
}

View File

@@ -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
}

View File

@@ -9,10 +9,8 @@ public enum Symbol
{
Whitespace,
Import,
Extern,
Func,
Return,
Let,
If,
Else,
While,

View File

@@ -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;

View File

@@ -1,10 +1,10 @@
namespace Nub.Lang.Frontend.Parsing;
public class ExternFuncDefinitionNode(string name, List<FuncParameter> parameters, Optional<Type> returnType) : DefinitionNode
public class ExternFuncDefinitionNode(string name, List<FuncParameter> parameters, Optional<NubType> returnType) : DefinitionNode
{
public string Name { get; } = name;
public List<FuncParameter> Parameters { get; } = parameters;
public Optional<Type> ReturnType { get; } = returnType;
public Optional<NubType> ReturnType { get; } = returnType;
public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}";
}

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Frontend.Parsing;
public class LiteralNode(string literal, NubType type) : ExpressionNode
{
public string Literal { get; } = literal;
public NubType LiteralType { get; } = type;
}

View File

@@ -1,11 +1,12 @@
namespace Nub.Lang.Frontend.Parsing;
public class LocalFuncDefinitionNode(string name, List<FuncParameter> parameters, BlockNode body, Optional<Type> returnType) : DefinitionNode
public class LocalFuncDefinitionNode(string name, List<FuncParameter> parameters, BlockNode body, Optional<NubType> returnType, bool global) : DefinitionNode
{
public string Name { get; } = name;
public List<FuncParameter> Parameters { get; } = parameters;
public BlockNode Body { get; } = body;
public Optional<Type> ReturnType { get; } = returnType;
public Optional<NubType> ReturnType { get; } = returnType;
public bool Global { get; } = global;
public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}";
}

View File

@@ -20,13 +20,7 @@ public class Parser
{
if (TryExpectSymbol(Symbol.Import))
{
var name = ExpectLiteral();
if (name.Type is not StringType)
{
throw new Exception("Import statements must have a string literal value");
}
TryExpectSymbol(Symbol.Semicolon);
var name = ExpectIdentifier();
imports.Add(name.Value);
}
else
@@ -40,28 +34,23 @@ public class Parser
private DefinitionNode ParseDefinition()
{
List<Modifier> modifiers = [];
while (TryExpectModifier(out var modifier))
{
modifiers.Add(modifier);
}
var keyword = ExpectSymbol();
return keyword.Symbol switch
{
Symbol.Let => ParseGlobalVariableDefinition(),
Symbol.Func => ParseFuncDefinition(),
Symbol.Extern => ParseExternFuncDefinition(),
Symbol.Struct => ParseStruct(),
Symbol.Func => ParseFuncDefinition(modifiers),
Symbol.Struct => ParseStruct(modifiers),
_ => throw new Exception("Unexpected symbol: " + keyword.Symbol)
};
}
private GlobalVariableDefinitionNode ParseGlobalVariableDefinition()
{
var name = ExpectIdentifier();
ExpectSymbol(Symbol.Assign);
var value = ParseExpression();
ExpectSymbol(Symbol.Semicolon);
return new GlobalVariableDefinitionNode(name.Value, value);
}
private LocalFuncDefinitionNode ParseFuncDefinition()
private DefinitionNode ParseFuncDefinition(List<Modifier> modifiers)
{
var name = ExpectIdentifier();
List<FuncParameter> parameters = [];
@@ -75,54 +64,41 @@ public class Parser
}
}
var returnType = Optional<Type>.Empty();
var returnType = Optional<NubType>.Empty();
if (TryExpectSymbol(Symbol.Colon))
{
returnType = ParseType();
}
var body = ParseBlock();
return new LocalFuncDefinitionNode(name.Value, parameters, body, returnType);
}
private ExternFuncDefinitionNode ParseExternFuncDefinition()
if (modifiers.Remove(Modifier.Extern))
{
ExpectSymbol(Symbol.Func);
var name = ExpectIdentifier();
List<FuncParameter> parameters = [];
ExpectSymbol(Symbol.OpenParen);
if (!TryExpectSymbol(Symbol.CloseParen))
if (modifiers.Count != 0)
{
while (!TryExpectSymbol(Symbol.CloseParen))
{
parameters.Add(ParseFuncParameter());
TryExpectSymbol(Symbol.Comma);
throw new Exception($"Modifiers: {string.Join(", ", modifiers)} is not valid for an extern function");
}
}
var returnType = Optional<Type>.Empty();
if (TryExpectSymbol(Symbol.Colon))
{
returnType = ParseType();
}
ExpectSymbol(Symbol.Semicolon);
return new ExternFuncDefinitionNode(name.Value, parameters, returnType);
}
private StructDefinitionNode ParseStruct()
var body = ParseBlock();
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 StructDefinitionNode ParseStruct(List<Modifier> modifiers)
{
var name = ExpectIdentifier().Value;
ExpectSymbol(Symbol.OpenBrace);
List<StructMember> variables = [];
List<StructField> variables = [];
while (!TryExpectSymbol(Symbol.CloseBrace))
{
ExpectSymbol(Symbol.Let);
var variableName = ExpectIdentifier().Value;
ExpectSymbol(Symbol.Colon);
var variableType = ParseType();
@@ -134,9 +110,7 @@ public class Parser
variableValue = ParseExpression();
}
ExpectSymbol(Symbol.Semicolon);
variables.Add(new StructMember(variableName, variableType, variableValue));
variables.Add(new StructField(variableName, variableType, variableValue));
}
return new StructDefinitionNode(name, variables);
@@ -144,11 +118,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 = ParseType();
return new FuncParameter(name.Value, type);
return new FuncParameter(name.Value, type, variadic);
}
private StatementNode ParseStatement()
@@ -170,29 +152,19 @@ public class Parser
TryExpectSymbol(Symbol.Comma);
}
ExpectSymbol(Symbol.Semicolon);
if (identifier.Value == "syscall")
{
return new SyscallStatementNode(new Syscall(parameters));
}
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();
ExpectSymbol(Symbol.Semicolon);
return new VariableReassignmentNode(identifier.Value, value);
return new VariableAssignmentNode(identifier.Value, Optional<NubType>.Empty(), value);
}
case Symbol.Colon:
{
var type = ParseType();
ExpectSymbol(Symbol.Assign);
var value = ParseExpression();
return new VariableAssignmentNode(identifier.Value,type, value);
}
default:
{
@@ -205,11 +177,10 @@ public class Parser
return symbol.Symbol switch
{
Symbol.Return => ParseReturn(),
Symbol.Let => ParseVariableAssignment(),
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}")
};
}
@@ -226,22 +197,11 @@ public class Parser
if (!TryExpectSymbol(Symbol.Semicolon))
{
value = ParseExpression();
ExpectSymbol(Symbol.Semicolon);
}
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();
@@ -265,18 +225,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();
@@ -284,7 +232,8 @@ 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();
@@ -360,9 +309,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)
@@ -376,22 +329,6 @@ public class Parser
case Symbol.New:
{
var type = ParseType();
switch (type)
{
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<string, ExpressionNode> initializers = [];
ExpectSymbol(Symbol.OpenBrace);
while (!TryExpectSymbol(Symbol.CloseBrace))
@@ -399,20 +336,17 @@ public class Parser
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");
}
return new StructInitializerNode(type, initializers);
}
default:
{
throw new Exception($"Unknown symbol: {symbolToken.Symbol}");
}
}
}
default:
throw new Exception($"Unexpected token type {token.GetType().Name}");
}
@@ -435,25 +369,15 @@ public class Parser
case Symbol.Period:
{
Next();
List<string> members =
[
identifier.Value,
ExpectIdentifier().Value
];
ExpressionNode result = new IdentifierNode(identifier.Value);
while (TryExpectSymbol(Symbol.Period))
do
{
members.Add(ExpectIdentifier().Value);
}
var field = ExpectIdentifier();
result = new StructFieldAccessorNode(result, field.Value);
} while (TryExpectSymbol(Symbol.Period));
return new StructMemberAccessorNode(members);
}
case Symbol.OpenBracket:
{
Next();
var index = ParseExpression();
ExpectSymbol(Symbol.CloseBracket);
return new ArrayIndexAccessNode(new IdentifierNode(identifier.Value), index);
return result;
}
case Symbol.OpenParen:
{
@@ -465,14 +389,10 @@ public class Parser
TryExpectSymbol(Symbol.Comma);
}
if (identifier.Value == "syscall")
{
return new SyscallExpressionNode(new Syscall(parameters));
}
return new FuncCallExpressionNode(new FuncCall(identifier.Value, parameters));
}
}
break;
}
}
@@ -492,36 +412,10 @@ public class Parser
return new BlockNode(statements);
}
private Type ParseType()
private NubType ParseType()
{
var name = ExpectIdentifier().Value;
switch (name)
{
case "String":
{
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);
}
}
return NubType.Parse(name);
}
private Token ExpectToken()
@@ -563,6 +457,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();

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Frontend.Parsing;
public class StructDefinitionNode(string name, List<StructField> fields) : DefinitionNode
{
public string Name { get; } = name;
public List<StructField> Fields { get; } = fields;
}

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Frontend.Parsing;
public class StructFieldAccessorNode(ExpressionNode @struct, string field) : ExpressionNode
{
public ExpressionNode Struct { get; } = @struct;
public string Field { get; } = field;
}

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Frontend.Parsing;
public class StructInitializerNode(NubType structType, Dictionary<string, ExpressionNode> initializers) : ExpressionNode
{
public NubType StructType { get; } = structType;
public Dictionary<string, ExpressionNode> Initializers { get; } = initializers;
}

View File

@@ -0,0 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
public class VariableAssignmentNode(string name, Optional<NubType> explicitType, ExpressionNode value) : StatementNode
{
public string Name { get; } = name;
public Optional<NubType> ExplicitType { get; } = explicitType;
public ExpressionNode Value { get; } = value;
}

View File

@@ -2,18 +2,17 @@
namespace Nub.Lang.Frontend.Typing;
public class Func(string name, List<FuncParameter> parameters, Optional<BlockNode> body, Optional<Type> returnType)
public class Func(string name, List<FuncParameter> parameters, Optional<BlockNode> body, Optional<NubType> returnType)
{
public string Name { get; } = name;
public List<FuncParameter> Parameters { get; } = parameters;
public Optional<BlockNode> Body { get; } = body;
public Optional<Type> ReturnType { get; } = returnType;
public Optional<NubType> ReturnType { get; } = returnType;
}
public class ExpressionTyper
{
private readonly List<Func> _functions;
private readonly List<GlobalVariableDefinitionNode> _variableDefinitions;
private readonly List<StructDefinitionNode> _structDefinitions;
private readonly Stack<Variable> _variables;
@@ -21,7 +20,6 @@ public class ExpressionTyper
{
_variables = new Stack<Variable>();
_functions = [];
_variableDefinitions = [];
_structDefinitions = definitions.OfType<StructDefinitionNode>().ToList();
@@ -37,7 +35,6 @@ public class ExpressionTyper
_functions.AddRange(functions);
_functions.AddRange(externFunctions);
_variableDefinitions.AddRange(definitions.OfType<GlobalVariableDefinitionNode>());
}
public void Populate()
@@ -46,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)
{
@@ -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)
@@ -96,9 +87,6 @@ public class ExpressionTyper
{
switch (statement)
{
case ArrayIndexAssignmentNode arrayIndexAssignment:
PopulateArrayIndexAssignment(arrayIndexAssignment);
break;
case BreakNode:
case ContinueNode:
break;
@@ -111,15 +99,9 @@ public class ExpressionTyper
case ReturnNode returnNode:
PopulateReturn(returnNode);
break;
case SyscallStatementNode syscall:
PopulateSyscallStatement(syscall);
break;
case VariableAssignmentNode variableAssignment:
PopulateVariableAssignment(variableAssignment);
break;
case VariableReassignmentNode variableReassignment:
PopulateVariableReassignment(variableReassignment);
break;
case WhileNode whileStatement:
PopulateWhileStatement(whileStatement);
break;
@@ -128,13 +110,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)
@@ -157,14 +132,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)
@@ -176,12 +143,12 @@ 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(VariableReassignmentNode variableReassignment)
private void PopulateVariableReassignment(VariableAssignmentNode variableAssignment)
{
PopulateExpression(variableReassignment.Value);
PopulateExpression(variableAssignment.Value);
}
private void PopulateWhileStatement(WhileNode whileStatement)
@@ -194,12 +161,6 @@ public class ExpressionTyper
{
switch (expression)
{
case ArrayIndexAccessNode arrayIndexAccess:
PopulateArrayIndexAccess(arrayIndexAccess);
break;
case ArrayInitializerNode arrayInitializer:
PopulateArrayInitializer(arrayInitializer);
break;
case BinaryExpressionNode binaryExpression:
PopulateBinaryExpression(binaryExpression);
break;
@@ -215,41 +176,14 @@ public class ExpressionTyper
case StructInitializerNode structInitializer:
PopulateStructInitializer(structInitializer);
break;
case StructMemberAccessorNode structMemberAccessor:
GenerateStructMemberAccessorNode(structMemberAccessor);
break;
case SyscallExpressionNode syscall:
PopulateSyscallExpression(syscall);
case StructFieldAccessorNode structMemberAccessor:
PopulateStructMemberAccessorNode(structMemberAccessor);
break;
default:
throw new ArgumentOutOfRangeException(nameof(expression));
}
}
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 +197,7 @@ public class ExpressionTyper
case BinaryExpressionOperator.LessThan:
case BinaryExpressionOperator.LessThanOrEqual:
{
binaryExpression.Type = new PrimitiveType(PrimitiveTypeKind.Bool);
binaryExpression.Type = NubPrimitiveType.Bool;
break;
}
case BinaryExpressionOperator.Plus:
@@ -325,68 +259,34 @@ 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.Members[0]);
if (variable == null)
PopulateExpression(structFieldAccessor.Struct);
var structType = structFieldAccessor.Struct.Type;
if (structType == null)
{
throw new Exception($"Variable {structMemberAccessor.Members[0]} is not defined");
throw new Exception($"Cannot access field on non-struct type: {structFieldAccessor.Struct}");
}
if (variable.Type is not StructType variableType)
var structDefinition = _structDefinitions.FirstOrDefault(s => s.Name == structType.Name);
if (structDefinition == null)
{
throw new Exception("Variable " + structMemberAccessor.Members[0] + " is not a struct");
throw new Exception($"Struct {structType.Name} is not defined");
}
var definition = _structDefinitions.FirstOrDefault(sd => sd.Name == variableType.Name);
if (definition == null)
var field = structDefinition.Fields.FirstOrDefault(f => f.Name == structFieldAccessor.Field);
if (field == null)
{
throw new Exception($"Struct {structMemberAccessor.Members[0]} is not defined");
throw new Exception($"Field {structFieldAccessor.Field} is not defined in struct {structType.Name}");
}
for (var i = 1; i < structMemberAccessor.Members.Count - 1; i++)
{
var member = definition.Members.FirstOrDefault(m => m.Name == structMemberAccessor.Members[i]);
if (member == null)
{
throw new Exception($"Member {structMemberAccessor.Members[i]} does not exist on struct {definition.Name}");
structFieldAccessor.Type = field.Type;
}
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);
if (definition == null)
{
throw new Exception($"Struct {structMemberAccessor.Members[i]} is not defined");
}
}
var tmp = definition.Members.FirstOrDefault(m => m.Name == structMemberAccessor.Members.Last());
if (tmp == null)
{
throw new Exception($"Member {structMemberAccessor.Members.Last()} does not exist on struct {definition.Name}");
}
structMemberAccessor.Type = tmp.Type;
}
private void PopulateSyscallExpression(SyscallExpressionNode syscall)
{
foreach (var parameter in syscall.Syscall.Parameters)
{
PopulateExpression(parameter);
}
syscall.Type = new PrimitiveType(PrimitiveTypeKind.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;
}
}

View File

@@ -0,0 +1,10 @@
namespace Nub.Lang;
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}";
}

View File

@@ -1,10 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>nub</AssemblyName>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PublishAot>true</PublishAot>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,120 @@
using System.Diagnostics.CodeAnalysis;
namespace Nub.Lang;
public abstract class NubType
{
protected NubType(string name)
{
Name = name;
}
public string Name { get; }
public static NubType Parse(string s)
{
if (NubPrimitiveType.TryParse(s, out var kind))
{
return new NubPrimitiveType(kind.Value);
}
return new NubCustomType(s);
}
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))
{
Kind = kind;
}
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)
{
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.I8 => "i8",
PrimitiveTypeKind.I16 => "i16",
PrimitiveTypeKind.I32 => "i32",
PrimitiveTypeKind.I64 => "i64",
PrimitiveTypeKind.U8 => "u8",
PrimitiveTypeKind.U16 => "u16",
PrimitiveTypeKind.U32 => "u32",
PrimitiveTypeKind.U64 => "u64",
PrimitiveTypeKind.F32 => "f32",
PrimitiveTypeKind.F64 => "f64",
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
}

View File

@@ -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;
@@ -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;
}

View File

@@ -2,9 +2,9 @@
namespace Nub.Lang;
public class StructMember(string name, Type type, Optional<ExpressionNode> value)
public class StructField(string name, NubType type, Optional<ExpressionNode> value)
{
public string Name { get; } = name;
public Type Type { get; } = type;
public NubType Type { get; } = type;
public Optional<ExpressionNode> Value { get; } = value;
}

36
src/runtime/runtime.asm Normal file
View File

@@ -0,0 +1,36 @@
global _start
extern main
section .text
_start:
; 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
nub_strcmp:
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
.not_equal:
mov rax, 0
ret
.equal:
mov rax, 1
ret

View File

@@ -1,264 +0,0 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#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
}

View File

@@ -1,20 +0,0 @@
global str_cmp
section .text
str_cmp:
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
.not_equal:
mov rax, 0
ret
.equal:
mov rax, 1
ret

View File

@@ -1,6 +0,0 @@
global arr_size
section .text
arr_size:
mov rax, [rdi]
ret

View File

@@ -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

View File

@@ -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