...
This commit is contained in:
@@ -3,220 +3,18 @@ using Nub.Lang.Frontend.Parsing;
|
||||
|
||||
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 Dictionary<string, string> _strings;
|
||||
private readonly HashSet<string> _externFuncDefinitions;
|
||||
private int _stringIndex;
|
||||
private int _labelIndex;
|
||||
private readonly SymbolTable _symbolTable;
|
||||
|
||||
public CustomGenerator(IReadOnlyCollection<DefinitionNode> definitions)
|
||||
public ExpressionGenerator(SymbolTable symbolTable, StringBuilder builder)
|
||||
{
|
||||
_strings = [];
|
||||
_definitions = definitions;
|
||||
_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);
|
||||
}
|
||||
_symbolTable = symbolTable;
|
||||
_builder = builder;
|
||||
}
|
||||
|
||||
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);
|
||||
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)
|
||||
public void GenerateExpression(ExpressionNode expression, LocalFunc func)
|
||||
{
|
||||
switch (expression)
|
||||
{
|
||||
@@ -431,8 +229,8 @@ public class CustomGenerator
|
||||
}
|
||||
case StringType:
|
||||
{
|
||||
var ident = $"string{++_stringIndex}";
|
||||
_strings.Add(ident, literal.Literal);
|
||||
var ident = _symbolTable.LabelFactory.Create();
|
||||
_symbolTable.DefineString(ident, literal.Literal);
|
||||
_builder.AppendLine($" mov rax, {ident}");
|
||||
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());
|
||||
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"];
|
||||
|
||||
@@ -505,9 +303,4 @@ public class CustomGenerator
|
||||
|
||||
_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<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;
|
||||
foreach (var globalVariable in globalVariableDefinitions)
|
||||
{
|
||||
var identifier = $"variable{++globalVariableIndex}";
|
||||
_globalVariables.Add(new GlobalVariable(globalVariable.Name, globalVariable.Value.Type, identifier));
|
||||
Strings.Add(label, value);
|
||||
}
|
||||
|
||||
public void DefineGlobalVariable(GlobalVariableDefinitionNode globalVariableDefinition)
|
||||
{
|
||||
var identifier = $"variable{++_globalVariableIndex}";
|
||||
_globalVariables.Add(new GlobalVariable(globalVariableDefinition.Name, globalVariableDefinition.Value.Type, identifier));
|
||||
}
|
||||
|
||||
public void DefineFunc(ExternFuncDefinitionNode externFuncDefinition)
|
||||
@@ -52,8 +56,8 @@ public class SymbolTable
|
||||
throw new Exception($"Func {existing} is already defined");
|
||||
}
|
||||
|
||||
var startLabel = $"func{++_labelIndex}";
|
||||
var endLabel = $"func_end{_labelIndex}";
|
||||
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()));
|
||||
}
|
||||
|
||||
@@ -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 class LabelFactory
|
||||
{
|
||||
private int _index;
|
||||
|
||||
public string Create() => $"label{++_index}";
|
||||
}
|
||||
@@ -9,6 +9,7 @@ public class Lexer
|
||||
["func"] = Symbol.Func,
|
||||
["extern"] = Symbol.Extern,
|
||||
["return"] = Symbol.Return,
|
||||
["include"] = Symbol.Include,
|
||||
["let"] = Symbol.Let,
|
||||
["if"] = Symbol.If,
|
||||
["else"] = Symbol.Else,
|
||||
|
||||
@@ -8,6 +8,7 @@ public class SymbolToken(Symbol symbol) : Token
|
||||
public enum Symbol
|
||||
{
|
||||
Whitespace,
|
||||
Include,
|
||||
Extern,
|
||||
Func,
|
||||
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();
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<DefinitionNode> Parse()
|
||||
public FileNode ParseFile(string relativePath)
|
||||
{
|
||||
_index = 0;
|
||||
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)
|
||||
{
|
||||
definitions.Add(ParseDefinition());
|
||||
}
|
||||
|
||||
return definitions;
|
||||
return new FileNode(includes, definitions);
|
||||
}
|
||||
|
||||
private DefinitionNode ParseDefinition()
|
||||
|
||||
@@ -13,25 +13,39 @@ public class Func(string name, IReadOnlyCollection<FuncParameter> parameters, Op
|
||||
|
||||
public class ExpressionTyper
|
||||
{
|
||||
private readonly IReadOnlyCollection<Func> _functions;
|
||||
private readonly IReadOnlyCollection<GlobalVariableDefinitionNode> _variableDefinitions;
|
||||
private readonly List<Func> _functions;
|
||||
private readonly List<GlobalVariableDefinitionNode> _variableDefinitions;
|
||||
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>()
|
||||
.Select(f => new Func(f.Name, f.Parameters, f.Body, f.ReturnType))
|
||||
.ToList();
|
||||
|
||||
var externFunctions = definitions
|
||||
var externFunctions = file.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();
|
||||
_variables = new Stack<Variable>();
|
||||
_functions.AddRange(functions);
|
||||
_functions.AddRange(externFunctions);
|
||||
_variableDefinitions.AddRange(file.Definitions.OfType<GlobalVariableDefinitionNode>());
|
||||
|
||||
foreach (var include in file.Includes)
|
||||
{
|
||||
ResolveFunctions(deps[include], deps);
|
||||
}
|
||||
}
|
||||
|
||||
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.Parsing;
|
||||
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);
|
||||
|
||||
Dictionary<string, FileNode> files = [];
|
||||
|
||||
Queue<string> queue = [];
|
||||
queue.Enqueue(rootFileName);
|
||||
|
||||
while (queue.TryDequeue(out var path))
|
||||
{
|
||||
var src = File.ReadAllText(Path.Combine(rootPath, path));
|
||||
|
||||
var lexer = new Lexer(src);
|
||||
var tokens = lexer.Lex();
|
||||
|
||||
var parser = new Parser(tokens);
|
||||
var definitions = parser.Parse();
|
||||
var file = parser.ParseFile(path);
|
||||
files[path] = file;
|
||||
|
||||
var typer = new ExpressionTyper(definitions);
|
||||
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 CustomGenerator(definitions);
|
||||
|
||||
var generator = new Generator(files[rootFileName], files);
|
||||
var asm = generator.Generate();
|
||||
|
||||
Console.WriteLine(asm);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace core;
|
||||
include "core/string.nub"
|
||||
|
||||
let SYS_WRITE = 1;
|
||||
let STD_IN = 0;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
global strlen
|
||||
global strcmp
|
||||
section .text
|
||||
|
||||
strcmp:
|
||||
@@ -18,3 +19,14 @@ strcmp:
|
||||
.equal:
|
||||
mov rax, 1
|
||||
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;
|
||||
@@ -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;
|
||||
using core;
|
||||
include "core/print.nub";
|
||||
|
||||
func main() {
|
||||
core::println("test");
|
||||
core::println(true);
|
||||
println("test");
|
||||
println(true);
|
||||
|
||||
if true {
|
||||
core::println("1");
|
||||
if strlen("1") == 1 {
|
||||
println("1");
|
||||
} else if false {
|
||||
core::println("2");
|
||||
println("2");
|
||||
} else if true {
|
||||
core::println("3");
|
||||
println("3");
|
||||
} else {
|
||||
core::println("4");
|
||||
println("4");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/bin/sh
|
||||
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/strcmp.asm -o strcmp.o
|
||||
nasm -g -felf64 ../input/core/string.asm -o string.o
|
||||
|
||||
ld -o out out.o strlen.o strcmp.o
|
||||
ld -o out out.o string.o
|
||||
|
||||
Reference in New Issue
Block a user