...
This commit is contained in:
@@ -3,220 +3,18 @@ using Nub.Lang.Frontend.Parsing;
|
|||||||
|
|
||||||
namespace Nub.Lang.Backend.Custom;
|
namespace Nub.Lang.Backend.Custom;
|
||||||
|
|
||||||
public class CustomGenerator
|
public class ExpressionGenerator
|
||||||
{
|
{
|
||||||
private const string Entrypoint = "main";
|
|
||||||
|
|
||||||
private readonly IReadOnlyCollection<DefinitionNode> _definitions;
|
|
||||||
private readonly SymbolTable _symbolTable;
|
|
||||||
private readonly StringBuilder _builder;
|
private readonly StringBuilder _builder;
|
||||||
private readonly Dictionary<string, string> _strings;
|
private readonly SymbolTable _symbolTable;
|
||||||
private readonly HashSet<string> _externFuncDefinitions;
|
|
||||||
private int _stringIndex;
|
|
||||||
private int _labelIndex;
|
|
||||||
|
|
||||||
public CustomGenerator(IReadOnlyCollection<DefinitionNode> definitions)
|
public ExpressionGenerator(SymbolTable symbolTable, StringBuilder builder)
|
||||||
{
|
{
|
||||||
_strings = [];
|
_symbolTable = symbolTable;
|
||||||
_definitions = definitions;
|
_builder = builder;
|
||||||
_builder = new StringBuilder();
|
|
||||||
_externFuncDefinitions = ["strcmp"];
|
|
||||||
_symbolTable = new SymbolTable(definitions.OfType<GlobalVariableDefinitionNode>().ToList());
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Generate()
|
public void GenerateExpression(ExpressionNode expression, LocalFunc func)
|
||||||
{
|
|
||||||
_builder.AppendLine("global _start");
|
|
||||||
|
|
||||||
foreach (var externFuncDefinition in _externFuncDefinitions)
|
|
||||||
{
|
|
||||||
_builder.AppendLine($"extern {externFuncDefinition}");
|
|
||||||
}
|
|
||||||
|
|
||||||
_builder.AppendLine();
|
|
||||||
_builder.AppendLine("section .bss");
|
|
||||||
foreach (var globalVariable in _definitions.OfType<GlobalVariableDefinitionNode>())
|
|
||||||
{
|
|
||||||
var symbol = _symbolTable.ResolveGlobalVariable(globalVariable.Name);
|
|
||||||
_builder.AppendLine($" {symbol.Identifier}: resq 1 ; {globalVariable.Name}");
|
|
||||||
}
|
|
||||||
|
|
||||||
_builder.AppendLine();
|
|
||||||
_builder.AppendLine("section .text");
|
|
||||||
_builder.AppendLine("_start:");
|
|
||||||
|
|
||||||
var main = _symbolTable.ResolveLocalFunc(Entrypoint, []);
|
|
||||||
|
|
||||||
_builder.AppendLine(" ; Initialize global variables");
|
|
||||||
foreach (var globalVariable in _definitions.OfType<GlobalVariableDefinitionNode>())
|
|
||||||
{
|
|
||||||
var symbol = _symbolTable.ResolveGlobalVariable(globalVariable.Name);
|
|
||||||
GenerateExpression(globalVariable.Value, main);
|
|
||||||
_builder.AppendLine($" mov [{symbol.Identifier}], rax");
|
|
||||||
}
|
|
||||||
|
|
||||||
_builder.AppendLine();
|
|
||||||
_builder.AppendLine($" ; Call entrypoint {Entrypoint}");
|
|
||||||
_builder.AppendLine($" call {main.StartLabel}");
|
|
||||||
|
|
||||||
_builder.AppendLine();
|
|
||||||
_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(" syscall");
|
|
||||||
|
|
||||||
foreach (var funcDefinition in _definitions.OfType<LocalFuncDefinitionNode>())
|
|
||||||
{
|
|
||||||
_builder.AppendLine();
|
|
||||||
GenerateFuncDefinition(funcDefinition);
|
|
||||||
}
|
|
||||||
|
|
||||||
_builder.AppendLine();
|
|
||||||
_builder.AppendLine("section .data");
|
|
||||||
foreach (var str in _strings)
|
|
||||||
{
|
|
||||||
_builder.AppendLine($"{str.Key}: db `{str.Value}`, 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
return _builder.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GenerateFuncDefinition(LocalFuncDefinitionNode node)
|
|
||||||
{
|
|
||||||
var func = _symbolTable.ResolveLocalFunc(node.Name, node.Parameters.Select(p => p.Type).ToList());
|
|
||||||
|
|
||||||
_builder.AppendLine($"; {node.ToString()}");
|
|
||||||
_builder.AppendLine($"{func.StartLabel}:");
|
|
||||||
_builder.AppendLine(" ; Set up stack frame");
|
|
||||||
_builder.AppendLine(" push rbp");
|
|
||||||
_builder.AppendLine(" mov rbp, rsp");
|
|
||||||
_builder.AppendLine($" sub rsp, {func.StackAllocation}");
|
|
||||||
|
|
||||||
string[] registers = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
|
|
||||||
|
|
||||||
_builder.AppendLine(" ; Body");
|
|
||||||
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(" ; Clean up stack frame");
|
|
||||||
_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 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;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(statement));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GenerateIf(IfNode ifStatement, LocalFunc func)
|
|
||||||
{
|
|
||||||
var endLabel = CreateLabel();
|
|
||||||
GenerateIf(ifStatement, endLabel, func);
|
|
||||||
_builder.AppendLine($"{endLabel}:");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GenerateIf(IfNode ifStatement, string endLabel, LocalFunc func)
|
|
||||||
{
|
|
||||||
var nextLabel = CreateLabel();
|
|
||||||
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 GenerateExpression(ExpressionNode expression, LocalFunc func)
|
|
||||||
{
|
{
|
||||||
switch (expression)
|
switch (expression)
|
||||||
{
|
{
|
||||||
@@ -431,8 +229,8 @@ public class CustomGenerator
|
|||||||
}
|
}
|
||||||
case StringType:
|
case StringType:
|
||||||
{
|
{
|
||||||
var ident = $"string{++_stringIndex}";
|
var ident = _symbolTable.LabelFactory.Create();
|
||||||
_strings.Add(ident, literal.Literal);
|
_symbolTable.DefineString(ident, literal.Literal);
|
||||||
_builder.AppendLine($" mov rax, {ident}");
|
_builder.AppendLine($" mov rax, {ident}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -462,7 +260,7 @@ public class CustomGenerator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateFuncCall(FuncCall funcCall, LocalFunc func)
|
public void GenerateFuncCall(FuncCall funcCall, LocalFunc 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"];
|
||||||
@@ -488,7 +286,7 @@ public class CustomGenerator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateSyscall(Syscall syscall, LocalFunc func)
|
public void GenerateSyscall(Syscall syscall, LocalFunc func)
|
||||||
{
|
{
|
||||||
string[] registers = ["rax", "rdi", "rsi", "rdx", "r10", "r8", "r9"];
|
string[] registers = ["rax", "rdi", "rsi", "rdx", "r10", "r8", "r9"];
|
||||||
|
|
||||||
@@ -505,9 +303,4 @@ public class CustomGenerator
|
|||||||
|
|
||||||
_builder.AppendLine(" syscall");
|
_builder.AppendLine(" syscall");
|
||||||
}
|
}
|
||||||
|
|
||||||
private string CreateLabel()
|
|
||||||
{
|
|
||||||
return $"label{++_labelIndex}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
146
Nub.Lang/Nub.Lang/Backend/Custom/FuncGenerator.cs
Normal file
146
Nub.Lang/Nub.Lang/Backend/Custom/FuncGenerator.cs
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Nub.Lang.Frontend.Parsing;
|
||||||
|
|
||||||
|
namespace Nub.Lang.Backend.Custom;
|
||||||
|
|
||||||
|
public class FuncGenerator
|
||||||
|
{
|
||||||
|
private readonly SymbolTable _symbolTable;
|
||||||
|
private readonly ExpressionGenerator _expressionGenerator;
|
||||||
|
private readonly StringBuilder _builder;
|
||||||
|
|
||||||
|
public FuncGenerator(SymbolTable symbolTable, StringBuilder builder, ExpressionGenerator expressionGenerator)
|
||||||
|
{
|
||||||
|
_symbolTable = symbolTable;
|
||||||
|
_builder = builder;
|
||||||
|
_expressionGenerator = expressionGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GenerateFuncDefinition(LocalFuncDefinitionNode node)
|
||||||
|
{
|
||||||
|
var func = _symbolTable.ResolveLocalFunc(node.Name, node.Parameters.Select(p => p.Type).ToList());
|
||||||
|
|
||||||
|
_builder.AppendLine($"; {node.ToString()}");
|
||||||
|
_builder.AppendLine($"{func.StartLabel}:");
|
||||||
|
_builder.AppendLine(" ; Set up stack frame");
|
||||||
|
_builder.AppendLine(" push rbp");
|
||||||
|
_builder.AppendLine(" mov rbp, rsp");
|
||||||
|
_builder.AppendLine($" sub rsp, {func.StackAllocation}");
|
||||||
|
|
||||||
|
string[] registers = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
|
||||||
|
|
||||||
|
_builder.AppendLine(" ; Body");
|
||||||
|
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(" ; Clean up stack frame");
|
||||||
|
_builder.AppendLine(" mov rsp, rbp");
|
||||||
|
_builder.AppendLine(" pop rbp");
|
||||||
|
_builder.AppendLine(" ret");
|
||||||
|
|
||||||
|
var result = _builder.ToString();
|
||||||
|
_builder.Clear();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 FuncCallStatementNode funcCallStatement:
|
||||||
|
_expressionGenerator.GenerateFuncCall(funcCallStatement.FuncCall, func);
|
||||||
|
break;
|
||||||
|
case IfNode ifStatement:
|
||||||
|
GenerateIf(ifStatement, func);
|
||||||
|
break;
|
||||||
|
case ReturnNode @return:
|
||||||
|
GenerateReturn(@return, func);
|
||||||
|
break;
|
||||||
|
case SyscallStatementNode syscallStatement:
|
||||||
|
_expressionGenerator.GenerateSyscall(syscallStatement.Syscall, func);
|
||||||
|
break;
|
||||||
|
case VariableAssignmentNode variableAssignment:
|
||||||
|
GenerateVariableAssignment(variableAssignment, func);
|
||||||
|
break;
|
||||||
|
case VariableReassignmentNode variableReassignment:
|
||||||
|
GenerateVariableReassignment(variableReassignment, func);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(statement));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenerateIf(IfNode ifStatement, LocalFunc func)
|
||||||
|
{
|
||||||
|
var endLabel = _symbolTable.LabelFactory.Create();
|
||||||
|
GenerateIf(ifStatement, endLabel, func);
|
||||||
|
_builder.AppendLine($"{endLabel}:");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenerateIf(IfNode ifStatement, string endLabel, LocalFunc func)
|
||||||
|
{
|
||||||
|
var nextLabel = _symbolTable.LabelFactory.Create();
|
||||||
|
_expressionGenerator.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)
|
||||||
|
{
|
||||||
|
_expressionGenerator.GenerateExpression(@return.Value.Value, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
_builder.AppendLine($" jmp {func.EndLabel}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment, LocalFunc func)
|
||||||
|
{
|
||||||
|
var variable = func.ResolveLocalVariable(variableAssignment.Name);
|
||||||
|
_expressionGenerator.GenerateExpression(variableAssignment.Value, func);
|
||||||
|
_builder.AppendLine($" mov [rbp - {variable.Offset}], rax");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenerateVariableReassignment(VariableReassignmentNode variableReassignment, LocalFunc func)
|
||||||
|
{
|
||||||
|
var variable = func.ResolveLocalVariable(variableReassignment.Name);
|
||||||
|
_expressionGenerator.GenerateExpression(variableReassignment.Value, func);
|
||||||
|
_builder.AppendLine($" mov [rbp - {variable.Offset}], rax");
|
||||||
|
}
|
||||||
|
}
|
||||||
123
Nub.Lang/Nub.Lang/Backend/Custom/Generator.cs
Normal file
123
Nub.Lang/Nub.Lang/Backend/Custom/Generator.cs
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Nub.Lang.Frontend.Parsing;
|
||||||
|
|
||||||
|
namespace Nub.Lang.Backend.Custom;
|
||||||
|
|
||||||
|
public class Generator
|
||||||
|
{
|
||||||
|
private const string Entrypoint = "main";
|
||||||
|
|
||||||
|
private readonly List<DefinitionNode> _definitions;
|
||||||
|
private readonly SymbolTable _symbolTable;
|
||||||
|
private readonly StringBuilder _builder;
|
||||||
|
private readonly HashSet<string> _externFuncDefinitions;
|
||||||
|
private readonly ExpressionGenerator _expressionGenerator;
|
||||||
|
private readonly FuncGenerator _funcGenerator;
|
||||||
|
|
||||||
|
public Generator(FileNode file, Dictionary<string, FileNode> deps)
|
||||||
|
{
|
||||||
|
_definitions = [];
|
||||||
|
_builder = new StringBuilder();
|
||||||
|
_externFuncDefinitions = ["strcmp"];
|
||||||
|
_symbolTable = new SymbolTable();
|
||||||
|
_expressionGenerator = new ExpressionGenerator(_symbolTable, _builder);
|
||||||
|
_funcGenerator = new FuncGenerator(_symbolTable, _builder, _expressionGenerator);
|
||||||
|
|
||||||
|
ResolveGlobalVariables(file, deps);
|
||||||
|
ResolveDefinitions(file, deps);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResolveGlobalVariables(FileNode file, Dictionary<string, FileNode> deps)
|
||||||
|
{
|
||||||
|
|
||||||
|
foreach (var globalVariableDefinition in file.Definitions.OfType<GlobalVariableDefinitionNode>())
|
||||||
|
{
|
||||||
|
_symbolTable.DefineGlobalVariable(globalVariableDefinition);
|
||||||
|
_definitions.Add(globalVariableDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var include in file.Includes)
|
||||||
|
{
|
||||||
|
ResolveGlobalVariables(deps[include], deps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResolveDefinitions(FileNode file, Dictionary<string, FileNode> deps)
|
||||||
|
{
|
||||||
|
foreach (var funcDefinitionNode in file.Definitions.OfType<ExternFuncDefinitionNode>())
|
||||||
|
{
|
||||||
|
_symbolTable.DefineFunc(funcDefinitionNode);
|
||||||
|
_definitions.Add(funcDefinitionNode);
|
||||||
|
_externFuncDefinitions.Add(_symbolTable.ResolveExternFunc(funcDefinitionNode.Name, funcDefinitionNode.Parameters.Select(p => p.Type).ToList()).StartLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var funcDefinitionNode in file.Definitions.OfType<LocalFuncDefinitionNode>())
|
||||||
|
{
|
||||||
|
_symbolTable.DefineFunc(funcDefinitionNode);
|
||||||
|
_definitions.Add(funcDefinitionNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var include in file.Includes)
|
||||||
|
{
|
||||||
|
ResolveDefinitions(deps[include], deps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Generate()
|
||||||
|
{
|
||||||
|
_builder.AppendLine("global _start");
|
||||||
|
|
||||||
|
foreach (var externFuncDefinition in _externFuncDefinitions)
|
||||||
|
{
|
||||||
|
_builder.AppendLine($"extern {externFuncDefinition}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_builder.AppendLine();
|
||||||
|
_builder.AppendLine("section .bss");
|
||||||
|
foreach (var globalVariable in _definitions.OfType<GlobalVariableDefinitionNode>())
|
||||||
|
{
|
||||||
|
var symbol = _symbolTable.ResolveGlobalVariable(globalVariable.Name);
|
||||||
|
_builder.AppendLine($" {symbol.Identifier}: resq 1 ; {globalVariable.Name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_builder.AppendLine();
|
||||||
|
_builder.AppendLine("section .text");
|
||||||
|
_builder.AppendLine("_start:");
|
||||||
|
|
||||||
|
var main = _symbolTable.ResolveLocalFunc(Entrypoint, []);
|
||||||
|
|
||||||
|
_builder.AppendLine(" ; Initialize global variables");
|
||||||
|
foreach (var globalVariable in _definitions.OfType<GlobalVariableDefinitionNode>())
|
||||||
|
{
|
||||||
|
var symbol = _symbolTable.ResolveGlobalVariable(globalVariable.Name);
|
||||||
|
_expressionGenerator.GenerateExpression(globalVariable.Value, main);
|
||||||
|
_builder.AppendLine($" mov [{symbol.Identifier}], rax");
|
||||||
|
}
|
||||||
|
|
||||||
|
_builder.AppendLine();
|
||||||
|
_builder.AppendLine($" ; Call entrypoint {Entrypoint}");
|
||||||
|
_builder.AppendLine($" call {main.StartLabel}");
|
||||||
|
|
||||||
|
_builder.AppendLine();
|
||||||
|
_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(" syscall");
|
||||||
|
|
||||||
|
foreach (var funcDefinition in _definitions.OfType<LocalFuncDefinitionNode>())
|
||||||
|
{
|
||||||
|
_builder.AppendLine();
|
||||||
|
_builder.AppendLine(_funcGenerator.GenerateFuncDefinition(funcDefinition));
|
||||||
|
}
|
||||||
|
|
||||||
|
_builder.AppendLine();
|
||||||
|
_builder.AppendLine("section .data");
|
||||||
|
foreach (var str in _symbolTable.Strings)
|
||||||
|
{
|
||||||
|
_builder.AppendLine($"{str.Key}: db `{str.Value}`, 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
return _builder.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,16 +7,20 @@ public class SymbolTable
|
|||||||
{
|
{
|
||||||
private readonly List<Func> _funcDefinitions = [];
|
private readonly List<Func> _funcDefinitions = [];
|
||||||
private readonly List<GlobalVariable> _globalVariables = [];
|
private readonly List<GlobalVariable> _globalVariables = [];
|
||||||
private int _labelIndex;
|
private int _globalVariableIndex;
|
||||||
|
public LabelFactory LabelFactory { get; } = new();
|
||||||
|
|
||||||
public SymbolTable(IReadOnlyCollection<GlobalVariableDefinitionNode> globalVariableDefinitions)
|
public readonly Dictionary<string, string> Strings = [];
|
||||||
|
|
||||||
|
public void DefineString(string label, string value)
|
||||||
{
|
{
|
||||||
var globalVariableIndex = 0;
|
Strings.Add(label, value);
|
||||||
foreach (var globalVariable in globalVariableDefinitions)
|
}
|
||||||
{
|
|
||||||
var identifier = $"variable{++globalVariableIndex}";
|
public void DefineGlobalVariable(GlobalVariableDefinitionNode globalVariableDefinition)
|
||||||
_globalVariables.Add(new GlobalVariable(globalVariable.Name, globalVariable.Value.Type, identifier));
|
{
|
||||||
}
|
var identifier = $"variable{++_globalVariableIndex}";
|
||||||
|
_globalVariables.Add(new GlobalVariable(globalVariableDefinition.Name, globalVariableDefinition.Value.Type, identifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DefineFunc(ExternFuncDefinitionNode externFuncDefinition)
|
public void DefineFunc(ExternFuncDefinitionNode externFuncDefinition)
|
||||||
@@ -52,8 +56,8 @@ public class SymbolTable
|
|||||||
throw new Exception($"Func {existing} is already defined");
|
throw new Exception($"Func {existing} is already defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
var startLabel = $"func{++_labelIndex}";
|
var startLabel = LabelFactory.Create();
|
||||||
var endLabel = $"func_end{_labelIndex}";
|
var endLabel = LabelFactory.Create();
|
||||||
_funcDefinitions.Add(new LocalFunc(localFuncDefinition.Name, startLabel, endLabel, localFuncDefinition.Parameters, localFuncDefinition.ReturnType, _globalVariables.Concat<Variable>(ResolveFuncVariables(localFuncDefinition)).ToList()));
|
_funcDefinitions.Add(new LocalFunc(localFuncDefinition.Name, startLabel, endLabel, localFuncDefinition.Parameters, localFuncDefinition.ReturnType, _globalVariables.Concat<Variable>(ResolveFuncVariables(localFuncDefinition)).ToList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,3 +214,10 @@ public class LocalFunc : Func
|
|||||||
|
|
||||||
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 : "")}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class LabelFactory
|
||||||
|
{
|
||||||
|
private int _index;
|
||||||
|
|
||||||
|
public string Create() => $"label{++_index}";
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ public class Lexer
|
|||||||
["func"] = Symbol.Func,
|
["func"] = Symbol.Func,
|
||||||
["extern"] = Symbol.Extern,
|
["extern"] = Symbol.Extern,
|
||||||
["return"] = Symbol.Return,
|
["return"] = Symbol.Return,
|
||||||
|
["include"] = Symbol.Include,
|
||||||
["let"] = Symbol.Let,
|
["let"] = Symbol.Let,
|
||||||
["if"] = Symbol.If,
|
["if"] = Symbol.If,
|
||||||
["else"] = Symbol.Else,
|
["else"] = Symbol.Else,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ public class SymbolToken(Symbol symbol) : Token
|
|||||||
public enum Symbol
|
public enum Symbol
|
||||||
{
|
{
|
||||||
Whitespace,
|
Whitespace,
|
||||||
|
Include,
|
||||||
Extern,
|
Extern,
|
||||||
Func,
|
Func,
|
||||||
Return,
|
Return,
|
||||||
|
|||||||
7
Nub.Lang/Nub.Lang/Frontend/Parsing/FileNode.cs
Normal file
7
Nub.Lang/Nub.Lang/Frontend/Parsing/FileNode.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Nub.Lang.Frontend.Parsing;
|
||||||
|
|
||||||
|
public class FileNode(IReadOnlyCollection<string> includes, IReadOnlyCollection<DefinitionNode> definitions) : Node
|
||||||
|
{
|
||||||
|
public IReadOnlyCollection<string> Includes { get; } = includes;
|
||||||
|
public IReadOnlyCollection<DefinitionNode> Definitions { get; } = definitions;
|
||||||
|
}
|
||||||
@@ -14,16 +14,30 @@ public class Parser
|
|||||||
_tokens = tokens.ToArray();
|
_tokens = tokens.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyCollection<DefinitionNode> Parse()
|
public FileNode ParseFile(string relativePath)
|
||||||
{
|
{
|
||||||
_index = 0;
|
_index = 0;
|
||||||
List<DefinitionNode> definitions = [];
|
List<DefinitionNode> definitions = [];
|
||||||
|
List<string> includes = [];
|
||||||
|
|
||||||
|
while (TryExpectSymbol(Symbol.Include))
|
||||||
|
{
|
||||||
|
var name = ExpectLiteral();
|
||||||
|
if (name.Type is not StringType)
|
||||||
|
{
|
||||||
|
throw new Exception("Using statements must have a string literal value");
|
||||||
|
}
|
||||||
|
|
||||||
|
TryExpectSymbol(Symbol.Semicolon);
|
||||||
|
includes.Add(name.Value);
|
||||||
|
}
|
||||||
|
|
||||||
while (Peek().HasValue)
|
while (Peek().HasValue)
|
||||||
{
|
{
|
||||||
definitions.Add(ParseDefinition());
|
definitions.Add(ParseDefinition());
|
||||||
}
|
}
|
||||||
|
|
||||||
return definitions;
|
return new FileNode(includes, definitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DefinitionNode ParseDefinition()
|
private DefinitionNode ParseDefinition()
|
||||||
|
|||||||
@@ -13,25 +13,39 @@ public class Func(string name, IReadOnlyCollection<FuncParameter> parameters, Op
|
|||||||
|
|
||||||
public class ExpressionTyper
|
public class ExpressionTyper
|
||||||
{
|
{
|
||||||
private readonly IReadOnlyCollection<Func> _functions;
|
private readonly List<Func> _functions;
|
||||||
private readonly IReadOnlyCollection<GlobalVariableDefinitionNode> _variableDefinitions;
|
private readonly List<GlobalVariableDefinitionNode> _variableDefinitions;
|
||||||
private readonly Stack<Variable> _variables;
|
private readonly Stack<Variable> _variables;
|
||||||
|
|
||||||
public ExpressionTyper(IReadOnlyCollection<DefinitionNode> definitions)
|
public ExpressionTyper(FileNode file, Dictionary<string, FileNode> deps)
|
||||||
{
|
{
|
||||||
var functions = definitions
|
_variables = new Stack<Variable>();
|
||||||
|
_functions = [];
|
||||||
|
_variableDefinitions = [];
|
||||||
|
|
||||||
|
ResolveFunctions(file, deps);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResolveFunctions(FileNode file, Dictionary<string, FileNode> deps)
|
||||||
|
{
|
||||||
|
var functions = file.Definitions
|
||||||
.OfType<LocalFuncDefinitionNode>()
|
.OfType<LocalFuncDefinitionNode>()
|
||||||
.Select(f => new Func(f.Name, f.Parameters, f.Body, f.ReturnType))
|
.Select(f => new Func(f.Name, f.Parameters, f.Body, f.ReturnType))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var externFunctions = definitions
|
var externFunctions = file.Definitions
|
||||||
.OfType<ExternFuncDefinitionNode>()
|
.OfType<ExternFuncDefinitionNode>()
|
||||||
.Select(f => new Func(f.Name, f.Parameters, Optional<BlockNode>.Empty(), f.ReturnType))
|
.Select(f => new Func(f.Name, f.Parameters, Optional<BlockNode>.Empty(), f.ReturnType))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
_functions = functions.Concat(externFunctions).ToList();
|
_functions.AddRange(functions);
|
||||||
_variableDefinitions = definitions.OfType<GlobalVariableDefinitionNode>().ToList();
|
_functions.AddRange(externFunctions);
|
||||||
_variables = new Stack<Variable>();
|
_variableDefinitions.AddRange(file.Definitions.OfType<GlobalVariableDefinitionNode>());
|
||||||
|
|
||||||
|
foreach (var include in file.Includes)
|
||||||
|
{
|
||||||
|
ResolveFunctions(deps[include], deps);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Populate()
|
public void Populate()
|
||||||
|
|||||||
@@ -1,20 +1,46 @@
|
|||||||
using Nub.Lang.Backend.Custom;
|
using System.Diagnostics;
|
||||||
|
using Nub.Lang.Backend.Custom;
|
||||||
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;
|
||||||
|
|
||||||
var src = File.ReadAllText(args[0]);
|
var rootPath = Path.GetDirectoryName(args[0]);
|
||||||
|
var rootFileName = Path.GetFileName(args[0]);
|
||||||
|
Debug.Assert(rootPath != null && rootFileName != null);
|
||||||
|
|
||||||
var lexer = new Lexer(src);
|
Dictionary<string, FileNode> files = [];
|
||||||
var tokens = lexer.Lex();
|
|
||||||
|
|
||||||
var parser = new Parser(tokens);
|
Queue<string> queue = [];
|
||||||
var definitions = parser.Parse();
|
queue.Enqueue(rootFileName);
|
||||||
|
|
||||||
var typer = new ExpressionTyper(definitions);
|
while (queue.TryDequeue(out var path))
|
||||||
typer.Populate();
|
{
|
||||||
|
var src = File.ReadAllText(Path.Combine(rootPath, path));
|
||||||
|
|
||||||
var generator = new CustomGenerator(definitions);
|
var lexer = new Lexer(src);
|
||||||
|
var tokens = lexer.Lex();
|
||||||
|
|
||||||
|
var parser = new Parser(tokens);
|
||||||
|
var file = parser.ParseFile(path);
|
||||||
|
files[path] = file;
|
||||||
|
|
||||||
|
foreach (var include in file.Includes)
|
||||||
|
{
|
||||||
|
if (!files.ContainsKey(include))
|
||||||
|
{
|
||||||
|
queue.Enqueue(include);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
var typer = new ExpressionTyper(file.Value, files);
|
||||||
|
typer.Populate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var generator = new Generator(files[rootFileName], files);
|
||||||
var asm = generator.Generate();
|
var asm = generator.Generate();
|
||||||
|
|
||||||
Console.WriteLine(asm);
|
Console.WriteLine(asm);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace core;
|
include "core/string.nub"
|
||||||
|
|
||||||
let SYS_WRITE = 1;
|
let SYS_WRITE = 1;
|
||||||
let STD_IN = 0;
|
let STD_IN = 0;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
global strlen
|
global strlen
|
||||||
|
global strcmp
|
||||||
section .text
|
section .text
|
||||||
|
|
||||||
strcmp:
|
strcmp:
|
||||||
@@ -18,3 +19,14 @@ strcmp:
|
|||||||
.equal:
|
.equal:
|
||||||
mov rax, 1
|
mov rax, 1
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
strlen:
|
||||||
|
xor rax, rax
|
||||||
|
.loop:
|
||||||
|
cmp byte [rdi], 0
|
||||||
|
jz .done
|
||||||
|
inc rax
|
||||||
|
inc rdi
|
||||||
|
jmp .loop
|
||||||
|
.done:
|
||||||
|
ret
|
||||||
@@ -1,3 +1,2 @@
|
|||||||
namespace core;
|
extern func strlen(msg: String): int64;
|
||||||
|
|
||||||
extern func strcmp(a: String, b: String): bool;
|
extern func strcmp(a: String, b: String): bool;
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
global strlen
|
|
||||||
section .text
|
|
||||||
|
|
||||||
strlen:
|
|
||||||
xor rax, rax
|
|
||||||
.loop:
|
|
||||||
cmp byte [rdi], 0
|
|
||||||
jz .done
|
|
||||||
inc rax
|
|
||||||
inc rdi
|
|
||||||
jmp .loop
|
|
||||||
.done:
|
|
||||||
ret
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
namespace core;
|
|
||||||
|
|
||||||
extern func strlen(msg: String): int64;
|
|
||||||
@@ -1,17 +1,16 @@
|
|||||||
namespace program;
|
include "core/print.nub";
|
||||||
using core;
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
core::println("test");
|
println("test");
|
||||||
core::println(true);
|
println(true);
|
||||||
|
|
||||||
if true {
|
if strlen("1") == 1 {
|
||||||
core::println("1");
|
println("1");
|
||||||
} else if false {
|
} else if false {
|
||||||
core::println("2");
|
println("2");
|
||||||
} else if true {
|
} else if true {
|
||||||
core::println("3");
|
println("3");
|
||||||
} else {
|
} else {
|
||||||
core::println("4");
|
println("4");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
nasm -g -felf64 out.asm -o out.o
|
nasm -g -felf64 out.asm -o out.o
|
||||||
nasm -g -felf64 ../input/core/string/strlen.asm -o strlen.o
|
nasm -g -felf64 ../input/core/string.asm -o string.o
|
||||||
nasm -g -felf64 ../input/core/string/strcmp.asm -o strcmp.o
|
|
||||||
|
|
||||||
ld -o out out.o strlen.o strcmp.o
|
ld -o out out.o string.o
|
||||||
|
|||||||
Reference in New Issue
Block a user