summaryrefslogtreecommitdiff
path: root/Compiler/visitors
diff options
context:
space:
mode:
Diffstat (limited to 'Compiler/visitors')
-rw-r--r--Compiler/visitors/Constants.cs50
-rw-r--r--Compiler/visitors/Definitions.cs123
-rw-r--r--Compiler/visitors/Expressions.cs134
-rw-r--r--Compiler/visitors/FlowControl.cs177
-rw-r--r--Compiler/visitors/Main.cs183
-rw-r--r--Compiler/visitors/Operations.cs305
-rw-r--r--Compiler/visitors/Targets.cs40
7 files changed, 1012 insertions, 0 deletions
diff --git a/Compiler/visitors/Constants.cs b/Compiler/visitors/Constants.cs
new file mode 100644
index 0000000..598c642
--- /dev/null
+++ b/Compiler/visitors/Constants.cs
@@ -0,0 +1,50 @@
+namespace Q1.Compiler.visitors;
+
+using System.Text;
+
+public partial class CCompilerVisitor
+{
+ public Encoding Encoding;
+ public override string VisitIntConstant(CGrammarParser.IntConstantContext context)
+ {
+ string valueString = context.INT().GetText();
+ if (int.TryParse(valueString, out int value))
+ return $"{value}";
+ else
+ throw new CompilerException($"Invalid integer value: {valueString}");
+ }
+ public override string VisitHexConstant(CGrammarParser.HexConstantContext context)
+ {
+ string valueString = context.HEX().GetText();
+ valueString = valueString[2..]; // Remove 0x
+ return $"${valueString}";
+ }
+
+ public override object? VisitCharConstant(CGrammarParser.CharConstantContext context)
+ {
+ char c = context.LETTER().GetText()[1];
+ return $"{this.Encoding.GetBytes(c.ToString())[0]}";
+ }
+
+ public override object? VisitStringConstant(CGrammarParser.StringConstantContext context)
+ {
+ string text = context.STRING().GetText()[1..^1];
+
+ int? arrPtr = null;
+
+ if (text.Length == 0)
+ throw new CompilerException("Cannot create empty string");
+
+ foreach (char c in text)
+ {
+ int itemPtr = Alloc(CType.Int.Size);
+
+ if (arrPtr == null)
+ arrPtr = itemPtr;
+
+ this.DynamicMove(CType.Int, $"{this.Encoding.GetBytes(c.ToString())[0]}", $"[${itemPtr:X4}]");
+ }
+
+ return $"${arrPtr:X4}";
+ }
+} \ No newline at end of file
diff --git a/Compiler/visitors/Definitions.cs b/Compiler/visitors/Definitions.cs
new file mode 100644
index 0000000..e4cf797
--- /dev/null
+++ b/Compiler/visitors/Definitions.cs
@@ -0,0 +1,123 @@
+namespace Q1.Compiler.visitors;
+
+public partial class CCompilerVisitor
+{
+ private List<(CGrammarParser.FunctionDefinitionContext context,
+ FunctionReference function, FunctionOverload overload,
+ List<string> paramNames, List<CType> paramTypes)> _functionBodies = new();
+
+ private VariableReference FindVariable(string varName)
+ {
+ foreach (Dictionary<string, VariableReference> variables in this._variableStack)
+ {
+ if (variables.TryGetValue(varName, out VariableReference? reference))
+ return reference;
+ }
+
+ throw new CompilerException($"Variable {varName} could not be found in current scope.");
+ }
+ private int Alloc(int size)
+ {
+ int loc = CCompilerVisitor._memPointer;
+ CCompilerVisitor._memPointer += size;
+
+ return loc;
+ }
+ private FunctionReference CreateOrGetFunctionReference(string name, CType type)
+ {
+ FunctionReference? function = this._methods.FirstOrDefault(x => x.Name == name);
+ if (function != null)
+ return function;
+
+ function = new FunctionReference(name, type);
+ this._methods.Add(function);
+
+ return function;
+ }
+ private FunctionReference GetFunctionReference(string name)
+ {
+ FunctionReference? function = this._methods.FirstOrDefault(x => x.Name == name);
+ if (function != null)
+ return function;
+
+ throw new CompilerException($"Function '{name}' has not been defined or prototyped.");
+ }
+
+ public override string? VisitFunctionPrototype(CGrammarParser.FunctionPrototypeContext context)
+ {
+ string functionName = context.name.Text;
+ CType type = this.VisitCType(context._type);
+
+ List<CType> parameterTypes = context._types.Select(this.VisitCType).ToList();
+ List<string> parameterNames = context._params.Select(x => x.Text).ToList();
+
+ FunctionReference function = this.CreateOrGetFunctionReference(functionName, type);
+ FunctionOverload overload = function.CreateOverloadWithoutBody(parameterNames, parameterTypes);
+
+ function.Overloads.Add(overload);
+
+ return null;
+ }
+ public override string? VisitFunctionDefinition(CGrammarParser.FunctionDefinitionContext context)
+ {
+ string functionName = context.name.Text;
+ CType type = this.VisitCType(context.rtype);
+
+ List<CType> paramTypes = context._types.Select(this.VisitCType).ToList();
+ List<string> paramNames = context._params.Select(x => x.Text).ToList();
+
+ FunctionReference function = this.CreateOrGetFunctionReference(functionName, type);
+ FunctionOverload overload = function.CreateOverloadWithBody(paramNames, paramTypes);
+
+ this._functionBodies.Add((context, function, overload, paramNames, paramTypes));
+
+ return null;
+ }
+ private void VisitFunctionBody(CGrammarParser.FunctionDefinitionContext context, FunctionReference function, FunctionOverload overload, List<string> paramNames, List<CType> paramTypes)
+ {
+ this.Branch(function.GetBranchName(overload), function.GetBranchFriendlyName(overload));
+
+ if (paramNames.Count > 0)
+ this.Instruction("pop V0", "preserve function return address");
+
+ Dictionary<string, VariableReference> scope = this.EnterScope();
+ foreach ((string paramName, CType paramType) in paramNames.Zip(paramTypes))
+ {
+ int addr = this.Alloc(paramType.Size);
+ scope[paramName] = new(addr, paramType);
+ this.Instruction($"pop [${addr:X4}]", $"param {paramType} {paramName}");
+ }
+
+ if (paramNames.Count > 0)
+ this.Instruction("push V0", "restore function return address");
+
+ this.VisitChildren(context);
+ this.ExitScope();
+
+ this.Instruction("mov 0, V0", "fallback return 0");
+ this.Instruction("ret");
+ this.Blank();
+ }
+ public override string? VisitVariableDeclaration(CGrammarParser.VariableDeclarationContext context)
+ {
+ CType type = this.VisitCType(context.type());
+ int addr = this.Alloc(CType.Int.Size);
+ string name = context.name.Text;
+
+ Dictionary<string, VariableReference> scope = this._variableStack.Peek();
+ if (scope.ContainsKey(name))
+ throw new CompilerException($"Variable {name} already exists in current scope.");
+
+ scope[name] = new VariableReference(addr, type);
+ string initialValue = "0";
+
+ if (context.value != null)
+ {
+ initialValue = this.VisitExpression(context.value);
+ }
+
+ this.DynamicMove(CType.Int, initialValue, $"[${addr:X4}]", $"int {name} = {initialValue}");
+
+ return null;
+ }
+} \ No newline at end of file
diff --git a/Compiler/visitors/Expressions.cs b/Compiler/visitors/Expressions.cs
new file mode 100644
index 0000000..e67688d
--- /dev/null
+++ b/Compiler/visitors/Expressions.cs
@@ -0,0 +1,134 @@
+namespace Q1.Compiler.visitors;
+
+public partial class CCompilerVisitor
+{
+ private string VisitExpression(CGrammarParser.ExpressionContext expression)
+ {
+ string? expressionString = this.Visit(expression) as string;
+ if (expressionString == null)
+ throw new CompilerException("Expression did not return a string");
+
+ return expressionString;
+ }
+
+ public override string VisitCallExpression(CGrammarParser.CallExpressionContext context)
+ {
+ string functionName = context.ID().GetText();
+
+ FunctionReference function = this.GetFunctionReference(functionName);
+ FunctionOverload overload = function.GetOverload(context._params.Count);
+
+ // We reverse arugments so when we perform pop, its in the right order
+ CGrammarParser.ExpressionContext[] argumentsReversed = context._params.Reverse().ToArray();
+
+ for (int i = 0; i < argumentsReversed.Length; i++)
+ {
+ CGrammarParser.ExpressionContext argument = argumentsReversed[i];
+ string result = this.VisitExpression(argument);
+
+ this.Instruction($"push {result}", $"arg {overload.ParameterTypes[i]} {overload.ParameterNames[i]}");
+ }
+
+ this.Instruction($"call {function.GetBranchName(overload)}", function.GetBranchFriendlyName(overload));
+
+ return "V0";
+ }
+ public override string VisitParenthesizedExpression(CGrammarParser.ParenthesizedExpressionContext context)
+ {
+ return this.VisitExpression(context.expression());
+ }
+ public override string VisitVariableExpression(CGrammarParser.VariableExpressionContext context)
+ {
+ string varName = context.ID().GetText();
+ VariableReference reference = this.FindVariable(varName);
+
+ return $"[${reference.Address:X4}]";
+ }
+ public override string VisitAssignmentExpression(CGrammarParser.AssignmentExpressionContext context)
+ {
+ Target target = this.VisitTarget(context.target());
+ string value = this.VisitExpression(context.expression());
+
+ this.DynamicMove(target.Type, value, target.Value, $"{target.Value} = {value}");
+ return target.Value;
+ }
+ public override string VisitTernaryExpression(CGrammarParser.TernaryExpressionContext context)
+ {
+ string cond = this.VisitExpression(context.cond);
+
+ string thenBranch = this.GenerateBranchLabel("then");
+ string endBranch = this.GenerateBranchLabel("end");
+ string result;
+
+ // Else branch is first since it saves us from having to perform a NOT operation.
+ // If cond, jump to then, else fall through to else, then jump to end
+
+ this.Instruction($"bz {cond}", "check ternary condition");
+ this.Instruction($"jmp {thenBranch}");
+
+ result = this.VisitExpression(context.@else);
+ this.Instruction($"mov {result}, V0", "store ternary fail result");
+ this.Instruction($"jmp {endBranch}");
+
+ this.Branch(thenBranch);
+ result = this.VisitExpression(context.then);
+ this.Instruction($"mov {result}, V0", "store ternary success result");
+
+ this.Branch(endBranch);
+
+ return "V0";
+ }
+ public override string VisitAddressOfExpression(CGrammarParser.AddressOfExpressionContext context)
+ {
+ Target target = this.VisitTarget(context.target());
+
+ return target.Address;
+ }
+ public override string VisitDereferenceExpression(CGrammarParser.DereferenceExpressionContext context)
+ {
+ string value = this.VisitExpression(context.expression());
+
+ this.Instruction($"mov {value}, V0", "move pointer into register for dereference");
+ return "[V0]";
+ }
+
+ public override object? VisitIndexExpression(CGrammarParser.IndexExpressionContext context)
+ {
+ Target obj = this.VisitTarget(context.obj);
+ if (obj.Type is not CTypePtr ptr)
+ throw new CompilerException("Indexer target was not a pointer!");
+
+ this.Instruction($"push {obj.Value}", "store index obj");
+
+ string indexer = this.VisitExpression(context.indexer);
+
+ this.Instruction($"mov {indexer}, V1", "store indexer");
+ this.Instruction($"pop V0", "restore index obj");
+ this.Instruction($"mul V1, {ptr.Sub.Size}", $"multiply indexer by {ptr.Sub.Size}");
+ this.Instruction($"add AX, V0", "add indexer and index obj");
+ this.Instruction($"mov AX, V0", "move to general purpose register");
+ return "[V0]";
+ }
+
+ public override object? VisitArrayExpression(CGrammarParser.ArrayExpressionContext context)
+ {
+ CType type = this.VisitCType(context.type());
+ int? arrPtr = null;
+
+ if (context._params.Count == 0)
+ throw new CompilerException("Cannot create empty array");
+
+ foreach (CGrammarParser.ExpressionContext? param in context._params)
+ {
+ int itemPtr = Alloc(type.Size);
+
+ if (arrPtr == null)
+ arrPtr = itemPtr;
+
+ string value = this.VisitExpression(param);
+ this.DynamicMove(type, value, $"[${itemPtr:X4}]");
+ }
+
+ return $"${arrPtr:X4}";
+ }
+} \ No newline at end of file
diff --git a/Compiler/visitors/FlowControl.cs b/Compiler/visitors/FlowControl.cs
new file mode 100644
index 0000000..944595d
--- /dev/null
+++ b/Compiler/visitors/FlowControl.cs
@@ -0,0 +1,177 @@
+namespace Q1.Compiler.visitors;
+
+public partial class CCompilerVisitor
+{
+ public override string? VisitBlock(CGrammarParser.BlockContext context)
+ {
+ this.EnterScope();
+
+ this.VisitChildren(context);
+
+ this.ExitScope();
+
+ return null;
+ }
+ public override string? VisitReturnStatement(CGrammarParser.ReturnStatementContext context)
+ {
+ if (context.expression() != null)
+ {
+ string expression = this.VisitExpression(context.expression());
+ this.Instruction($"mov {expression}, V0", "return value");
+ this.Instruction($"ret");
+ }
+ else
+ this.Instruction("ret");
+
+ return null;
+ }
+ public override string? VisitIfStatement(CGrammarParser.IfStatementContext context)
+ {
+ string cond = this.VisitExpression(context.expression());
+ string endBranch = this.GenerateBranchLabel("end");
+
+ if (context.@else != null)
+ {
+ this.Instruction($"bz {cond}", "check if condition");
+ // Else branch is first since it saves us from having to perform a NOT operation.
+ // If cond, jump to then, else fall through to else, then jump to end
+
+ string thenBranch = this.GenerateBranchLabel("then");
+
+ this.Instruction($"jmp {thenBranch}");
+
+ this.GeneratedComment("if failed");
+ if (context.@else != null)
+ this.Visit(context.@else);
+ this.Instruction($"jmp {endBranch}");
+
+ this.Branch(thenBranch, "if succeeded");
+ this.Visit(context.then);
+ }
+ else
+ {
+ this.Instruction($"not {cond}");
+ this.Instruction($"bz", "check if condition");
+ this.Instruction($"jmp {endBranch}", "if succeeds, continue, else jump to end");
+ this.Visit(context.then);
+ }
+
+ this.Branch(endBranch);
+
+ return null;
+ }
+ public override string? VisitForExprStatement(CGrammarParser.ForExprStatementContext context)
+ {
+ string bodyBranch = this.GenerateBranchLabel("body");
+ string endBranch = this.GenerateBranchLabel("end");
+
+ context.LoopBodyBranch = bodyBranch;
+ context.LoopEndBranch = endBranch;
+
+ this.EnterScope();
+ this.Visit(context.initializer);
+
+ this.Branch(bodyBranch);
+ string cond = this.VisitExpression(context.cond);
+
+ this.Instruction($"not {cond}");
+ this.Instruction($"bz");
+ this.Instruction($"jmp {endBranch}", "if for condition fails, jump to end");
+
+ this.Visit(context.body);
+ this.Visit(context.post);
+
+ this.Branch(endBranch);
+ this.ExitScope();
+
+ return null;
+ }
+ public override string? VisitForDeclStatement(CGrammarParser.ForDeclStatementContext context)
+ {
+ string bodyBranch = this.GenerateBranchLabel("body");
+ string endBranch = this.GenerateBranchLabel("end");
+
+ context.LoopBodyBranch = bodyBranch;
+ context.LoopEndBranch = endBranch;
+
+ this.EnterScope();
+ this.Visit(context.initializer);
+
+ this.Branch(bodyBranch);
+ string cond = this.VisitExpression(context.cond);
+
+ this.Instruction($"not {cond}");
+ this.Instruction($"bz");
+ this.Instruction($"jmp {endBranch}", "if for condition fails, jump to end");
+
+ this.Visit(context.body);
+ this.Visit(context.post);
+
+ this.Instruction($"jmp {bodyBranch}");
+
+ this.Branch(endBranch);
+ this.ExitScope();
+
+ return null;
+ }
+ public override string? VisitWhileStatement(CGrammarParser.WhileStatementContext context)
+ {
+ string bodyBranch = this.GenerateBranchLabel("body");
+ string endBranch = this.GenerateBranchLabel("end");
+
+ context.LoopBodyBranch = bodyBranch;
+ context.LoopEndBranch = endBranch;
+
+ this.Branch(bodyBranch);
+ string cond = this.VisitExpression(context.cond);
+
+ this.Instruction($"not {cond}");
+ this.Instruction($"bz");
+ this.Instruction($"jmp {endBranch}", "if while condition fails, jump to end");
+
+ this.Visit(context.body);
+ this.Instruction($"jmp {bodyBranch}");
+
+ this.Branch(endBranch);
+
+ return null;
+ }
+ public override string? VisitDoWhileStatement(CGrammarParser.DoWhileStatementContext context)
+ {
+ string bodyBranch = this.GenerateBranchLabel("body");
+ string endBranch = this.GenerateBranchLabel("end");
+
+ context.LoopBodyBranch = bodyBranch;
+ context.LoopEndBranch = endBranch;
+
+ this.Branch(bodyBranch);
+ this.Visit(context.body);
+
+ string cond = this.VisitExpression(context.cond);
+
+ this.Instruction($"bz {cond}");
+ this.Instruction($"jmp {bodyBranch}", "if while condition succeeds, jump back to start");
+
+ this.Branch(endBranch);
+
+ return null;
+ }
+ public override string? VisitBreakStatement(CGrammarParser.BreakStatementContext context)
+ {
+ if (context.LoopEndBranch == null)
+ throw new CompilerException("Cannot break, not inside loop with end");
+
+ this.Instruction($"jmp {context.LoopEndBranch}", "break");
+
+ return null;
+ }
+ public override string? VisitContinueStatement(CGrammarParser.ContinueStatementContext context)
+ {
+ if (context.LoopBodyBranch == null)
+ throw new CompilerException("Cannot skip body (continue), not inside loop with body");
+
+ this.Instruction($"jmp {context.LoopBodyBranch}", "continue");
+
+ return null;
+ }
+} \ No newline at end of file
diff --git a/Compiler/visitors/Main.cs b/Compiler/visitors/Main.cs
new file mode 100644
index 0000000..cbd468f
--- /dev/null
+++ b/Compiler/visitors/Main.cs
@@ -0,0 +1,183 @@
+namespace Q1.Compiler.visitors;
+
+using System.Text;
+using Antlr4.Runtime.Tree;
+using Q1.Emulator;
+
+public partial class CCompilerVisitor : CGrammarBaseVisitor<object?>
+{
+ private int _branchCount = 1;
+ private List<string> _instructions = new();
+ private Stack<Dictionary<string, VariableReference>> _variableStack = new();
+ private List<FunctionReference> _methods = new();
+ private static int _memLowest = Q1Layout.FreeMemoryStart;
+ private static int _memPointer = CCompilerVisitor._memLowest;
+
+ private const int ColumnWidth = 30;
+
+ public readonly CommentCompilationMode CommentMode;
+
+ public CCompilerVisitor(CommentCompilationMode commentMode, Encoding encoding)
+ {
+ this.CommentMode = commentMode;
+ this.Encoding = encoding;
+ }
+
+ private string GenerateBranchLabel(string type)
+ {
+ return $"_{type}_{this._branchCount++}";
+ }
+ private void Instruction(string instruction, string? generatedComment = null)
+ {
+ this._instructions.Add($" {instruction}");
+ if (generatedComment != null)
+ this.CommentLastInstruction(generatedComment);
+ }
+ private void Blank()
+ {
+ this._instructions.Add("");
+ }
+ private void Branch(string name, string? generatedComment = null)
+ {
+ this._instructions.Add($"{name}:");
+ if (generatedComment != null)
+ this.CommentLastInstruction(generatedComment);
+ }
+ private void UserComment(string comment)
+ {
+ if (this.CommentMode.HasFlag(CommentCompilationMode.UserDefined))
+ this._instructions.Add($" ; {comment}");
+ }
+
+ private void GeneratedComment(string comment)
+ {
+ if (this.CommentMode.HasFlag(CommentCompilationMode.Generated))
+ this._instructions.Add($"{"".PadRight(CCompilerVisitor.ColumnWidth)}; {comment}");
+ }
+
+ private void CommentLastInstruction(string comment)
+ {
+ if (!this.CommentMode.HasFlag(CommentCompilationMode.Generated))
+ return;
+
+ if (this._instructions.Count == 0)
+ throw new Exception("Cannot add comment to last instruction, there are none");
+
+ string lastInstruction = this._instructions[^1];
+ lastInstruction = $"{lastInstruction.PadRight(CCompilerVisitor.ColumnWidth)}; {comment}";
+
+ this._instructions[^1] = lastInstruction;
+ }
+
+
+ public IEnumerable<string> Compile(CGrammarParser.ProgramContext program)
+ {
+ this.Instruction("jmp _main");
+ this.Visit(program);
+
+ foreach ((CGrammarParser.FunctionDefinitionContext context,
+ FunctionReference function, FunctionOverload overload,
+ List<string> paramNames, List<CType> paramTypes) in this._functionBodies)
+ this.VisitFunctionBody(context, function, overload, paramNames, paramTypes);
+
+ return this._instructions;
+ }
+
+ private Dictionary<string, VariableReference> EnterScope()
+ {
+ Dictionary<string, VariableReference> scope = new();
+ this._variableStack.Push(scope);
+
+ return scope;
+ }
+ private void ExitScope()
+ {
+ foreach ((string key, VariableReference value) in this._variableStack.Peek())
+ {
+ // CCompilerVisitor._memPointer -= value.Type.Size;
+ // if (CCompilerVisitor._memPointer < CCompilerVisitor._memLowest)
+ // throw new CompilerException("Memory pointer is lower than mem lowest!");
+ }
+
+ // Delete scope
+ this._variableStack.Pop();
+ }
+
+ public void DynamicMove(CType type, string source, string dest, string? comment = null)
+ {
+ if (type.Size == 1)
+ this.Instruction($"movb {source}, {dest}", comment);
+ else if (type.Size == 2)
+ this.Instruction($"mov {source}, {dest}", comment);
+ else
+ throw new CompilerException($"Cannot move {type.Size} bytes {source} to {dest}");
+ }
+
+ private CType VisitCType(CGrammarParser.TypeContext typeContext)
+ {
+ return GetCType(typeContext.GetText().Trim());
+ }
+
+ private CType GetCType(string type)
+ {
+ return type switch
+ {
+ "int" => CType.Int,
+ "char" => CType.Char,
+ _ when type.EndsWith("*") => new CTypePtr(this.GetCType(type[..^1])),
+ _ => throw new CompilerException($"Unknown CTYPE '{type}'")
+ };
+ }
+
+ public override object? VisitErrorNode(IErrorNode node)
+ {
+ if (node.Symbol.Type == CGrammarParser.COMMENT)
+ {
+ // strip // and \n and whitespace
+ string comment = node
+ .Symbol.Text
+ .TrimStart("//")
+ .TrimEnd('\n')
+ .Trim();
+
+ this.UserComment(comment);
+ }
+
+ if (node.Symbol.Type == CGrammarParser.DIRECTIVE)
+ {
+ // strip // and \n and whitespace
+ string directive = node
+ .Symbol.Text
+ .TrimStart("#")
+ .TrimEnd('\n')
+ .Trim();
+
+ this.Instruction("." + directive);
+ }
+
+ return base.VisitErrorNode(node);
+ }
+
+ public override object? Visit(IParseTree tree)
+ {
+ if (tree is Context childCtx && tree.Parent is Context ctx)
+ ctx.SyncProps(childCtx);
+
+ return base.Visit(tree);
+ }
+
+ /// <summary>
+ /// Basically identical to base version, except calls Visit on each child, making Visit not redundant.
+ /// </summary>
+ public override object? VisitChildren(IRuleNode node)
+ {
+ object? result = this.DefaultResult;
+ int childCount = node.ChildCount;
+ for (int i = 0; i < childCount && this.ShouldVisitNextChild(node, result); ++i)
+ {
+ object? nextResult = this.Visit(node.GetChild(i));
+ result = this.AggregateResult(result, nextResult);
+ }
+ return result;
+ }
+} \ No newline at end of file
diff --git a/Compiler/visitors/Operations.cs b/Compiler/visitors/Operations.cs
new file mode 100644
index 0000000..8483747
--- /dev/null
+++ b/Compiler/visitors/Operations.cs
@@ -0,0 +1,305 @@
+namespace Q1.Compiler.visitors;
+
+public partial class CCompilerVisitor
+{
+ private void BaseBinaryOperation(CGrammarParser.ExpressionContext leftExpr, CGrammarParser.ExpressionContext rightExpr, string operation, string operationFriendlyName)
+ {
+ string left = this.VisitExpression(leftExpr);
+
+ this.Instruction($"push {left}", "left operand");
+
+ string right = this.VisitExpression(rightExpr);
+
+ this.Instruction($"mov {right}, V1", "right operand");
+ this.Instruction($"pop V0", "get left operand back");
+ this.Instruction($"{operation} V0, V1", $"compute {operationFriendlyName}");
+ }
+
+ private string BaseBinaryCompoundOperation(CGrammarParser.TargetContext leftTarget, CGrammarParser.ExpressionContext rightExpr, string operation, string operationFriendlyName, string operationOutput)
+ {
+ Target left = this.VisitTarget(leftTarget);
+
+ this.Instruction($"push {left.Value}", "left operand");
+
+ string? right = this.VisitExpression(rightExpr);
+ if (right == null)
+ throw new CompilerException($"Right operand for {operationFriendlyName} was not an expression.");
+
+ this.Instruction($"mov {right}, V1", "right operand");
+ this.Instruction($"pop V0", "get left operand back");
+ this.Instruction($"{operation} V0, V1", $"compute {operationFriendlyName}");
+
+ this.Instruction($"push {operationOutput}", "computation results");
+
+ left = this.VisitTarget(leftTarget);
+ this.Instruction("pop V1", "get computation results back");
+ this.DynamicMove(left.Type, "V1", left.Value, "store back into left param");
+
+ return left.Value;
+ }
+
+ public override string VisitNotExpression(CGrammarParser.NotExpressionContext context)
+ {
+ string value = this.VisitExpression(context.expression());
+
+ this.Instruction($"not {value}");
+ return "LX";
+ }
+ public override string VisitInvertExpression(CGrammarParser.InvertExpressionContext context)
+ {
+ string value = this.VisitExpression(context.expression());
+
+ this.Instruction($"inv {value}");
+ return "DX";
+ }
+ public override string VisitAddExpression(CGrammarParser.AddExpressionContext context)
+ {
+ this.BaseBinaryOperation(context.left, context.right, "add", "addition");
+ return "AX";
+ }
+ public override string VisitSubtractExpression(CGrammarParser.SubtractExpressionContext context)
+ {
+ this.BaseBinaryOperation(context.left, context.right, "sub", "subtraction");
+ return "AX";
+ }
+ public override string VisitMultiplyExpression(CGrammarParser.MultiplyExpressionContext context)
+ {
+ this.BaseBinaryOperation(context.left, context.right, "mul", "multiplication");
+ return "AX";
+ }
+ public override string VisitDivideExpression(CGrammarParser.DivideExpressionContext context)
+ {
+ this.BaseBinaryOperation(context.left, context.right, "div", "division");
+ return "AX";
+ }
+ public override string VisitModulusExpression(CGrammarParser.ModulusExpressionContext context)
+ {
+ this.BaseBinaryOperation(context.left, context.right, "mod", "modulus");
+ return "AX";
+ }
+ public override string VisitEqualExpression(CGrammarParser.EqualExpressionContext context)
+ {
+ this.BaseBinaryOperation(context.left, context.right, "cmp", "equality check");
+ return "LX";
+ }
+ public override string VisitNotEqualExpression(CGrammarParser.NotEqualExpressionContext context)
+ {
+ this.BaseBinaryOperation(context.left, context.right, "cmp", "inequality check");
+ this.Instruction($"not"); // lx is implicit
+ return "LX";
+ }
+ public override string VisitGreaterThanExpression(CGrammarParser.GreaterThanExpressionContext context)
+ {
+ this.BaseBinaryOperation(context.left, context.right, "gt", "greater than check");
+ return "LX";
+ }
+ public override string VisitLessThanExpression(CGrammarParser.LessThanExpressionContext context)
+ {
+ this.BaseBinaryOperation(context.left, context.right, "lt", "less than check");
+ return "LX";
+ }
+ public override string VisitLogicalOrExpression(CGrammarParser.LogicalOrExpressionContext context)
+ {
+ string left = this.VisitExpression(context.left);
+
+ string clauseBranch = this.GenerateBranchLabel("clause");
+ string endBranch = this.GenerateBranchLabel("end");
+
+ this.Instruction($"cmp {left}, 0", "logical or");
+ this.Instruction("bz"); // implicit LX
+ this.Instruction($"jmp {clauseBranch}"); // only if bz is not 0
+ this.Instruction("mov 1, V0", "first operand succeeded, store 1 and skip second operand");
+ this.Instruction($"jmp {endBranch}");
+ this.Branch(clauseBranch);
+
+ string right = this.VisitExpression(context.right);
+
+ this.Instruction($"cmp {right}, 0", "compare result from second operand to 0");
+ this.Instruction("not");
+ this.Instruction("mov LX, V0", "store second operand result");
+ this.Branch(endBranch);
+
+ return "V0";
+ }
+ public override string VisitLogicalAndExpression(CGrammarParser.LogicalAndExpressionContext context)
+ {
+ string left = this.VisitExpression(context.left);
+
+ string clauseBranch = this.GenerateBranchLabel("clause");
+ string endBranch = this.GenerateBranchLabel("end");
+
+ this.Instruction($"cmp {left}, 0", "logical and");
+ this.Instruction("not");
+ this.Instruction("bz"); // implicit LX
+ this.Instruction($"jmp {clauseBranch}"); // only if bz is not 0
+ this.Instruction("mov 0, V0", "first operand failed, store 0 and skip second");
+ this.Instruction($"jmp {endBranch}");
+ this.Branch(clauseBranch);
+
+ string right = this.VisitExpression(context.right);
+
+ this.Instruction($"cmp {right}, 0", "compare result from second operand to 0");
+ this.Instruction("not");
+ this.Instruction("mov LX, V0", "store second operand result");
+ this.Branch(endBranch);
+
+ return "V0";
+ }
+ public override string VisitGreaterThanOrEqualExpression(CGrammarParser.GreaterThanOrEqualExpressionContext context)
+ {
+ this.BaseBinaryOperation(context.left, context.right, "cmp", "equality check");
+ string endBranch = this.GenerateBranchLabel("end");
+
+ this.Instruction("bz");
+ this.Instruction($"jmp {endBranch}");
+ this.Instruction($"gt V0, V1");
+ this.Branch(endBranch);
+
+ return "LX";
+ }
+ public override string VisitLessThanOrEqualExpression(CGrammarParser.LessThanOrEqualExpressionContext context)
+ {
+ this.BaseBinaryOperation(context.left, context.right, "cmp", "equality check");
+ string endBranch = this.GenerateBranchLabel("end");
+
+ this.Instruction("bz");
+ this.Instruction($"jmp {endBranch}");
+ this.Instruction($"lt V0, V1");
+ this.Branch(endBranch);
+
+ return "LX";
+ }
+ public override string VisitBitwiseXorExpression(CGrammarParser.BitwiseXorExpressionContext context)
+ {
+ this.BaseBinaryOperation(context.left, context.right, "xor", "exclusive or");
+ return "DX";
+ }
+ public override string VisitBitwiseOrExpression(CGrammarParser.BitwiseOrExpressionContext context)
+ {
+ this.BaseBinaryOperation(context.left, context.right, "or", "or");
+ return "DX";
+ }
+ public override string VisitBitwiseAndExpression(CGrammarParser.BitwiseAndExpressionContext context)
+ {
+ this.BaseBinaryOperation(context.left, context.right, "and", "and");
+ return "DX";
+ }
+ public override string VisitBitwiseLeftShiftExpression(CGrammarParser.BitwiseLeftShiftExpressionContext context)
+ {
+ this.BaseBinaryOperation(context.left, context.right, "shl", "left shift");
+ return "DX";
+ }
+ public override string VisitBitwiseRightShiftExpression(CGrammarParser.BitwiseRightShiftExpressionContext context)
+ {
+ this.BaseBinaryOperation(context.left, context.right, "shr", "right shift");
+ return "DX";
+ }
+ public override string VisitCompoundAddExpression(CGrammarParser.CompoundAddExpressionContext context)
+ {
+ return this.BaseBinaryCompoundOperation(context.target(),
+ context.expression(),
+ "add",
+ "addition",
+ "AX");
+ }
+ public override string VisitCompoundSubtractExpression(CGrammarParser.CompoundSubtractExpressionContext context)
+ {
+ return this.BaseBinaryCompoundOperation(context.target(),
+ context.expression(),
+ "sub",
+ "subtraction",
+ "AX");
+ }
+ public override string VisitCompoundMultiplyExpression(CGrammarParser.CompoundMultiplyExpressionContext context)
+ {
+ return this.BaseBinaryCompoundOperation(context.target(),
+ context.expression(),
+ "mul",
+ "multiplication",
+ "AX");
+ }
+ public override string VisitCompoundDivideExpression(CGrammarParser.CompoundDivideExpressionContext context)
+ {
+ return this.BaseBinaryCompoundOperation(context.target(),
+ context.expression(),
+ "div",
+ "division",
+ "AX");
+ }
+ public override string VisitCompoundModulusExpression(CGrammarParser.CompoundModulusExpressionContext context)
+ {
+ return this.BaseBinaryCompoundOperation(context.target(),
+ context.expression(),
+ "mod",
+ "modulus",
+ "AX");
+ }
+ public override string VisitCompoundBitwiseOrExpression(CGrammarParser.CompoundBitwiseOrExpressionContext context)
+ {
+ return this.BaseBinaryCompoundOperation(context.target(),
+ context.expression(),
+ "or",
+ "or",
+ "DX");
+ }
+ public override string VisitCompoundBitwiseXorExpression(CGrammarParser.CompoundBitwiseXorExpressionContext context)
+ {
+ return this.BaseBinaryCompoundOperation(context.target(),
+ context.expression(),
+ "xor",
+ "exclusive or",
+ "DX");
+ }
+ public override string VisitCompoundBitwiseAndExpression(CGrammarParser.CompoundBitwiseAndExpressionContext context)
+ {
+ return this.BaseBinaryCompoundOperation(context.target(),
+ context.expression(),
+ "and",
+ "and",
+ "DX");
+ }
+ public override string VisitCompoundBitwiseLeftShiftExpression(CGrammarParser.CompoundBitwiseLeftShiftExpressionContext context)
+ {
+ return this.BaseBinaryCompoundOperation(context.target(),
+ context.expression(),
+ "shl",
+ "left shift",
+ "DX");
+ }
+ public override string VisitCompoundBitwiseRightShiftExpression(CGrammarParser.CompoundBitwiseRightShiftExpressionContext context)
+ {
+ return this.BaseBinaryCompoundOperation(context.target(),
+ context.expression(),
+ "shr",
+ "right shift",
+ "DX");
+ }
+ public override string VisitIncrementPrefixExpression(CGrammarParser.IncrementPrefixExpressionContext context)
+ {
+ Target target = this.VisitTarget(context.target());
+ this.Instruction($"inc {target.Value}", "++i");
+ return target.Value;
+ }
+ public override string VisitDecrementPrefixExpression(CGrammarParser.DecrementPrefixExpressionContext context)
+ {
+ Target target = this.VisitTarget(context.target());
+ this.Instruction($"dec {target.Value}", "--i");
+ return target.Value;
+ }
+ public override string VisitIncrementPostfixExpression(CGrammarParser.IncrementPostfixExpressionContext context)
+ {
+ Target target = this.VisitTarget(context.target());
+ this.Instruction($"push {target.Value}", "i++");
+ this.Instruction($"inc {target.Value}");
+ this.Instruction("pop V0");
+ return "V0";
+ }
+ public override string VisitDecrementPostfixExpression(CGrammarParser.DecrementPostfixExpressionContext context)
+ {
+ Target target = this.VisitTarget(context.target());
+ this.Instruction($"push {target.Value}", "i--");
+ this.Instruction($"dec {target.Value}");
+ this.Instruction($"pop V0");
+ return "V0";
+ }
+} \ No newline at end of file
diff --git a/Compiler/visitors/Targets.cs b/Compiler/visitors/Targets.cs
new file mode 100644
index 0000000..10d97f4
--- /dev/null
+++ b/Compiler/visitors/Targets.cs
@@ -0,0 +1,40 @@
+namespace Q1.Compiler.visitors;
+
+public partial class CCompilerVisitor
+{
+ private Target VisitTarget(CGrammarParser.TargetContext target)
+ {
+ Target? expressionString = this.Visit(target) as Target;
+ if (expressionString == null)
+ throw new CompilerException("Target did not return a target");
+
+ return expressionString;
+ }
+
+ public override Target VisitVariableTarget(CGrammarParser.VariableTargetContext context)
+ {
+ string varName = context.ID().GetText();
+ VariableReference reference = this.FindVariable(varName);
+
+ return new Target(reference.Type, $"[${reference.Address:X4}]", $"${reference.Address:X4}");
+ }
+
+ public override object? VisitIndexerTarget(CGrammarParser.IndexerTargetContext context)
+ {
+ Target obj = this.VisitTarget(context.obj);
+ if (obj.Type is not CTypePtr ptr)
+ throw new CompilerException("Indexer target was not a pointer!");
+
+ this.Instruction($"push {obj.Value}", "store index obj");
+
+ string indexer = this.VisitExpression(context.indexer);
+
+ this.Instruction($"mov {indexer}, V1", "store indexer");
+ this.Instruction($"pop V0", "restore index obj");
+ this.Instruction($"mul V1, {ptr.Sub.Size}", $"multiply indexer by {ptr.Sub.Size}");
+ this.Instruction($"add AX, V0", "add indexer and index obj");
+ this.Instruction($"mov AX, V0", "move to general purpose register");
+
+ return new Target(ptr.Sub, $"[V0]", $"V0");
+ }
+} \ No newline at end of file