...
This commit is contained in:
@@ -1,306 +0,0 @@
|
||||
using System.Text;
|
||||
using Nub.Lang.Frontend.Parsing;
|
||||
|
||||
namespace Nub.Lang.Backend.Custom;
|
||||
|
||||
public class ExpressionGenerator
|
||||
{
|
||||
private readonly StringBuilder _builder;
|
||||
private readonly SymbolTable _symbolTable;
|
||||
|
||||
public ExpressionGenerator(SymbolTable symbolTable, StringBuilder builder)
|
||||
{
|
||||
_symbolTable = symbolTable;
|
||||
_builder = builder;
|
||||
}
|
||||
|
||||
public void GenerateExpression(ExpressionNode expression, LocalFunc func)
|
||||
{
|
||||
switch (expression)
|
||||
{
|
||||
case BinaryExpressionNode binaryExpression:
|
||||
GenerateBinaryExpression(binaryExpression, func);
|
||||
break;
|
||||
case FuncCallExpressionNode funcCallExpression:
|
||||
GenerateFuncCall(funcCallExpression.FuncCall, func);
|
||||
break;
|
||||
case IdentifierNode identifier:
|
||||
GenerateIdentifier(identifier, func);
|
||||
break;
|
||||
case LiteralNode literal:
|
||||
GenerateLiteral(literal);
|
||||
break;
|
||||
case SyscallExpressionNode syscallExpression:
|
||||
GenerateSyscall(syscallExpression.Syscall, func);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(expression));
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateBinaryExpression(BinaryExpressionNode binaryExpression, LocalFunc func)
|
||||
{
|
||||
GenerateExpression(binaryExpression.Left, func);
|
||||
_builder.AppendLine(" push rax");
|
||||
GenerateExpression(binaryExpression.Right, func);
|
||||
_builder.AppendLine(" mov rbx, rax");
|
||||
_builder.AppendLine(" pop rax");
|
||||
|
||||
switch (binaryExpression.Operator)
|
||||
{
|
||||
case BinaryExpressionOperator.Equal:
|
||||
GenerateComparison(binaryExpression.Left.Type);
|
||||
_builder.AppendLine(" sete al");
|
||||
_builder.AppendLine(" movzx rax, al");
|
||||
break;
|
||||
case BinaryExpressionOperator.NotEqual:
|
||||
GenerateComparison(binaryExpression.Left.Type);
|
||||
_builder.AppendLine(" setne al");
|
||||
_builder.AppendLine(" movzx rax, al");
|
||||
break;
|
||||
case BinaryExpressionOperator.GreaterThan:
|
||||
GenerateComparison(binaryExpression.Left.Type);
|
||||
_builder.AppendLine(" setg al");
|
||||
_builder.AppendLine(" movzx rax, al");
|
||||
break;
|
||||
case BinaryExpressionOperator.GreaterThanOrEqual:
|
||||
GenerateComparison(binaryExpression.Left.Type);
|
||||
_builder.AppendLine(" setge al");
|
||||
_builder.AppendLine(" movzx rax, al");
|
||||
break;
|
||||
case BinaryExpressionOperator.LessThan:
|
||||
GenerateComparison(binaryExpression.Left.Type);
|
||||
_builder.AppendLine(" setl al");
|
||||
_builder.AppendLine(" movzx rax, al");
|
||||
break;
|
||||
case BinaryExpressionOperator.LessThanOrEqual:
|
||||
GenerateComparison(binaryExpression.Left.Type);
|
||||
_builder.AppendLine(" setle al");
|
||||
_builder.AppendLine(" movzx rax, al");
|
||||
break;
|
||||
case BinaryExpressionOperator.Plus:
|
||||
GenerateBinaryAddition(binaryExpression.Left.Type);
|
||||
break;
|
||||
case BinaryExpressionOperator.Minus:
|
||||
GenerateBinarySubtraction(binaryExpression.Left.Type);
|
||||
break;
|
||||
case BinaryExpressionOperator.Multiply:
|
||||
GenerateBinaryMultiplication(binaryExpression.Left.Type);
|
||||
break;
|
||||
case BinaryExpressionOperator.Divide:
|
||||
GenerateBinaryDivision(binaryExpression.Left.Type);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateComparison(Type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DelegateType:
|
||||
throw new NotSupportedException($"Comparison on type {type.GetType().Name} is not supported");
|
||||
break;
|
||||
case PrimitiveType:
|
||||
_builder.AppendLine(" cmp rax, rax");
|
||||
break;
|
||||
case StringType:
|
||||
_builder.AppendLine(" mov rdi, rax");
|
||||
_builder.AppendLine(" mov rsi, rbx");
|
||||
_builder.AppendLine(" call strcmp");
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type));
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateBinaryAddition(Type type)
|
||||
{
|
||||
if (type is not PrimitiveType primitiveType)
|
||||
{
|
||||
throw new InvalidOperationException("Addition can only be done on primitive types");
|
||||
}
|
||||
|
||||
switch (primitiveType.Kind)
|
||||
{
|
||||
case PrimitiveTypeKind.Int64:
|
||||
_builder.AppendLine(" add rax, rbx");
|
||||
break;
|
||||
case PrimitiveTypeKind.Int32:
|
||||
_builder.AppendLine(" add eax, ebx");
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid type {primitiveType.Kind}");
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateBinarySubtraction(Type type)
|
||||
{
|
||||
if (type is not PrimitiveType primitiveType)
|
||||
{
|
||||
throw new InvalidOperationException("Subtraction can only be done on primitive types");
|
||||
}
|
||||
|
||||
switch (primitiveType.Kind)
|
||||
{
|
||||
case PrimitiveTypeKind.Int64:
|
||||
_builder.AppendLine(" sub rax, rbx");
|
||||
break;
|
||||
case PrimitiveTypeKind.Int32:
|
||||
_builder.AppendLine(" sub eax, ebx");
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid type {primitiveType.Kind}");
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateBinaryMultiplication(Type type)
|
||||
{
|
||||
if (type is not PrimitiveType primitiveType)
|
||||
{
|
||||
throw new InvalidOperationException("Multiplication can only be done on primitive types");
|
||||
}
|
||||
|
||||
switch (primitiveType.Kind)
|
||||
{
|
||||
case PrimitiveTypeKind.Int64:
|
||||
_builder.AppendLine(" imul rbx");
|
||||
break;
|
||||
case PrimitiveTypeKind.Int32:
|
||||
_builder.AppendLine(" imul ebx");
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid type {primitiveType.Kind}");
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateBinaryDivision(Type type)
|
||||
{
|
||||
if (type is not PrimitiveType primitiveType)
|
||||
{
|
||||
throw new InvalidOperationException("Division can only be done on primitive types");
|
||||
}
|
||||
|
||||
switch (primitiveType.Kind)
|
||||
{
|
||||
case PrimitiveTypeKind.Int64:
|
||||
_builder.AppendLine(" cqo");
|
||||
_builder.AppendLine(" idiv rbx");
|
||||
break;
|
||||
case PrimitiveTypeKind.Int32:
|
||||
_builder.AppendLine(" cdq");
|
||||
_builder.AppendLine(" idiv ebx");
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid type {primitiveType.Kind}");
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateIdentifier(IdentifierNode identifier, LocalFunc func)
|
||||
{
|
||||
var variable = func.ResolveVariable(identifier.Identifier);
|
||||
|
||||
switch (variable)
|
||||
{
|
||||
case GlobalVariable globalVariable:
|
||||
_builder.AppendLine($" mov rax, [{globalVariable.Identifier}]");
|
||||
break;
|
||||
case LocalVariable localVariable:
|
||||
{
|
||||
_builder.AppendLine($" mov rax, [rbp - {localVariable.Offset}]");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(variable));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateLiteral(LiteralNode literal)
|
||||
{
|
||||
switch (literal.Type)
|
||||
{
|
||||
case DelegateType:
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
break;
|
||||
}
|
||||
case StringType:
|
||||
{
|
||||
var ident = _symbolTable.LabelFactory.Create();
|
||||
_symbolTable.DefineString(ident, literal.Literal);
|
||||
_builder.AppendLine($" mov rax, {ident}");
|
||||
break;
|
||||
}
|
||||
case PrimitiveType primitive:
|
||||
{
|
||||
switch (primitive.Kind)
|
||||
{
|
||||
case PrimitiveTypeKind.Bool:
|
||||
_builder.AppendLine($" mov rax, {(bool.Parse(literal.Literal) ? "1" : "0")}");
|
||||
break;
|
||||
case PrimitiveTypeKind.Char:
|
||||
_builder.AppendLine($" mov rax, '{literal.Literal}'");
|
||||
break;
|
||||
case PrimitiveTypeKind.Int64:
|
||||
_builder.AppendLine($" mov rax, {literal.Literal}");
|
||||
break;
|
||||
case PrimitiveTypeKind.Int32:
|
||||
_builder.AppendLine($" mov rax, {literal.Literal}");
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Cannot convert literal to string");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
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"];
|
||||
|
||||
for (var i = funcCall.Parameters.Count - 1; i >= 0; i--)
|
||||
{
|
||||
GenerateExpression(funcCall.Parameters.ElementAt(i), func);
|
||||
_builder.AppendLine(" push rax");
|
||||
}
|
||||
|
||||
var registerParameters = Math.Min(registers.Length, funcCall.Parameters.Count);
|
||||
var stackParameters = funcCall.Parameters.Count - registerParameters;
|
||||
|
||||
for (var i = 0; i < registerParameters; i++)
|
||||
{
|
||||
_builder.AppendLine($" pop {registers[i]}");
|
||||
}
|
||||
|
||||
_builder.AppendLine($" call {symbol.StartLabel}");
|
||||
if (stackParameters != 0)
|
||||
{
|
||||
_builder.AppendLine($" add rsp, {stackParameters}");
|
||||
}
|
||||
}
|
||||
|
||||
public void GenerateSyscall(Syscall syscall, LocalFunc func)
|
||||
{
|
||||
string[] registers = ["rax", "rdi", "rsi", "rdx", "r10", "r8", "r9"];
|
||||
|
||||
foreach (var parameter in syscall.Parameters)
|
||||
{
|
||||
GenerateExpression(parameter, func);
|
||||
_builder.AppendLine(" push rax");
|
||||
}
|
||||
|
||||
for (var i = syscall.Parameters.Count - 1; i >= 0; i--)
|
||||
{
|
||||
_builder.AppendLine($" pop {registers[i]}");
|
||||
}
|
||||
|
||||
_builder.AppendLine(" syscall");
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
@@ -10,16 +10,12 @@ public class Generator
|
||||
private readonly List<DefinitionNode> _definitions;
|
||||
private readonly SymbolTable _symbolTable;
|
||||
private readonly StringBuilder _builder;
|
||||
private readonly ExpressionGenerator _expressionGenerator;
|
||||
private readonly FuncGenerator _funcGenerator;
|
||||
|
||||
public Generator(IReadOnlyCollection<DefinitionNode> definitions)
|
||||
{
|
||||
_definitions = [];
|
||||
_builder = new StringBuilder();
|
||||
_symbolTable = new SymbolTable();
|
||||
_expressionGenerator = new ExpressionGenerator(_symbolTable, _builder);
|
||||
_funcGenerator = new FuncGenerator(_symbolTable, _builder, _expressionGenerator);
|
||||
|
||||
foreach (var globalVariableDefinition in definitions.OfType<GlobalVariableDefinitionNode>())
|
||||
{
|
||||
@@ -67,7 +63,7 @@ public class Generator
|
||||
foreach (var globalVariable in _definitions.OfType<GlobalVariableDefinitionNode>())
|
||||
{
|
||||
var symbol = _symbolTable.ResolveGlobalVariable(globalVariable.Name);
|
||||
_expressionGenerator.GenerateExpression(globalVariable.Value, main);
|
||||
GenerateExpression(globalVariable.Value, main);
|
||||
_builder.AppendLine($" mov [{symbol.Identifier}], rax");
|
||||
}
|
||||
|
||||
@@ -85,7 +81,7 @@ public class Generator
|
||||
foreach (var funcDefinition in _definitions.OfType<LocalFuncDefinitionNode>())
|
||||
{
|
||||
_builder.AppendLine();
|
||||
_builder.AppendLine(_funcGenerator.GenerateFuncDefinition(funcDefinition));
|
||||
_builder.AppendLine(GenerateFuncDefinition(funcDefinition));
|
||||
}
|
||||
|
||||
_builder.AppendLine("""
|
||||
@@ -118,4 +114,420 @@ public class Generator
|
||||
|
||||
return _builder.ToString();
|
||||
}
|
||||
|
||||
private 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:
|
||||
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 = _symbolTable.LabelFactory.Create();
|
||||
GenerateIf(ifStatement, endLabel, func);
|
||||
_builder.AppendLine($"{endLabel}:");
|
||||
}
|
||||
|
||||
private void GenerateIf(IfNode ifStatement, string endLabel, LocalFunc func)
|
||||
{
|
||||
var nextLabel = _symbolTable.LabelFactory.Create();
|
||||
GenerateExpression(ifStatement.Condition, func);
|
||||
_builder.AppendLine(" cmp rax, 0");
|
||||
_builder.AppendLine($" je {nextLabel}");
|
||||
GenerateBlock(ifStatement.Body, func);
|
||||
_builder.AppendLine($" jmp {endLabel}");
|
||||
_builder.AppendLine($"{nextLabel}:");
|
||||
|
||||
if (ifStatement.Else.HasValue)
|
||||
{
|
||||
ifStatement.Else.Value.Match
|
||||
(
|
||||
elseIfStatement => GenerateIf(elseIfStatement, endLabel, func),
|
||||
elseStatement => GenerateBlock(elseStatement, func)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateReturn(ReturnNode @return, LocalFunc func)
|
||||
{
|
||||
if (@return.Value.HasValue)
|
||||
{
|
||||
GenerateExpression(@return.Value.Value, func);
|
||||
}
|
||||
|
||||
_builder.AppendLine($" jmp {func.EndLabel}");
|
||||
}
|
||||
|
||||
private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment, LocalFunc func)
|
||||
{
|
||||
var variable = func.ResolveLocalVariable(variableAssignment.Name);
|
||||
GenerateExpression(variableAssignment.Value, func);
|
||||
_builder.AppendLine($" mov [rbp - {variable.Offset}], rax");
|
||||
}
|
||||
|
||||
private void GenerateVariableReassignment(VariableReassignmentNode variableReassignment, LocalFunc func)
|
||||
{
|
||||
var variable = func.ResolveLocalVariable(variableReassignment.Name);
|
||||
GenerateExpression(variableReassignment.Value, func);
|
||||
_builder.AppendLine($" mov [rbp - {variable.Offset}], rax");
|
||||
}
|
||||
|
||||
private void GenerateExpression(ExpressionNode expression, LocalFunc func)
|
||||
{
|
||||
switch (expression)
|
||||
{
|
||||
case BinaryExpressionNode binaryExpression:
|
||||
GenerateBinaryExpression(binaryExpression, func);
|
||||
break;
|
||||
case FuncCallExpressionNode funcCallExpression:
|
||||
GenerateFuncCall(funcCallExpression.FuncCall, func);
|
||||
break;
|
||||
case IdentifierNode identifier:
|
||||
GenerateIdentifier(identifier, func);
|
||||
break;
|
||||
case LiteralNode literal:
|
||||
GenerateLiteral(literal);
|
||||
break;
|
||||
case SyscallExpressionNode syscallExpression:
|
||||
GenerateSyscall(syscallExpression.Syscall, func);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(expression));
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateBinaryExpression(BinaryExpressionNode binaryExpression, LocalFunc func)
|
||||
{
|
||||
GenerateExpression(binaryExpression.Left, func);
|
||||
_builder.AppendLine(" push rax");
|
||||
GenerateExpression(binaryExpression.Right, func);
|
||||
_builder.AppendLine(" mov rbx, rax");
|
||||
_builder.AppendLine(" pop rax");
|
||||
|
||||
switch (binaryExpression.Operator)
|
||||
{
|
||||
case BinaryExpressionOperator.Equal:
|
||||
GenerateComparison(binaryExpression.Left.Type);
|
||||
_builder.AppendLine(" sete al");
|
||||
_builder.AppendLine(" movzx rax, al");
|
||||
break;
|
||||
case BinaryExpressionOperator.NotEqual:
|
||||
GenerateComparison(binaryExpression.Left.Type);
|
||||
_builder.AppendLine(" setne al");
|
||||
_builder.AppendLine(" movzx rax, al");
|
||||
break;
|
||||
case BinaryExpressionOperator.GreaterThan:
|
||||
GenerateComparison(binaryExpression.Left.Type);
|
||||
_builder.AppendLine(" setg al");
|
||||
_builder.AppendLine(" movzx rax, al");
|
||||
break;
|
||||
case BinaryExpressionOperator.GreaterThanOrEqual:
|
||||
GenerateComparison(binaryExpression.Left.Type);
|
||||
_builder.AppendLine(" setge al");
|
||||
_builder.AppendLine(" movzx rax, al");
|
||||
break;
|
||||
case BinaryExpressionOperator.LessThan:
|
||||
GenerateComparison(binaryExpression.Left.Type);
|
||||
_builder.AppendLine(" setl al");
|
||||
_builder.AppendLine(" movzx rax, al");
|
||||
break;
|
||||
case BinaryExpressionOperator.LessThanOrEqual:
|
||||
GenerateComparison(binaryExpression.Left.Type);
|
||||
_builder.AppendLine(" setle al");
|
||||
_builder.AppendLine(" movzx rax, al");
|
||||
break;
|
||||
case BinaryExpressionOperator.Plus:
|
||||
GenerateBinaryAddition(binaryExpression.Left.Type);
|
||||
break;
|
||||
case BinaryExpressionOperator.Minus:
|
||||
GenerateBinarySubtraction(binaryExpression.Left.Type);
|
||||
break;
|
||||
case BinaryExpressionOperator.Multiply:
|
||||
GenerateBinaryMultiplication(binaryExpression.Left.Type);
|
||||
break;
|
||||
case BinaryExpressionOperator.Divide:
|
||||
GenerateBinaryDivision(binaryExpression.Left.Type);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(binaryExpression.Operator));
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateComparison(Type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DelegateType:
|
||||
throw new NotSupportedException($"Comparison on type {type.GetType().Name} is not supported");
|
||||
case PrimitiveType:
|
||||
_builder.AppendLine(" cmp rax, rax");
|
||||
break;
|
||||
case StringType:
|
||||
_builder.AppendLine(" mov rdi, rax");
|
||||
_builder.AppendLine(" mov rsi, rbx");
|
||||
_builder.AppendLine(" call strcmp");
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type));
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateBinaryAddition(Type type)
|
||||
{
|
||||
if (type is not PrimitiveType primitiveType)
|
||||
{
|
||||
throw new InvalidOperationException("Addition can only be done on primitive types");
|
||||
}
|
||||
|
||||
switch (primitiveType.Kind)
|
||||
{
|
||||
case PrimitiveTypeKind.Int64:
|
||||
_builder.AppendLine(" add rax, rbx");
|
||||
break;
|
||||
case PrimitiveTypeKind.Int32:
|
||||
_builder.AppendLine(" add eax, ebx");
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid type {primitiveType.Kind}");
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateBinarySubtraction(Type type)
|
||||
{
|
||||
if (type is not PrimitiveType primitiveType)
|
||||
{
|
||||
throw new InvalidOperationException("Subtraction can only be done on primitive types");
|
||||
}
|
||||
|
||||
switch (primitiveType.Kind)
|
||||
{
|
||||
case PrimitiveTypeKind.Int64:
|
||||
_builder.AppendLine(" sub rax, rbx");
|
||||
break;
|
||||
case PrimitiveTypeKind.Int32:
|
||||
_builder.AppendLine(" sub eax, ebx");
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid type {primitiveType.Kind}");
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateBinaryMultiplication(Type type)
|
||||
{
|
||||
if (type is not PrimitiveType primitiveType)
|
||||
{
|
||||
throw new InvalidOperationException("Multiplication can only be done on primitive types");
|
||||
}
|
||||
|
||||
switch (primitiveType.Kind)
|
||||
{
|
||||
case PrimitiveTypeKind.Int64:
|
||||
_builder.AppendLine(" imul rbx");
|
||||
break;
|
||||
case PrimitiveTypeKind.Int32:
|
||||
_builder.AppendLine(" imul ebx");
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid type {primitiveType.Kind}");
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateBinaryDivision(Type type)
|
||||
{
|
||||
if (type is not PrimitiveType primitiveType)
|
||||
{
|
||||
throw new InvalidOperationException("Division can only be done on primitive types");
|
||||
}
|
||||
|
||||
switch (primitiveType.Kind)
|
||||
{
|
||||
case PrimitiveTypeKind.Int64:
|
||||
_builder.AppendLine(" cqo");
|
||||
_builder.AppendLine(" idiv rbx");
|
||||
break;
|
||||
case PrimitiveTypeKind.Int32:
|
||||
_builder.AppendLine(" cdq");
|
||||
_builder.AppendLine(" idiv ebx");
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid type {primitiveType.Kind}");
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateIdentifier(IdentifierNode identifier, LocalFunc func)
|
||||
{
|
||||
var variable = func.ResolveVariable(identifier.Identifier);
|
||||
|
||||
switch (variable)
|
||||
{
|
||||
case GlobalVariable globalVariable:
|
||||
_builder.AppendLine($" mov rax, [{globalVariable.Identifier}]");
|
||||
break;
|
||||
case LocalVariable localVariable:
|
||||
{
|
||||
_builder.AppendLine($" mov rax, [rbp - {localVariable.Offset}]");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(variable));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateLiteral(LiteralNode literal)
|
||||
{
|
||||
switch (literal.Type)
|
||||
{
|
||||
case DelegateType:
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
case StringType:
|
||||
{
|
||||
var ident = _symbolTable.LabelFactory.Create();
|
||||
_symbolTable.DefineString(ident, literal.Literal);
|
||||
_builder.AppendLine($" mov rax, {ident}");
|
||||
break;
|
||||
}
|
||||
case PrimitiveType primitive:
|
||||
{
|
||||
switch (primitive.Kind)
|
||||
{
|
||||
case PrimitiveTypeKind.Bool:
|
||||
_builder.AppendLine($" mov rax, {(bool.Parse(literal.Literal) ? "1" : "0")}");
|
||||
break;
|
||||
case PrimitiveTypeKind.Char:
|
||||
_builder.AppendLine($" mov rax, '{literal.Literal}'");
|
||||
break;
|
||||
case PrimitiveTypeKind.Int64:
|
||||
_builder.AppendLine($" mov rax, {literal.Literal}");
|
||||
break;
|
||||
case PrimitiveTypeKind.Int32:
|
||||
_builder.AppendLine($" mov rax, {literal.Literal}");
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Cannot convert literal to string");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateFuncCall(FuncCall funcCall, LocalFunc func)
|
||||
{
|
||||
var symbol = _symbolTable.ResolveFunc(funcCall.Name, funcCall.Parameters.Select(p => p.Type).ToList());
|
||||
string[] registers = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
|
||||
|
||||
for (var i = funcCall.Parameters.Count - 1; i >= 0; i--)
|
||||
{
|
||||
GenerateExpression(funcCall.Parameters.ElementAt(i), func);
|
||||
_builder.AppendLine(" push rax");
|
||||
}
|
||||
|
||||
var registerParameters = Math.Min(registers.Length, funcCall.Parameters.Count);
|
||||
var stackParameters = funcCall.Parameters.Count - registerParameters;
|
||||
|
||||
for (var i = 0; i < registerParameters; i++)
|
||||
{
|
||||
_builder.AppendLine($" pop {registers[i]}");
|
||||
}
|
||||
|
||||
_builder.AppendLine($" call {symbol.StartLabel}");
|
||||
if (stackParameters != 0)
|
||||
{
|
||||
_builder.AppendLine($" add rsp, {stackParameters}");
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateSyscall(Syscall syscall, LocalFunc func)
|
||||
{
|
||||
string[] registers = ["rax", "rdi", "rsi", "rdx", "r10", "r8", "r9"];
|
||||
|
||||
foreach (var parameter in syscall.Parameters)
|
||||
{
|
||||
GenerateExpression(parameter, func);
|
||||
_builder.AppendLine(" push rax");
|
||||
}
|
||||
|
||||
for (var i = syscall.Parameters.Count - 1; i >= 0; i--)
|
||||
{
|
||||
_builder.AppendLine($" pop {registers[i]}");
|
||||
}
|
||||
|
||||
_builder.AppendLine(" syscall");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user