Extern functions
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -33,4 +33,6 @@ Thumbs.db
|
|||||||
Desktop.ini
|
Desktop.ini
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
Nub.Lang/Nub.Lang/Output/out*
|
Nub.Lang/Nub.Lang/Output/*.o
|
||||||
|
Nub.Lang/Nub.Lang/Output/out
|
||||||
|
Nub.Lang/Nub.Lang/Output/out.asm
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public class BranchChecker
|
|||||||
|
|
||||||
public void Check()
|
public void Check()
|
||||||
{
|
{
|
||||||
foreach (var funcDefinition in _definitions.OfType<FuncDefinitionNode>())
|
foreach (var funcDefinition in _definitions.OfType<LocalFuncDefinitionNode>())
|
||||||
{
|
{
|
||||||
if (funcDefinition.ReturnType.HasValue)
|
if (funcDefinition.ReturnType.HasValue)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ public class Generator
|
|||||||
private readonly SymbolTable _symbolTable;
|
private readonly SymbolTable _symbolTable;
|
||||||
private readonly StringBuilder _builder;
|
private readonly StringBuilder _builder;
|
||||||
private readonly Dictionary<string, string> _strings;
|
private readonly Dictionary<string, string> _strings;
|
||||||
|
private readonly HashSet<string> _externFuncDefinitions;
|
||||||
private int _stringIndex;
|
private int _stringIndex;
|
||||||
|
|
||||||
public Generator(IReadOnlyCollection<DefinitionNode> definitions)
|
public Generator(IReadOnlyCollection<DefinitionNode> definitions)
|
||||||
@@ -18,8 +19,15 @@ public class Generator
|
|||||||
_strings = [];
|
_strings = [];
|
||||||
_definitions = definitions;
|
_definitions = definitions;
|
||||||
_builder = new StringBuilder();
|
_builder = new StringBuilder();
|
||||||
|
_externFuncDefinitions = ["strcmp"];
|
||||||
_symbolTable = new SymbolTable(definitions.OfType<GlobalVariableDefinitionNode>().ToList());
|
_symbolTable = new SymbolTable(definitions.OfType<GlobalVariableDefinitionNode>().ToList());
|
||||||
foreach (var funcDefinitionNode in definitions.OfType<FuncDefinitionNode>())
|
|
||||||
|
foreach (var funcDefinitionNode in definitions.OfType<ExternFuncDefinitionNode>())
|
||||||
|
{
|
||||||
|
_symbolTable.DefineFunc(funcDefinitionNode);
|
||||||
|
_externFuncDefinitions.Add(_symbolTable.ResolveExternFunc(funcDefinitionNode.Name, funcDefinitionNode.Parameters.Select(p => p.Type).ToList()).StartLabel);
|
||||||
|
}
|
||||||
|
foreach (var funcDefinitionNode in definitions.OfType<LocalFuncDefinitionNode>())
|
||||||
{
|
{
|
||||||
_symbolTable.DefineFunc(funcDefinitionNode);
|
_symbolTable.DefineFunc(funcDefinitionNode);
|
||||||
}
|
}
|
||||||
@@ -29,6 +37,11 @@ public class Generator
|
|||||||
{
|
{
|
||||||
_builder.AppendLine("global _start");
|
_builder.AppendLine("global _start");
|
||||||
|
|
||||||
|
foreach (var externFuncDefinition in _externFuncDefinitions)
|
||||||
|
{
|
||||||
|
_builder.AppendLine($"extern {externFuncDefinition}");
|
||||||
|
}
|
||||||
|
|
||||||
_builder.AppendLine();
|
_builder.AppendLine();
|
||||||
_builder.AppendLine("section .bss");
|
_builder.AppendLine("section .bss");
|
||||||
foreach (var globalVariable in _definitions.OfType<GlobalVariableDefinitionNode>())
|
foreach (var globalVariable in _definitions.OfType<GlobalVariableDefinitionNode>())
|
||||||
@@ -41,7 +54,7 @@ public class Generator
|
|||||||
_builder.AppendLine("section .text");
|
_builder.AppendLine("section .text");
|
||||||
_builder.AppendLine("_start:");
|
_builder.AppendLine("_start:");
|
||||||
|
|
||||||
var main = _symbolTable.ResolveFunc(Entrypoint, []);
|
var main = _symbolTable.ResolveLocalFunc(Entrypoint, []);
|
||||||
|
|
||||||
_builder.AppendLine(" ; Initialize global variables");
|
_builder.AppendLine(" ; Initialize global variables");
|
||||||
foreach (var globalVariable in _definitions.OfType<GlobalVariableDefinitionNode>())
|
foreach (var globalVariable in _definitions.OfType<GlobalVariableDefinitionNode>())
|
||||||
@@ -56,57 +69,18 @@ public class Generator
|
|||||||
_builder.AppendLine($" call {main.StartLabel}");
|
_builder.AppendLine($" call {main.StartLabel}");
|
||||||
|
|
||||||
_builder.AppendLine();
|
_builder.AppendLine();
|
||||||
_builder.AppendLine(" ; Exit with status code 0");
|
_builder.AppendLine(main.ReturnType.HasValue
|
||||||
|
? " mov rdi, rax ; Exit with return value of entrypoint"
|
||||||
|
: " mov rdi, 0 ; Exit with default status code 0");
|
||||||
_builder.AppendLine(" mov rax, 60");
|
_builder.AppendLine(" mov rax, 60");
|
||||||
_builder.AppendLine(" mov rdi, 0");
|
|
||||||
_builder.AppendLine(" syscall");
|
_builder.AppendLine(" syscall");
|
||||||
|
|
||||||
foreach (var funcDefinition in _definitions.OfType<FuncDefinitionNode>())
|
foreach (var funcDefinition in _definitions.OfType<LocalFuncDefinitionNode>())
|
||||||
{
|
{
|
||||||
_builder.AppendLine();
|
_builder.AppendLine();
|
||||||
GenerateFuncDefinition(funcDefinition);
|
GenerateFuncDefinition(funcDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
_builder.AppendLine("""
|
|
||||||
|
|
||||||
; https://tuttlem.github.io/2013/01/08/strlen-implementation-in-nasm.html
|
|
||||||
strlen:
|
|
||||||
push rcx ; save and clear out counter
|
|
||||||
xor rcx, rcx
|
|
||||||
strlen_next:
|
|
||||||
cmp [rdi], byte 0 ; null byte yet?
|
|
||||||
jz strlen_null ; yes, get out
|
|
||||||
inc rcx ; char is ok, count it
|
|
||||||
inc rdi ; move to next char
|
|
||||||
jmp strlen_next ; process again
|
|
||||||
strlen_null:
|
|
||||||
mov rax, rcx ; rcx = the length (put in rax)
|
|
||||||
pop rcx ; restore rcx
|
|
||||||
ret ; get out
|
|
||||||
""");
|
|
||||||
|
|
||||||
_builder.AppendLine("""
|
|
||||||
|
|
||||||
strcmp:
|
|
||||||
xor rdx, rdx
|
|
||||||
strcmp_loop:
|
|
||||||
mov al, [rsi + rdx]
|
|
||||||
mov bl, [rdi + rdx]
|
|
||||||
inc rdx
|
|
||||||
cmp al, bl
|
|
||||||
jne strcmp_not_equal
|
|
||||||
cmp al, 0
|
|
||||||
je strcmp_equal
|
|
||||||
jmp strcmp_loop
|
|
||||||
strcmp_not_equal:
|
|
||||||
mov rax, 0
|
|
||||||
ret
|
|
||||||
strcmp_equal:
|
|
||||||
mov rax, 1
|
|
||||||
ret
|
|
||||||
""");
|
|
||||||
|
|
||||||
|
|
||||||
_builder.AppendLine();
|
_builder.AppendLine();
|
||||||
_builder.AppendLine("section .data");
|
_builder.AppendLine("section .data");
|
||||||
foreach (var str in _strings)
|
foreach (var str in _strings)
|
||||||
@@ -117,9 +91,10 @@ public class Generator
|
|||||||
return _builder.ToString();
|
return _builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateFuncDefinition(FuncDefinitionNode node)
|
private void GenerateFuncDefinition(LocalFuncDefinitionNode node)
|
||||||
{
|
{
|
||||||
var func = _symbolTable.ResolveFunc(node.Name, node.Parameters.Select(p => p.Type).ToList());
|
var func = _symbolTable.ResolveLocalFunc(node.Name, node.Parameters.Select(p => p.Type).ToList());
|
||||||
|
|
||||||
_builder.AppendLine($"; {node.ToString()}");
|
_builder.AppendLine($"; {node.ToString()}");
|
||||||
_builder.AppendLine($"{func.StartLabel}:");
|
_builder.AppendLine($"{func.StartLabel}:");
|
||||||
_builder.AppendLine(" ; Set up stack frame");
|
_builder.AppendLine(" ; Set up stack frame");
|
||||||
@@ -154,7 +129,7 @@ public class Generator
|
|||||||
_builder.AppendLine(" ret");
|
_builder.AppendLine(" ret");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateBlock(BlockNode block, Func func)
|
private void GenerateBlock(BlockNode block, LocalFunc func)
|
||||||
{
|
{
|
||||||
foreach (var statement in block.Statements)
|
foreach (var statement in block.Statements)
|
||||||
{
|
{
|
||||||
@@ -162,7 +137,7 @@ public class Generator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateStatement(StatementNode statement, Func func)
|
private void GenerateStatement(StatementNode statement, LocalFunc func)
|
||||||
{
|
{
|
||||||
switch (statement)
|
switch (statement)
|
||||||
{
|
{
|
||||||
@@ -170,13 +145,13 @@ public class Generator
|
|||||||
GenerateFuncCall(funcCallStatement.FuncCall, func);
|
GenerateFuncCall(funcCallStatement.FuncCall, func);
|
||||||
break;
|
break;
|
||||||
case ReturnNode @return:
|
case ReturnNode @return:
|
||||||
GenerateReturn(func, @return);
|
GenerateReturn(@return, func);
|
||||||
break;
|
break;
|
||||||
case SyscallStatementNode syscallStatement:
|
case SyscallStatementNode syscallStatement:
|
||||||
GenerateSyscall(syscallStatement.Syscall, func);
|
GenerateSyscall(syscallStatement.Syscall, func);
|
||||||
break;
|
break;
|
||||||
case VariableAssignmentNode variableAssignment:
|
case VariableAssignmentNode variableAssignment:
|
||||||
GenerateVariableAssignment(func, variableAssignment);
|
GenerateVariableAssignment(variableAssignment, func);
|
||||||
break;
|
break;
|
||||||
case VariableReassignmentNode variableReassignment:
|
case VariableReassignmentNode variableReassignment:
|
||||||
GenerateVariableReassignment(variableReassignment, func);
|
GenerateVariableReassignment(variableReassignment, func);
|
||||||
@@ -186,7 +161,7 @@ public class Generator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateReturn(Func func, ReturnNode @return)
|
private void GenerateReturn(ReturnNode @return, LocalFunc func)
|
||||||
{
|
{
|
||||||
if (@return.Value.HasValue)
|
if (@return.Value.HasValue)
|
||||||
{
|
{
|
||||||
@@ -196,21 +171,21 @@ public class Generator
|
|||||||
_builder.AppendLine($" jmp {func.EndLabel}");
|
_builder.AppendLine($" jmp {func.EndLabel}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateVariableAssignment(Func func, VariableAssignmentNode variableAssignment)
|
private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment, LocalFunc func)
|
||||||
{
|
{
|
||||||
var variable = func.ResolveLocalVariable(variableAssignment.Name);
|
var variable = func.ResolveLocalVariable(variableAssignment.Name);
|
||||||
GenerateExpression(variableAssignment.Value, func);
|
GenerateExpression(variableAssignment.Value, func);
|
||||||
_builder.AppendLine($" mov [rbp - {variable.Offset}], rax");
|
_builder.AppendLine($" mov [rbp - {variable.Offset}], rax");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateVariableReassignment(VariableReassignmentNode variableReassignment, Func func)
|
private void GenerateVariableReassignment(VariableReassignmentNode variableReassignment, LocalFunc func)
|
||||||
{
|
{
|
||||||
var variable = func.ResolveLocalVariable(variableReassignment.Name);
|
var variable = func.ResolveLocalVariable(variableReassignment.Name);
|
||||||
GenerateExpression(variableReassignment.Value, func);
|
GenerateExpression(variableReassignment.Value, func);
|
||||||
_builder.AppendLine($" mov [rbp - {variable.Offset}], rax");
|
_builder.AppendLine($" mov [rbp - {variable.Offset}], rax");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateExpression(ExpressionNode expression, Func func)
|
private void GenerateExpression(ExpressionNode expression, LocalFunc func)
|
||||||
{
|
{
|
||||||
switch (expression)
|
switch (expression)
|
||||||
{
|
{
|
||||||
@@ -224,10 +199,7 @@ public class Generator
|
|||||||
GenerateIdentifier(identifier, func);
|
GenerateIdentifier(identifier, func);
|
||||||
break;
|
break;
|
||||||
case LiteralNode literal:
|
case LiteralNode literal:
|
||||||
GenerateLiteral(literal, func);
|
GenerateLiteral(literal);
|
||||||
break;
|
|
||||||
case StrlenNode strlen:
|
|
||||||
GenerateStrlen(strlen, func);
|
|
||||||
break;
|
break;
|
||||||
case SyscallExpressionNode syscallExpression:
|
case SyscallExpressionNode syscallExpression:
|
||||||
GenerateSyscall(syscallExpression.Syscall, func);
|
GenerateSyscall(syscallExpression.Syscall, func);
|
||||||
@@ -237,7 +209,7 @@ public class Generator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateBinaryExpression(BinaryExpressionNode binaryExpression, Func func)
|
private void GenerateBinaryExpression(BinaryExpressionNode binaryExpression, LocalFunc func)
|
||||||
{
|
{
|
||||||
GenerateExpression(binaryExpression.Left, func);
|
GenerateExpression(binaryExpression.Left, func);
|
||||||
_builder.AppendLine(" push rax");
|
_builder.AppendLine(" push rax");
|
||||||
@@ -396,7 +368,7 @@ public class Generator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateIdentifier(IdentifierNode identifier, Func func)
|
private void GenerateIdentifier(IdentifierNode identifier, LocalFunc func)
|
||||||
{
|
{
|
||||||
var variable = func.ResolveVariable(identifier.Identifier);
|
var variable = func.ResolveVariable(identifier.Identifier);
|
||||||
|
|
||||||
@@ -417,7 +389,7 @@ public class Generator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateLiteral(LiteralNode literal, Func func)
|
private void GenerateLiteral(LiteralNode literal)
|
||||||
{
|
{
|
||||||
switch (literal.Type)
|
switch (literal.Type)
|
||||||
{
|
{
|
||||||
@@ -459,14 +431,7 @@ public class Generator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateStrlen(StrlenNode strlen, Func func)
|
private void GenerateFuncCall(FuncCall funcCall, LocalFunc func)
|
||||||
{
|
|
||||||
GenerateExpression(strlen.String, func);
|
|
||||||
_builder.AppendLine(" mov rdi, rax");
|
|
||||||
_builder.AppendLine(" call strlen");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GenerateFuncCall(FuncCall funcCall, Func func)
|
|
||||||
{
|
{
|
||||||
var symbol = _symbolTable.ResolveFunc(funcCall.Name, funcCall.Parameters.Select(p => p.Type).ToList());
|
var symbol = _symbolTable.ResolveFunc(funcCall.Name, funcCall.Parameters.Select(p => p.Type).ToList());
|
||||||
string[] registers = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
|
string[] registers = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
|
||||||
@@ -492,7 +457,7 @@ public class Generator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateSyscall(Syscall syscall, Func func)
|
private void GenerateSyscall(Syscall syscall, LocalFunc func)
|
||||||
{
|
{
|
||||||
string[] registers = ["rax", "rdi", "rsi", "rdx", "r10", "r8", "r9"];
|
string[] registers = ["rax", "rdi", "rsi", "rdx", "r10", "r8", "r9"];
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace Nub.Lang.Generation;
|
|||||||
|
|
||||||
public class SymbolTable
|
public class SymbolTable
|
||||||
{
|
{
|
||||||
private readonly List<Func> _functions = [];
|
private readonly List<Func> _funcDefinitions = [];
|
||||||
private readonly List<GlobalVariable> _globalVariables = [];
|
private readonly List<GlobalVariable> _globalVariables = [];
|
||||||
private int _labelIndex;
|
private int _labelIndex;
|
||||||
|
|
||||||
@@ -19,26 +19,30 @@ public class SymbolTable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DefineFunc(FuncDefinitionNode funcDefinition)
|
public void DefineFunc(ExternFuncDefinitionNode externFuncDefinition)
|
||||||
{
|
{
|
||||||
var startLabel = $"func{++_labelIndex}";
|
_funcDefinitions.Add(new ExternFunc(externFuncDefinition.Name, externFuncDefinition.Name, externFuncDefinition.Parameters, externFuncDefinition.ReturnType));
|
||||||
var endLabel = $"endfunc{_labelIndex}";
|
|
||||||
var localVariables = ResolveFunctionVariables(funcDefinition);
|
|
||||||
_functions.Add(new Func(startLabel, endLabel, funcDefinition.Name, funcDefinition.Parameters, funcDefinition.ReturnType, _globalVariables.Concat<Variable>(localVariables.Variables).ToList(), localVariables.StackSize));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private (int StackSize, List<LocalVariable> Variables) ResolveFunctionVariables(FuncDefinitionNode funcDefinition)
|
public void DefineFunc(LocalFuncDefinitionNode localFuncDefinition)
|
||||||
|
{
|
||||||
|
var startLabel = $"func{++_labelIndex}";
|
||||||
|
var endLabel = $"func_end{_labelIndex}";
|
||||||
|
_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;
|
var offset = 0;
|
||||||
List<LocalVariable> variables = [];
|
List<LocalVariable> variables = [];
|
||||||
|
|
||||||
foreach (var parameter in funcDefinition.Parameters)
|
foreach (var parameter in localFuncDefinition.Parameters)
|
||||||
{
|
{
|
||||||
offset += 8;
|
offset += 8;
|
||||||
variables.Add(new LocalVariable(parameter.Name, parameter.Type, offset));
|
variables.Add(new LocalVariable(parameter.Name, parameter.Type, offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var statement in funcDefinition.Body.Statements)
|
foreach (var statement in localFuncDefinition.Body.Statements)
|
||||||
{
|
{
|
||||||
if (statement is VariableAssignmentNode variableAssignment)
|
if (statement is VariableAssignmentNode variableAssignment)
|
||||||
{
|
{
|
||||||
@@ -47,12 +51,12 @@ public class SymbolTable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (offset, variables);
|
return variables;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Func ResolveFunc(string name, IReadOnlyCollection<Type> parameterTypes)
|
public Func ResolveFunc(string name, IReadOnlyCollection<Type> parameterTypes)
|
||||||
{
|
{
|
||||||
var func = _functions.FirstOrDefault(f => f.Name == name && f.Parameters.Count == parameterTypes.Count && f.Parameters.Where((p, i) => p.Type == parameterTypes.ElementAt(i)).Count() == parameterTypes.Count);
|
var func = _funcDefinitions.FirstOrDefault(f => f.Name == name && f.Parameters.Count == parameterTypes.Count && f.Parameters.Where((p, i) => p.Type == parameterTypes.ElementAt(i)).Count() == parameterTypes.Count);
|
||||||
if (func == null)
|
if (func == null)
|
||||||
{
|
{
|
||||||
throw new Exception($"Func {name}({string.Join(", ", parameterTypes)}) is not defined");
|
throw new Exception($"Func {name}({string.Join(", ", parameterTypes)}) is not defined");
|
||||||
@@ -61,6 +65,26 @@ public class SymbolTable
|
|||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LocalFunc ResolveLocalFunc(string name, IReadOnlyCollection<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, IReadOnlyCollection<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)
|
public GlobalVariable ResolveGlobalVariable(string name)
|
||||||
{
|
{
|
||||||
var variable = _globalVariables.FirstOrDefault(v => v.Name == name);
|
var variable = _globalVariables.FirstOrDefault(v => v.Name == name);
|
||||||
@@ -89,15 +113,40 @@ public class GlobalVariable(string name, Type type, string identifier) : Variabl
|
|||||||
public string Identifier { get; } = identifier;
|
public string Identifier { get; } = identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Func(string startLabel, string endLabel, string name, IReadOnlyCollection<FuncParameter> parameters, Optional<Type> returnType, IReadOnlyCollection<Variable> variables, int stackAllocation)
|
public abstract class Func
|
||||||
{
|
{
|
||||||
public string StartLabel { get; } = startLabel;
|
public Func(string name, string startLabel, IReadOnlyCollection<FuncParameter> parameters, Optional<Type> returnType)
|
||||||
public string EndLabel { get; } = endLabel;
|
{
|
||||||
public string Name { get; } = name;
|
Name = name;
|
||||||
public IReadOnlyCollection<FuncParameter> Parameters { get; } = parameters;
|
Parameters = parameters;
|
||||||
public Optional<Type> ReturnType { get; } = returnType;
|
ReturnType = returnType;
|
||||||
public IReadOnlyCollection<Variable> Variables { get; } = variables;
|
StartLabel = startLabel;
|
||||||
public int StackAllocation { get; } = stackAllocation;
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
public string StartLabel { get; }
|
||||||
|
public IReadOnlyCollection<FuncParameter> Parameters { get; }
|
||||||
|
public Optional<Type> ReturnType { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExternFunc : Func
|
||||||
|
{
|
||||||
|
public ExternFunc(string name, string startLabel, IReadOnlyCollection<FuncParameter> parameters, Optional<Type> returnType) : base(name, startLabel, parameters, returnType)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LocalFunc : Func
|
||||||
|
{
|
||||||
|
public LocalFunc(string name, string startLabel, string endLabel, IReadOnlyCollection<FuncParameter> parameters, Optional<Type> returnType, IReadOnlyCollection<Variable> variables) : base(name, startLabel, parameters, returnType)
|
||||||
|
{
|
||||||
|
EndLabel = endLabel;
|
||||||
|
Variables = variables;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string EndLabel { get; }
|
||||||
|
public IReadOnlyCollection<Variable> Variables { get; }
|
||||||
|
public int StackAllocation => Variables.OfType<LocalVariable>().Sum(variable => variable.Offset);
|
||||||
|
|
||||||
public Variable ResolveVariable(string name)
|
public Variable ResolveVariable(string name)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,15 @@
|
|||||||
|
let SYS_WRITE = 1;
|
||||||
|
let STD_IN = 0;
|
||||||
|
let STD_OUT = 1;
|
||||||
|
let STD_ERR = 2;
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
syscall(60, 5 + 3 * 5);
|
print("test\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func print(msg: String) {
|
||||||
|
syscall(SYS_WRITE, STD_OUT, msg, strlen(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
extern func strlen(msg: String): int64;
|
||||||
|
extern func strcmp(a: String, b: String): bool;
|
||||||
|
|||||||
@@ -7,6 +7,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,
|
||||||
["return"] = Symbol.Return,
|
["return"] = Symbol.Return,
|
||||||
["let"] = Symbol.Let,
|
["let"] = Symbol.Let,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ public class SymbolToken(Symbol symbol) : Token
|
|||||||
public enum Symbol
|
public enum Symbol
|
||||||
{
|
{
|
||||||
Whitespace,
|
Whitespace,
|
||||||
|
Extern,
|
||||||
Func,
|
Func,
|
||||||
Return,
|
Return,
|
||||||
Let,
|
Let,
|
||||||
|
|||||||
6
Nub.Lang/Nub.Lang/Output/build.sh
Normal file
6
Nub.Lang/Nub.Lang/Output/build.sh
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
nasm -g -felf64 out.asm -o out.o
|
||||||
|
nasm -g -felf64 core/strlen.asm -o strlen.o
|
||||||
|
nasm -g -felf64 core/strcmp.asm -o strcmp.o
|
||||||
|
|
||||||
|
ld -o out out.o strlen.o strcmp.o
|
||||||
20
Nub.Lang/Nub.Lang/Output/core/strcmp.asm
Normal file
20
Nub.Lang/Nub.Lang/Output/core/strcmp.asm
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
global strlen
|
||||||
|
section .text
|
||||||
|
|
||||||
|
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
|
||||||
13
Nub.Lang/Nub.Lang/Output/core/strlen.asm
Normal file
13
Nub.Lang/Nub.Lang/Output/core/strlen.asm
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
global strlen
|
||||||
|
section .text
|
||||||
|
|
||||||
|
strlen:
|
||||||
|
xor rax, rax
|
||||||
|
.loop:
|
||||||
|
cmp byte [rdi], 0
|
||||||
|
jz .done
|
||||||
|
inc rax
|
||||||
|
inc rdi
|
||||||
|
jmp .loop
|
||||||
|
.done:
|
||||||
|
ret
|
||||||
@@ -1,9 +1,3 @@
|
|||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
|
./build.sh
|
||||||
nasm -g -felf64 out.asm -o out.o
|
|
||||||
ld out.o -o out
|
|
||||||
|
|
||||||
gdb -tui out
|
gdb -tui out
|
||||||
|
|
||||||
rm out.o
|
|
||||||
rm out
|
|
||||||
@@ -1,10 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
|
./build.sh
|
||||||
nasm -g -felf64 out.asm -o out.o
|
|
||||||
ld out.o -o out
|
|
||||||
|
|
||||||
./out
|
./out
|
||||||
echo "Process exited with status code $?"
|
echo "Process exited with status code $?"
|
||||||
|
|
||||||
rm out.o
|
|
||||||
rm out
|
|
||||||
12
Nub.Lang/Nub.Lang/Parsing/ExternFuncDefinitionNode.cs
Normal file
12
Nub.Lang/Nub.Lang/Parsing/ExternFuncDefinitionNode.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Nub.Core;
|
||||||
|
|
||||||
|
namespace Nub.Lang.Parsing;
|
||||||
|
|
||||||
|
public class ExternFuncDefinitionNode(string name, IReadOnlyCollection<FuncParameter> parameters, Optional<Type> returnType) : DefinitionNode
|
||||||
|
{
|
||||||
|
public string Name { get; } = name;
|
||||||
|
public IReadOnlyCollection<FuncParameter> Parameters { get; } = parameters;
|
||||||
|
public Optional<Type> ReturnType { get; } = returnType;
|
||||||
|
|
||||||
|
public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}";
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Nub.Lang.Parsing;
|
namespace Nub.Lang.Parsing;
|
||||||
|
|
||||||
public class FuncDefinitionNode(string name, IReadOnlyCollection<FuncParameter> parameters, BlockNode body, Optional<Type> returnType) : DefinitionNode
|
public class LocalFuncDefinitionNode(string name, IReadOnlyCollection<FuncParameter> parameters, BlockNode body, Optional<Type> returnType) : DefinitionNode
|
||||||
{
|
{
|
||||||
public string Name { get; } = name;
|
public string Name { get; } = name;
|
||||||
public IReadOnlyCollection<FuncParameter> Parameters { get; } = parameters;
|
public IReadOnlyCollection<FuncParameter> Parameters { get; } = parameters;
|
||||||
@@ -33,6 +33,7 @@ public class Parser
|
|||||||
{
|
{
|
||||||
Symbol.Let => ParseGlobalVariableDefinition(),
|
Symbol.Let => ParseGlobalVariableDefinition(),
|
||||||
Symbol.Func => ParseFuncDefinition(),
|
Symbol.Func => ParseFuncDefinition(),
|
||||||
|
Symbol.Extern => ParseExternFuncDefinition(),
|
||||||
_ => throw new Exception("Unexpected symbol: " + keyword.Symbol)
|
_ => throw new Exception("Unexpected symbol: " + keyword.Symbol)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -47,7 +48,7 @@ public class Parser
|
|||||||
return new GlobalVariableDefinitionNode(name.Value, value);
|
return new GlobalVariableDefinitionNode(name.Value, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private FuncDefinitionNode ParseFuncDefinition()
|
private LocalFuncDefinitionNode ParseFuncDefinition()
|
||||||
{
|
{
|
||||||
var name = ExpectIdentifier();
|
var name = ExpectIdentifier();
|
||||||
List<FuncParameter> parameters = [];
|
List<FuncParameter> parameters = [];
|
||||||
@@ -69,7 +70,33 @@ public class Parser
|
|||||||
|
|
||||||
var body = ParseBlock();
|
var body = ParseBlock();
|
||||||
|
|
||||||
return new FuncDefinitionNode(name.Value, parameters, body, returnType);
|
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))
|
||||||
|
{
|
||||||
|
parameters.Add(ParseFuncParameter());
|
||||||
|
TryExpectSymbol(Symbol.Comma);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var returnType = Optional<Type>.Empty();
|
||||||
|
if (TryExpectSymbol(Symbol.Colon))
|
||||||
|
{
|
||||||
|
returnType = ParseType();
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpectSymbol(Symbol.Semicolon);
|
||||||
|
|
||||||
|
return new ExternFuncDefinitionNode(name.Value, parameters, returnType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private FuncParameter ParseFuncParameter()
|
private FuncParameter ParseFuncParameter()
|
||||||
@@ -268,11 +295,6 @@ public class Parser
|
|||||||
return new SyscallExpressionNode(new Syscall(parameters));
|
return new SyscallExpressionNode(new Syscall(parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (identifier.Value == "strlen" && parameters.Count == 1)
|
|
||||||
{
|
|
||||||
return new StrlenNode(parameters[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FuncCallExpressionNode(new FuncCall(identifier.Value, parameters));
|
return new FuncCallExpressionNode(new FuncCall(identifier.Value, parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Nub.Lang.Parsing;
|
|
||||||
|
|
||||||
public class StrlenNode(ExpressionNode @string) : ExpressionNode
|
|
||||||
{
|
|
||||||
public ExpressionNode String { get; } = @string;
|
|
||||||
}
|
|
||||||
@@ -18,6 +18,7 @@ public record PrimitiveType : Type
|
|||||||
"bool" => PrimitiveTypeKind.Bool,
|
"bool" => PrimitiveTypeKind.Bool,
|
||||||
"char" => PrimitiveTypeKind.Char,
|
"char" => PrimitiveTypeKind.Char,
|
||||||
"int64" => PrimitiveTypeKind.Int64,
|
"int64" => PrimitiveTypeKind.Int64,
|
||||||
|
"int32" => PrimitiveTypeKind.Int32,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null)
|
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,35 @@
|
|||||||
using Nub.Lang.Parsing;
|
using Nub.Core;
|
||||||
|
using Nub.Lang.Parsing;
|
||||||
|
|
||||||
namespace Nub.Lang.Typing;
|
namespace Nub.Lang.Typing;
|
||||||
|
|
||||||
|
public class Func(string name, IReadOnlyCollection<FuncParameter> parameters, Optional<BlockNode> body, Optional<Type> returnType)
|
||||||
|
{
|
||||||
|
public string Name { get; } = name;
|
||||||
|
public IReadOnlyCollection<FuncParameter> Parameters { get; } = parameters;
|
||||||
|
public Optional<BlockNode> Body { get; } = body;
|
||||||
|
public Optional<Type> ReturnType { get; } = returnType;
|
||||||
|
}
|
||||||
|
|
||||||
public class ExpressionTyper
|
public class ExpressionTyper
|
||||||
{
|
{
|
||||||
private readonly IReadOnlyCollection<FuncDefinitionNode> _functions;
|
private readonly IReadOnlyCollection<Func> _functions;
|
||||||
private readonly IReadOnlyCollection<GlobalVariableDefinitionNode> _variableDefinitions;
|
private readonly IReadOnlyCollection<GlobalVariableDefinitionNode> _variableDefinitions;
|
||||||
private readonly Stack<Variable> _variables;
|
private readonly Stack<Variable> _variables;
|
||||||
|
|
||||||
public ExpressionTyper(IReadOnlyCollection<DefinitionNode> definitions)
|
public ExpressionTyper(IReadOnlyCollection<DefinitionNode> definitions)
|
||||||
{
|
{
|
||||||
_functions = definitions.OfType<FuncDefinitionNode>().ToList();
|
var functions = definitions
|
||||||
|
.OfType<LocalFuncDefinitionNode>()
|
||||||
|
.Select(f => new Func(f.Name, f.Parameters, f.Body, f.ReturnType))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var externFunctions = definitions
|
||||||
|
.OfType<ExternFuncDefinitionNode>()
|
||||||
|
.Select(f => new Func(f.Name, f.Parameters, Optional<BlockNode>.Empty(), f.ReturnType))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
_functions = functions.Concat(externFunctions).ToList();
|
||||||
_variableDefinitions = definitions.OfType<GlobalVariableDefinitionNode>().ToList();
|
_variableDefinitions = definitions.OfType<GlobalVariableDefinitionNode>().ToList();
|
||||||
_variables = new Stack<Variable>();
|
_variables = new Stack<Variable>();
|
||||||
}
|
}
|
||||||
@@ -31,8 +50,12 @@ public class ExpressionTyper
|
|||||||
{
|
{
|
||||||
_variables.Push(new Variable(parameter.Name, parameter.Type));
|
_variables.Push(new Variable(parameter.Name, parameter.Type));
|
||||||
}
|
}
|
||||||
PopulateBlock(function.Body);
|
|
||||||
for (var i = 0; i < function.Parameters.Count(); i++)
|
if (function.Body.HasValue)
|
||||||
|
{
|
||||||
|
PopulateBlock(function.Body.Value);
|
||||||
|
}
|
||||||
|
for (var i = 0; i < function.Parameters.Count; i++)
|
||||||
{
|
{
|
||||||
_variables.Pop();
|
_variables.Pop();
|
||||||
}
|
}
|
||||||
@@ -127,9 +150,6 @@ public class ExpressionTyper
|
|||||||
case LiteralNode literal:
|
case LiteralNode literal:
|
||||||
PopulateLiteral(literal);
|
PopulateLiteral(literal);
|
||||||
break;
|
break;
|
||||||
case StrlenNode strlen:
|
|
||||||
PopulateStrlen(strlen);
|
|
||||||
break;
|
|
||||||
case SyscallExpressionNode syscall:
|
case SyscallExpressionNode syscall:
|
||||||
PopulateSyscallExpression(syscall);
|
PopulateSyscallExpression(syscall);
|
||||||
break;
|
break;
|
||||||
@@ -203,11 +223,6 @@ public class ExpressionTyper
|
|||||||
literal.Type = literal.LiteralType;
|
literal.Type = literal.LiteralType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void PopulateStrlen(StrlenNode strlen)
|
|
||||||
{
|
|
||||||
strlen.Type = new PrimitiveType(PrimitiveTypeKind.Int64);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PopulateSyscallExpression(SyscallExpressionNode syscall)
|
private void PopulateSyscallExpression(SyscallExpressionNode syscall)
|
||||||
{
|
{
|
||||||
foreach (var parameter in syscall.Syscall.Parameters)
|
foreach (var parameter in syscall.Syscall.Parameters)
|
||||||
|
|||||||
Reference in New Issue
Block a user