22
build.sh
22
build.sh
@@ -1,15 +1,21 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
mkdir -p out
|
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
|
dotnet publish -c Release src/compiler/Nub.Lang
|
||||||
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
|
echo "compiling..."
|
||||||
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
|
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..."
|
||||||
|
|||||||
1
debug.sh
1
debug.sh
@@ -1,4 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
./clean.sh
|
./clean.sh
|
||||||
./build.sh
|
./build.sh
|
||||||
gdb -tui ./out/program
|
gdb -tui ./out/program
|
||||||
|
|||||||
2
example/c/bindings.nub
Normal file
2
example/c/bindings.nub
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
extern func puts(str: string)
|
||||||
|
extern func printf(fmt: string, ...args: any)
|
||||||
@@ -1 +0,0 @@
|
|||||||
extern func arr_size(array: Array<Any>): int64;
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
extern func str_len(msg: String): int64;
|
|
||||||
extern func itoa(value: int64): String;
|
|
||||||
@@ -1,16 +1,8 @@
|
|||||||
import "core";
|
import c
|
||||||
|
|
||||||
struct Human {
|
global func main(argc: i64, argv: i64) {
|
||||||
let name: String;
|
printf("args: %d, starts at %p\n", argc, argv)
|
||||||
let age: int64;
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
x: i8 = 320000
|
||||||
while true {
|
printf("%d\n", x)
|
||||||
let x = new Human
|
|
||||||
{
|
|
||||||
name = "test",
|
|
||||||
age = 34958743
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace Nub.Lang.Backend.Custom;
|
|
||||||
|
|
||||||
public class LabelFactory
|
|
||||||
{
|
|
||||||
private int _index;
|
|
||||||
public string Create() => $"label{++_index}";
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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}]";
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Nub.Lang.Frontend.Parsing;
|
|
||||||
|
|
||||||
public class StructMemberAccessorNode(List<string> members) : ExpressionNode
|
|
||||||
{
|
|
||||||
public List<string> Members { get; } = members;
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Nub.Lang.Frontend.Parsing;
|
|
||||||
|
|
||||||
public class Syscall(List<ExpressionNode> parameters)
|
|
||||||
{
|
|
||||||
public List<ExpressionNode> Parameters { get; } = parameters;
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Nub.Lang.Frontend.Parsing;
|
|
||||||
|
|
||||||
public class SyscallExpressionNode(Syscall syscall) : ExpressionNode
|
|
||||||
{
|
|
||||||
public Syscall Syscall { get; } = syscall;
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Nub.Lang.Frontend.Parsing;
|
|
||||||
|
|
||||||
public class SyscallStatementNode(Syscall syscall) : StatementNode
|
|
||||||
{
|
|
||||||
public Syscall Syscall { get; } = syscall;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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}";
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,8 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="UserContentModel">
|
<component name="UserContentModel">
|
||||||
<attachedFolders>
|
<attachedFolders>
|
||||||
<Path>../std</Path>
|
<Path>../../example</Path>
|
||||||
|
<Path>../core</Path>
|
||||||
</attachedFolders>
|
</attachedFolders>
|
||||||
<explicitIncludes />
|
<explicitIncludes />
|
||||||
<explicitExcludes />
|
<explicitExcludes />
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
1189
src/compiler/Nub.Lang/Backend/Generator.cs
Normal file
1189
src/compiler/Nub.Lang/Backend/Generator.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -5,9 +5,7 @@ public class Lexer
|
|||||||
private static readonly Dictionary<string, Symbol> Keywords = new()
|
private static readonly Dictionary<string, Symbol> Keywords = new()
|
||||||
{
|
{
|
||||||
["func"] = Symbol.Func,
|
["func"] = Symbol.Func,
|
||||||
["extern"] = Symbol.Extern,
|
|
||||||
["import"] = Symbol.Import,
|
["import"] = Symbol.Import,
|
||||||
["let"] = Symbol.Let,
|
|
||||||
["if"] = Symbol.If,
|
["if"] = Symbol.If,
|
||||||
["else"] = Symbol.Else,
|
["else"] = Symbol.Else,
|
||||||
["while"] = Symbol.While,
|
["while"] = Symbol.While,
|
||||||
@@ -17,6 +15,12 @@ public class Lexer
|
|||||||
["new"] = Symbol.New,
|
["new"] = Symbol.New,
|
||||||
["struct"] = Symbol.Struct,
|
["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()
|
private static readonly Dictionary<char[], Symbol> Chians = new()
|
||||||
{
|
{
|
||||||
@@ -84,9 +88,14 @@ public class Lexer
|
|||||||
return new SymbolToken(keywordSymbol);
|
return new SymbolToken(keywordSymbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Modifers.TryGetValue(buffer, out var modifer))
|
||||||
|
{
|
||||||
|
return new ModifierToken(modifer);
|
||||||
|
}
|
||||||
|
|
||||||
if (buffer is "true" or "false")
|
if (buffer is "true" or "false")
|
||||||
{
|
{
|
||||||
return new LiteralToken(new PrimitiveType(PrimitiveTypeKind.Bool), buffer);
|
return new LiteralToken(NubPrimitiveType.Bool, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new IdentifierToken(buffer);
|
return new IdentifierToken(buffer);
|
||||||
@@ -94,16 +103,41 @@ public class Lexer
|
|||||||
|
|
||||||
if (char.IsDigit(current.Value))
|
if (char.IsDigit(current.Value))
|
||||||
{
|
{
|
||||||
|
var isFloat = false;
|
||||||
var buffer = string.Empty;
|
var buffer = string.Empty;
|
||||||
|
|
||||||
while (current.HasValue && char.IsDigit(current.Value))
|
while (current.HasValue)
|
||||||
{
|
{
|
||||||
buffer += current.Value;
|
if (current.Value == '.')
|
||||||
Next();
|
{
|
||||||
|
if (isFloat)
|
||||||
|
{
|
||||||
|
throw new Exception("More than one period found in float literal");
|
||||||
|
}
|
||||||
|
isFloat = true;
|
||||||
|
buffer += current.Value;
|
||||||
|
Next();
|
||||||
current = Peek();
|
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
|
// TODO: Revisit this
|
||||||
@@ -148,7 +182,7 @@ public class Lexer
|
|||||||
buffer += current.Value;
|
buffer += current.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LiteralToken(new StringType(), buffer);
|
return new LiteralToken(NubPrimitiveType.String, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (char.IsWhiteSpace(current.Value))
|
if (char.IsWhiteSpace(current.Value))
|
||||||
7
src/compiler/Nub.Lang/Frontend/Lexing/LiteralToken.cs
Normal file
7
src/compiler/Nub.Lang/Frontend/Lexing/LiteralToken.cs
Normal 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;
|
||||||
|
}
|
||||||
12
src/compiler/Nub.Lang/Frontend/Lexing/ModifierToken.cs
Normal file
12
src/compiler/Nub.Lang/Frontend/Lexing/ModifierToken.cs
Normal 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
|
||||||
|
}
|
||||||
@@ -9,10 +9,8 @@ public enum Symbol
|
|||||||
{
|
{
|
||||||
Whitespace,
|
Whitespace,
|
||||||
Import,
|
Import,
|
||||||
Extern,
|
|
||||||
Func,
|
Func,
|
||||||
Return,
|
Return,
|
||||||
Let,
|
|
||||||
If,
|
If,
|
||||||
Else,
|
Else,
|
||||||
While,
|
While,
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
public abstract class ExpressionNode : Node
|
public abstract class ExpressionNode : Node
|
||||||
{
|
{
|
||||||
private Type? _type;
|
private NubType? _type;
|
||||||
public Type Type
|
public NubType Type
|
||||||
{
|
{
|
||||||
get => _type ?? throw new Exception("Tried to access expression type before type was populated");
|
get => _type ?? throw new Exception("Tried to access expression type before type was populated");
|
||||||
set => _type = value;
|
set => _type = value;
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
namespace Nub.Lang.Frontend.Parsing;
|
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 string Name { get; } = name;
|
||||||
public List<FuncParameter> Parameters { get; } = parameters;
|
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 : "")}";
|
public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}";
|
||||||
}
|
}
|
||||||
7
src/compiler/Nub.Lang/Frontend/Parsing/LiteralNode.cs
Normal file
7
src/compiler/Nub.Lang/Frontend/Parsing/LiteralNode.cs
Normal 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;
|
||||||
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
namespace Nub.Lang.Frontend.Parsing;
|
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 string Name { get; } = name;
|
||||||
public List<FuncParameter> Parameters { get; } = parameters;
|
public List<FuncParameter> Parameters { get; } = parameters;
|
||||||
public BlockNode Body { get; } = body;
|
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 : "")}";
|
public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}";
|
||||||
}
|
}
|
||||||
@@ -7,26 +7,20 @@ public class Parser
|
|||||||
{
|
{
|
||||||
private List<Token> _tokens = [];
|
private List<Token> _tokens = [];
|
||||||
private int _index;
|
private int _index;
|
||||||
|
|
||||||
public ModuleNode ParseModule(List<Token> tokens, string path)
|
public ModuleNode ParseModule(List<Token> tokens, string path)
|
||||||
{
|
{
|
||||||
_index = 0;
|
_index = 0;
|
||||||
_tokens = tokens;
|
_tokens = tokens;
|
||||||
|
|
||||||
List<DefinitionNode> definitions = [];
|
List<DefinitionNode> definitions = [];
|
||||||
List<string> imports = [];
|
List<string> imports = [];
|
||||||
|
|
||||||
while (Peek().HasValue)
|
while (Peek().HasValue)
|
||||||
{
|
{
|
||||||
if (TryExpectSymbol(Symbol.Import))
|
if (TryExpectSymbol(Symbol.Import))
|
||||||
{
|
{
|
||||||
var name = ExpectLiteral();
|
var name = ExpectIdentifier();
|
||||||
if (name.Type is not StringType)
|
|
||||||
{
|
|
||||||
throw new Exception("Import statements must have a string literal value");
|
|
||||||
}
|
|
||||||
|
|
||||||
TryExpectSymbol(Symbol.Semicolon);
|
|
||||||
imports.Add(name.Value);
|
imports.Add(name.Value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -40,28 +34,23 @@ public class Parser
|
|||||||
|
|
||||||
private DefinitionNode ParseDefinition()
|
private DefinitionNode ParseDefinition()
|
||||||
{
|
{
|
||||||
|
List<Modifier> modifiers = [];
|
||||||
|
|
||||||
|
while (TryExpectModifier(out var modifier))
|
||||||
|
{
|
||||||
|
modifiers.Add(modifier);
|
||||||
|
}
|
||||||
|
|
||||||
var keyword = ExpectSymbol();
|
var keyword = ExpectSymbol();
|
||||||
return keyword.Symbol switch
|
return keyword.Symbol switch
|
||||||
{
|
{
|
||||||
Symbol.Let => ParseGlobalVariableDefinition(),
|
Symbol.Func => ParseFuncDefinition(modifiers),
|
||||||
Symbol.Func => ParseFuncDefinition(),
|
Symbol.Struct => ParseStruct(modifiers),
|
||||||
Symbol.Extern => ParseExternFuncDefinition(),
|
|
||||||
Symbol.Struct => ParseStruct(),
|
|
||||||
_ => throw new Exception("Unexpected symbol: " + keyword.Symbol)
|
_ => throw new Exception("Unexpected symbol: " + keyword.Symbol)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private GlobalVariableDefinitionNode ParseGlobalVariableDefinition()
|
private DefinitionNode ParseFuncDefinition(List<Modifier> modifiers)
|
||||||
{
|
|
||||||
var name = ExpectIdentifier();
|
|
||||||
ExpectSymbol(Symbol.Assign);
|
|
||||||
var value = ParseExpression();
|
|
||||||
ExpectSymbol(Symbol.Semicolon);
|
|
||||||
|
|
||||||
return new GlobalVariableDefinitionNode(name.Value, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private LocalFuncDefinitionNode ParseFuncDefinition()
|
|
||||||
{
|
{
|
||||||
var name = ExpectIdentifier();
|
var name = ExpectIdentifier();
|
||||||
List<FuncParameter> parameters = [];
|
List<FuncParameter> parameters = [];
|
||||||
@@ -75,80 +64,73 @@ public class Parser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var returnType = Optional<Type>.Empty();
|
var returnType = Optional<NubType>.Empty();
|
||||||
if (TryExpectSymbol(Symbol.Colon))
|
if (TryExpectSymbol(Symbol.Colon))
|
||||||
{
|
{
|
||||||
returnType = ParseType();
|
returnType = ParseType();
|
||||||
}
|
}
|
||||||
|
|
||||||
var body = ParseBlock();
|
if (modifiers.Remove(Modifier.Extern))
|
||||||
|
|
||||||
return new LocalFuncDefinitionNode(name.Value, parameters, body, returnType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ExternFuncDefinitionNode ParseExternFuncDefinition()
|
|
||||||
{
|
|
||||||
ExpectSymbol(Symbol.Func);
|
|
||||||
var name = ExpectIdentifier();
|
|
||||||
List<FuncParameter> parameters = [];
|
|
||||||
ExpectSymbol(Symbol.OpenParen);
|
|
||||||
if (!TryExpectSymbol(Symbol.CloseParen))
|
|
||||||
{
|
{
|
||||||
while (!TryExpectSymbol(Symbol.CloseParen))
|
if (modifiers.Count != 0)
|
||||||
{
|
{
|
||||||
parameters.Add(ParseFuncParameter());
|
throw new Exception($"Modifiers: {string.Join(", ", modifiers)} is not valid for an extern function");
|
||||||
TryExpectSymbol(Symbol.Comma);
|
|
||||||
}
|
}
|
||||||
|
return new ExternFuncDefinitionNode(name.Value, parameters, returnType);
|
||||||
}
|
}
|
||||||
|
|
||||||
var returnType = Optional<Type>.Empty();
|
|
||||||
if (TryExpectSymbol(Symbol.Colon))
|
|
||||||
{
|
|
||||||
returnType = ParseType();
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpectSymbol(Symbol.Semicolon);
|
|
||||||
|
|
||||||
return new ExternFuncDefinitionNode(name.Value, parameters, returnType);
|
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()
|
private StructDefinitionNode ParseStruct(List<Modifier> modifiers)
|
||||||
{
|
{
|
||||||
var name = ExpectIdentifier().Value;
|
var name = ExpectIdentifier().Value;
|
||||||
|
|
||||||
ExpectSymbol(Symbol.OpenBrace);
|
ExpectSymbol(Symbol.OpenBrace);
|
||||||
|
|
||||||
List<StructMember> variables = [];
|
List<StructField> variables = [];
|
||||||
|
|
||||||
while (!TryExpectSymbol(Symbol.CloseBrace))
|
while (!TryExpectSymbol(Symbol.CloseBrace))
|
||||||
{
|
{
|
||||||
ExpectSymbol(Symbol.Let);
|
|
||||||
var variableName = ExpectIdentifier().Value;
|
var variableName = ExpectIdentifier().Value;
|
||||||
ExpectSymbol(Symbol.Colon);
|
ExpectSymbol(Symbol.Colon);
|
||||||
var variableType = ParseType();
|
var variableType = ParseType();
|
||||||
|
|
||||||
var variableValue = Optional<ExpressionNode>.Empty();
|
var variableValue = Optional<ExpressionNode>.Empty();
|
||||||
|
|
||||||
if (TryExpectSymbol(Symbol.Assign))
|
if (TryExpectSymbol(Symbol.Assign))
|
||||||
{
|
{
|
||||||
variableValue = ParseExpression();
|
variableValue = ParseExpression();
|
||||||
}
|
}
|
||||||
|
|
||||||
ExpectSymbol(Symbol.Semicolon);
|
variables.Add(new StructField(variableName, variableType, variableValue));
|
||||||
|
|
||||||
variables.Add(new StructMember(variableName, variableType, variableValue));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new StructDefinitionNode(name, variables);
|
return new StructDefinitionNode(name, variables);
|
||||||
}
|
}
|
||||||
|
|
||||||
private FuncParameter ParseFuncParameter()
|
private FuncParameter ParseFuncParameter()
|
||||||
{
|
{
|
||||||
|
var variadic = false;
|
||||||
|
if (TryExpectSymbol(Symbol.Period))
|
||||||
|
{
|
||||||
|
ExpectSymbol(Symbol.Period);
|
||||||
|
ExpectSymbol(Symbol.Period);
|
||||||
|
variadic = true;
|
||||||
|
}
|
||||||
|
|
||||||
var name = ExpectIdentifier();
|
var name = ExpectIdentifier();
|
||||||
ExpectSymbol(Symbol.Colon);
|
ExpectSymbol(Symbol.Colon);
|
||||||
var type = ParseType();
|
var type = ParseType();
|
||||||
|
|
||||||
return new FuncParameter(name.Value, type);
|
return new FuncParameter(name.Value, type, variadic);
|
||||||
}
|
}
|
||||||
|
|
||||||
private StatementNode ParseStatement()
|
private StatementNode ParseStatement()
|
||||||
@@ -170,29 +152,19 @@ public class Parser
|
|||||||
TryExpectSymbol(Symbol.Comma);
|
TryExpectSymbol(Symbol.Comma);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExpectSymbol(Symbol.Semicolon);
|
|
||||||
|
|
||||||
if (identifier.Value == "syscall")
|
|
||||||
{
|
|
||||||
return new SyscallStatementNode(new Syscall(parameters));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FuncCallStatementNode(new FuncCall(identifier.Value, 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:
|
case Symbol.Assign:
|
||||||
{
|
{
|
||||||
var value = ParseExpression();
|
var value = ParseExpression();
|
||||||
ExpectSymbol(Symbol.Semicolon);
|
return new VariableAssignmentNode(identifier.Value, Optional<NubType>.Empty(), value);
|
||||||
return new VariableReassignmentNode(identifier.Value, value);
|
}
|
||||||
|
case Symbol.Colon:
|
||||||
|
{
|
||||||
|
var type = ParseType();
|
||||||
|
ExpectSymbol(Symbol.Assign);
|
||||||
|
var value = ParseExpression();
|
||||||
|
return new VariableAssignmentNode(identifier.Value,type, value);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
@@ -205,11 +177,10 @@ public class Parser
|
|||||||
return symbol.Symbol switch
|
return symbol.Symbol switch
|
||||||
{
|
{
|
||||||
Symbol.Return => ParseReturn(),
|
Symbol.Return => ParseReturn(),
|
||||||
Symbol.Let => ParseVariableAssignment(),
|
|
||||||
Symbol.If => ParseIf(),
|
Symbol.If => ParseIf(),
|
||||||
Symbol.While => ParseWhile(),
|
Symbol.While => ParseWhile(),
|
||||||
Symbol.Break => ParseBreak(),
|
Symbol.Break => new BreakNode(),
|
||||||
Symbol.Continue => ParseContinue(),
|
Symbol.Continue => new ContinueNode(),
|
||||||
_ => throw new Exception($"Unexpected symbol {symbol.Symbol}")
|
_ => throw new Exception($"Unexpected symbol {symbol.Symbol}")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -226,22 +197,11 @@ public class Parser
|
|||||||
if (!TryExpectSymbol(Symbol.Semicolon))
|
if (!TryExpectSymbol(Symbol.Semicolon))
|
||||||
{
|
{
|
||||||
value = ParseExpression();
|
value = ParseExpression();
|
||||||
ExpectSymbol(Symbol.Semicolon);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ReturnNode(value);
|
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()
|
private IfNode ParseIf()
|
||||||
{
|
{
|
||||||
var condition = ParseExpression();
|
var condition = ParseExpression();
|
||||||
@@ -254,7 +214,7 @@ public class Parser
|
|||||||
? (Variant<IfNode, BlockNode>)ParseIf()
|
? (Variant<IfNode, BlockNode>)ParseIf()
|
||||||
: (Variant<IfNode, BlockNode>)ParseBlock();
|
: (Variant<IfNode, BlockNode>)ParseBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new IfNode(condition, body, elseStatement);
|
return new IfNode(condition, body, elseStatement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,18 +225,6 @@ public class Parser
|
|||||||
return new WhileNode(condition, body);
|
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)
|
private ExpressionNode ParseExpression(int precedence = 0)
|
||||||
{
|
{
|
||||||
var left = ParsePrimaryExpression();
|
var left = ParsePrimaryExpression();
|
||||||
@@ -284,15 +232,16 @@ public class Parser
|
|||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
var token = Peek();
|
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;
|
break;
|
||||||
|
|
||||||
Next();
|
Next();
|
||||||
var right = ParseExpression(GetBinaryOperatorPrecedence(op.Value) + 1);
|
var right = ParseExpression(GetBinaryOperatorPrecedence(op.Value) + 1);
|
||||||
|
|
||||||
left = new BinaryExpressionNode(left, op.Value, right);
|
left = new BinaryExpressionNode(left, op.Value, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
return left;
|
return left;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,9 +309,13 @@ public class Parser
|
|||||||
switch (token)
|
switch (token)
|
||||||
{
|
{
|
||||||
case LiteralToken literal:
|
case LiteralToken literal:
|
||||||
|
{
|
||||||
return new LiteralNode(literal.Value, literal.Type);
|
return new LiteralNode(literal.Value, literal.Type);
|
||||||
|
}
|
||||||
case IdentifierToken identifier:
|
case IdentifierToken identifier:
|
||||||
|
{
|
||||||
return ParseExpressionIdentifier(identifier);
|
return ParseExpressionIdentifier(identifier);
|
||||||
|
}
|
||||||
case SymbolToken symbolToken:
|
case SymbolToken symbolToken:
|
||||||
{
|
{
|
||||||
switch (symbolToken.Symbol)
|
switch (symbolToken.Symbol)
|
||||||
@@ -376,44 +329,25 @@ public class Parser
|
|||||||
case Symbol.New:
|
case Symbol.New:
|
||||||
{
|
{
|
||||||
var type = ParseType();
|
var type = ParseType();
|
||||||
switch (type)
|
Dictionary<string, ExpressionNode> initializers = [];
|
||||||
|
ExpectSymbol(Symbol.OpenBrace);
|
||||||
|
while (!TryExpectSymbol(Symbol.CloseBrace))
|
||||||
{
|
{
|
||||||
case ArrayType:
|
var name = ExpectIdentifier().Value;
|
||||||
{
|
ExpectSymbol(Symbol.Assign);
|
||||||
ExpectSymbol(Symbol.OpenParen);
|
var value = ParseExpression();
|
||||||
var size = ExpectLiteral();
|
initializers.Add(name, value);
|
||||||
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))
|
|
||||||
{
|
|
||||||
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:
|
default:
|
||||||
|
{
|
||||||
throw new Exception($"Unknown symbol: {symbolToken.Symbol}");
|
throw new Exception($"Unknown symbol: {symbolToken.Symbol}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new Exception($"Unexpected token type {token.GetType().Name}");
|
throw new Exception($"Unexpected token type {token.GetType().Name}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -435,25 +369,15 @@ public class Parser
|
|||||||
case Symbol.Period:
|
case Symbol.Period:
|
||||||
{
|
{
|
||||||
Next();
|
Next();
|
||||||
List<string> members =
|
ExpressionNode result = new IdentifierNode(identifier.Value);
|
||||||
[
|
|
||||||
identifier.Value,
|
|
||||||
ExpectIdentifier().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);
|
return result;
|
||||||
}
|
|
||||||
case Symbol.OpenBracket:
|
|
||||||
{
|
|
||||||
Next();
|
|
||||||
var index = ParseExpression();
|
|
||||||
ExpectSymbol(Symbol.CloseBracket);
|
|
||||||
return new ArrayIndexAccessNode(new IdentifierNode(identifier.Value), index);
|
|
||||||
}
|
}
|
||||||
case Symbol.OpenParen:
|
case Symbol.OpenParen:
|
||||||
{
|
{
|
||||||
@@ -465,18 +389,14 @@ public class Parser
|
|||||||
TryExpectSymbol(Symbol.Comma);
|
TryExpectSymbol(Symbol.Comma);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (identifier.Value == "syscall")
|
|
||||||
{
|
|
||||||
return new SyscallExpressionNode(new Syscall(parameters));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FuncCallExpressionNode(new FuncCall(identifier.Value, parameters));
|
return new FuncCallExpressionNode(new FuncCall(identifier.Value, parameters));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new IdentifierNode(identifier.Value);
|
return new IdentifierNode(identifier.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,36 +412,10 @@ public class Parser
|
|||||||
return new BlockNode(statements);
|
return new BlockNode(statements);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Type ParseType()
|
private NubType ParseType()
|
||||||
{
|
{
|
||||||
var name = ExpectIdentifier().Value;
|
var name = ExpectIdentifier().Value;
|
||||||
switch (name)
|
return NubType.Parse(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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Token ExpectToken()
|
private Token ExpectToken()
|
||||||
@@ -563,6 +457,19 @@ public class Parser
|
|||||||
return result;
|
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()
|
private IdentifierToken ExpectIdentifier()
|
||||||
{
|
{
|
||||||
var token = ExpectToken();
|
var token = ExpectToken();
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -2,18 +2,17 @@
|
|||||||
|
|
||||||
namespace Nub.Lang.Frontend.Typing;
|
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 string Name { get; } = name;
|
||||||
public List<FuncParameter> Parameters { get; } = parameters;
|
public List<FuncParameter> Parameters { get; } = parameters;
|
||||||
public Optional<BlockNode> Body { get; } = body;
|
public Optional<BlockNode> Body { get; } = body;
|
||||||
public Optional<Type> ReturnType { get; } = returnType;
|
public Optional<NubType> ReturnType { get; } = returnType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ExpressionTyper
|
public class ExpressionTyper
|
||||||
{
|
{
|
||||||
private readonly List<Func> _functions;
|
private readonly List<Func> _functions;
|
||||||
private readonly List<GlobalVariableDefinitionNode> _variableDefinitions;
|
|
||||||
private readonly List<StructDefinitionNode> _structDefinitions;
|
private readonly List<StructDefinitionNode> _structDefinitions;
|
||||||
private readonly Stack<Variable> _variables;
|
private readonly Stack<Variable> _variables;
|
||||||
|
|
||||||
@@ -21,7 +20,6 @@ public class ExpressionTyper
|
|||||||
{
|
{
|
||||||
_variables = new Stack<Variable>();
|
_variables = new Stack<Variable>();
|
||||||
_functions = [];
|
_functions = [];
|
||||||
_variableDefinitions = [];
|
|
||||||
|
|
||||||
_structDefinitions = definitions.OfType<StructDefinitionNode>().ToList();
|
_structDefinitions = definitions.OfType<StructDefinitionNode>().ToList();
|
||||||
|
|
||||||
@@ -37,7 +35,6 @@ public class ExpressionTyper
|
|||||||
|
|
||||||
_functions.AddRange(functions);
|
_functions.AddRange(functions);
|
||||||
_functions.AddRange(externFunctions);
|
_functions.AddRange(externFunctions);
|
||||||
_variableDefinitions.AddRange(definitions.OfType<GlobalVariableDefinitionNode>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Populate()
|
public void Populate()
|
||||||
@@ -46,7 +43,7 @@ public class ExpressionTyper
|
|||||||
|
|
||||||
foreach (var @class in _structDefinitions)
|
foreach (var @class in _structDefinitions)
|
||||||
{
|
{
|
||||||
foreach (var variable in @class.Members)
|
foreach (var variable in @class.Fields)
|
||||||
{
|
{
|
||||||
if (variable.Value.HasValue)
|
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 function in _functions)
|
||||||
{
|
{
|
||||||
foreach (var parameter in function.Parameters)
|
foreach (var parameter in function.Parameters)
|
||||||
@@ -96,9 +87,6 @@ public class ExpressionTyper
|
|||||||
{
|
{
|
||||||
switch (statement)
|
switch (statement)
|
||||||
{
|
{
|
||||||
case ArrayIndexAssignmentNode arrayIndexAssignment:
|
|
||||||
PopulateArrayIndexAssignment(arrayIndexAssignment);
|
|
||||||
break;
|
|
||||||
case BreakNode:
|
case BreakNode:
|
||||||
case ContinueNode:
|
case ContinueNode:
|
||||||
break;
|
break;
|
||||||
@@ -111,15 +99,9 @@ public class ExpressionTyper
|
|||||||
case ReturnNode returnNode:
|
case ReturnNode returnNode:
|
||||||
PopulateReturn(returnNode);
|
PopulateReturn(returnNode);
|
||||||
break;
|
break;
|
||||||
case SyscallStatementNode syscall:
|
|
||||||
PopulateSyscallStatement(syscall);
|
|
||||||
break;
|
|
||||||
case VariableAssignmentNode variableAssignment:
|
case VariableAssignmentNode variableAssignment:
|
||||||
PopulateVariableAssignment(variableAssignment);
|
PopulateVariableAssignment(variableAssignment);
|
||||||
break;
|
break;
|
||||||
case VariableReassignmentNode variableReassignment:
|
|
||||||
PopulateVariableReassignment(variableReassignment);
|
|
||||||
break;
|
|
||||||
case WhileNode whileStatement:
|
case WhileNode whileStatement:
|
||||||
PopulateWhileStatement(whileStatement);
|
PopulateWhileStatement(whileStatement);
|
||||||
break;
|
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)
|
private void PopulateFuncCallStatement(FuncCallStatementNode funcCall)
|
||||||
{
|
{
|
||||||
foreach (var parameter in funcCall.FuncCall.Parameters)
|
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)
|
private void PopulateReturn(ReturnNode returnNode)
|
||||||
{
|
{
|
||||||
if (returnNode.Value.HasValue)
|
if (returnNode.Value.HasValue)
|
||||||
@@ -176,12 +143,12 @@ public class ExpressionTyper
|
|||||||
private void PopulateVariableAssignment(VariableAssignmentNode variableAssignment)
|
private void PopulateVariableAssignment(VariableAssignmentNode variableAssignment)
|
||||||
{
|
{
|
||||||
PopulateExpression(variableAssignment.Value);
|
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)
|
private void PopulateWhileStatement(WhileNode whileStatement)
|
||||||
@@ -194,12 +161,6 @@ public class ExpressionTyper
|
|||||||
{
|
{
|
||||||
switch (expression)
|
switch (expression)
|
||||||
{
|
{
|
||||||
case ArrayIndexAccessNode arrayIndexAccess:
|
|
||||||
PopulateArrayIndexAccess(arrayIndexAccess);
|
|
||||||
break;
|
|
||||||
case ArrayInitializerNode arrayInitializer:
|
|
||||||
PopulateArrayInitializer(arrayInitializer);
|
|
||||||
break;
|
|
||||||
case BinaryExpressionNode binaryExpression:
|
case BinaryExpressionNode binaryExpression:
|
||||||
PopulateBinaryExpression(binaryExpression);
|
PopulateBinaryExpression(binaryExpression);
|
||||||
break;
|
break;
|
||||||
@@ -215,41 +176,14 @@ public class ExpressionTyper
|
|||||||
case StructInitializerNode structInitializer:
|
case StructInitializerNode structInitializer:
|
||||||
PopulateStructInitializer(structInitializer);
|
PopulateStructInitializer(structInitializer);
|
||||||
break;
|
break;
|
||||||
case StructMemberAccessorNode structMemberAccessor:
|
case StructFieldAccessorNode structMemberAccessor:
|
||||||
GenerateStructMemberAccessorNode(structMemberAccessor);
|
PopulateStructMemberAccessorNode(structMemberAccessor);
|
||||||
break;
|
|
||||||
case SyscallExpressionNode syscall:
|
|
||||||
PopulateSyscallExpression(syscall);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException(nameof(expression));
|
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)
|
private void PopulateBinaryExpression(BinaryExpressionNode binaryExpression)
|
||||||
{
|
{
|
||||||
PopulateExpression(binaryExpression.Left);
|
PopulateExpression(binaryExpression.Left);
|
||||||
@@ -263,7 +197,7 @@ public class ExpressionTyper
|
|||||||
case BinaryExpressionOperator.LessThan:
|
case BinaryExpressionOperator.LessThan:
|
||||||
case BinaryExpressionOperator.LessThanOrEqual:
|
case BinaryExpressionOperator.LessThanOrEqual:
|
||||||
{
|
{
|
||||||
binaryExpression.Type = new PrimitiveType(PrimitiveTypeKind.Bool);
|
binaryExpression.Type = NubPrimitiveType.Bool;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BinaryExpressionOperator.Plus:
|
case BinaryExpressionOperator.Plus:
|
||||||
@@ -325,68 +259,34 @@ public class ExpressionTyper
|
|||||||
structInitializer.Type = structInitializer.StructType;
|
structInitializer.Type = structInitializer.StructType;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Fix this ugly ass code
|
private void PopulateStructMemberAccessorNode(StructFieldAccessorNode structFieldAccessor)
|
||||||
private void GenerateStructMemberAccessorNode(StructMemberAccessorNode structMemberAccessor)
|
|
||||||
{
|
{
|
||||||
var variable = _variables.FirstOrDefault(v => v.Name == structMemberAccessor.Members[0]);
|
PopulateExpression(structFieldAccessor.Struct);
|
||||||
if (variable == null)
|
|
||||||
|
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);
|
var field = structDefinition.Fields.FirstOrDefault(f => f.Name == structFieldAccessor.Field);
|
||||||
if (definition == null)
|
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++)
|
structFieldAccessor.Type = field.Type;
|
||||||
{
|
|
||||||
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}");
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
private class Variable(string name, NubType type)
|
||||||
{
|
|
||||||
foreach (var parameter in syscall.Syscall.Parameters)
|
|
||||||
{
|
|
||||||
PopulateExpression(parameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
syscall.Type = new PrimitiveType(PrimitiveTypeKind.Int64);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Variable(string name, Type type)
|
|
||||||
{
|
{
|
||||||
public string Name { get; } = name;
|
public string Name { get; } = name;
|
||||||
public Type Type { get; } = type;
|
public NubType Type { get; } = type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
10
src/compiler/Nub.Lang/FuncParameter.cs
Normal file
10
src/compiler/Nub.Lang/FuncParameter.cs
Normal 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}";
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
<AssemblyName>nub</AssemblyName>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<PublishAot>true</PublishAot>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
120
src/compiler/Nub.Lang/NubType.cs
Normal file
120
src/compiler/Nub.Lang/NubType.cs
Normal 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
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Nub.Lang.Backend.Custom;
|
using Nub.Lang.Backend;
|
||||||
using Nub.Lang.Frontend.Lexing;
|
using Nub.Lang.Frontend.Lexing;
|
||||||
using Nub.Lang.Frontend.Parsing;
|
using Nub.Lang.Frontend.Parsing;
|
||||||
using Nub.Lang.Frontend.Typing;
|
using Nub.Lang.Frontend.Typing;
|
||||||
@@ -48,9 +48,9 @@ internal static class Program
|
|||||||
typer.Populate();
|
typer.Populate();
|
||||||
|
|
||||||
var generator = new Generator(definitions);
|
var generator = new Generator(definitions);
|
||||||
var asm = generator.Generate();
|
var result = generator.Generate();
|
||||||
|
|
||||||
File.WriteAllText(output, asm);
|
File.WriteAllText(output, result);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
namespace Nub.Lang;
|
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 string Name { get; } = name;
|
||||||
public Type Type { get; } = type;
|
public NubType Type { get; } = type;
|
||||||
public Optional<ExpressionNode> Value { get; } = value;
|
public Optional<ExpressionNode> Value { get; } = value;
|
||||||
}
|
}
|
||||||
36
src/runtime/runtime.asm
Normal file
36
src/runtime/runtime.asm
Normal 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
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
global arr_size
|
|
||||||
|
|
||||||
section .text
|
|
||||||
arr_size:
|
|
||||||
mov rax, [rdi]
|
|
||||||
ret
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
Reference in New Issue
Block a user