From 634681ce2bb36cd686943102a0f071cd6a09049f Mon Sep 17 00:00:00 2001 From: Melodi <62381002+Melodi17@users.noreply.github.com> Date: Wed, 27 Aug 2025 21:22:55 +1000 Subject: fixed bugs and revolutionized the universe --- Compiler/visitors/Constants.cs | 50 +++++++ Compiler/visitors/Definitions.cs | 123 ++++++++++++++++ Compiler/visitors/Expressions.cs | 134 +++++++++++++++++ Compiler/visitors/FlowControl.cs | 177 +++++++++++++++++++++++ Compiler/visitors/Main.cs | 183 +++++++++++++++++++++++ Compiler/visitors/Operations.cs | 305 +++++++++++++++++++++++++++++++++++++++ Compiler/visitors/Targets.cs | 40 +++++ 7 files changed, 1012 insertions(+) create mode 100644 Compiler/visitors/Constants.cs create mode 100644 Compiler/visitors/Definitions.cs create mode 100644 Compiler/visitors/Expressions.cs create mode 100644 Compiler/visitors/FlowControl.cs create mode 100644 Compiler/visitors/Main.cs create mode 100644 Compiler/visitors/Operations.cs create mode 100644 Compiler/visitors/Targets.cs (limited to 'Compiler/visitors') 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 paramNames, List paramTypes)> _functionBodies = new(); + + private VariableReference FindVariable(string varName) + { + foreach (Dictionary 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 parameterTypes = context._types.Select(this.VisitCType).ToList(); + List 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 paramTypes = context._types.Select(this.VisitCType).ToList(); + List 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 paramNames, List paramTypes) + { + this.Branch(function.GetBranchName(overload), function.GetBranchFriendlyName(overload)); + + if (paramNames.Count > 0) + this.Instruction("pop V0", "preserve function return address"); + + Dictionary 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 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 +{ + private int _branchCount = 1; + private List _instructions = new(); + private Stack> _variableStack = new(); + private List _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 Compile(CGrammarParser.ProgramContext program) + { + this.Instruction("jmp _main"); + this.Visit(program); + + foreach ((CGrammarParser.FunctionDefinitionContext context, + FunctionReference function, FunctionOverload overload, + List paramNames, List paramTypes) in this._functionBodies) + this.VisitFunctionBody(context, function, overload, paramNames, paramTypes); + + return this._instructions; + } + + private Dictionary EnterScope() + { + Dictionary 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); + } + + /// + /// Basically identical to base version, except calls Visit on each child, making Visit not redundant. + /// + 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 -- cgit v1.3.1