From 4761cd1f831f1b1a962f9c860ed79298b6148c33 Mon Sep 17 00:00:00 2001 From: Oliver Stene Date: Sun, 8 Feb 2026 17:55:15 +0100 Subject: [PATCH] ... --- compiler/Compiler/Generator.cs | 28 ++++++ compiler/Compiler/Parser.cs | 164 +++++++++++++++++++++++++++++++-- compiler/Compiler/Program.cs | 9 +- compiler/Compiler/Tokenizer.cs | 53 +++++++++++ 4 files changed, 245 insertions(+), 9 deletions(-) diff --git a/compiler/Compiler/Generator.cs b/compiler/Compiler/Generator.cs index 5d28061..22fb510 100644 --- a/compiler/Compiler/Generator.cs +++ b/compiler/Compiler/Generator.cs @@ -159,6 +159,7 @@ public sealed class Generator(List nodes) { EmitStatement(statement.Block); } + writer.WriteLine("}"); } @@ -166,6 +167,7 @@ public sealed class Generator(List nodes) { return node switch { + NodeExpressionBinary expression => EmitExpressionBinary(expression), NodeExpressionBoolLiteral expression => expression.Value.Value ? "true" : "false", NodeExpressionIntLiteral expression => expression.Value.Value.ToString(), NodeExpressionStringLiteral expression => $"(struct string){{ \"{expression.Value.Value}\", {expression.Value.Value.Length} }}", @@ -174,6 +176,32 @@ public sealed class Generator(List nodes) }; } + private string EmitExpressionBinary(NodeExpressionBinary expression) + { + var left = EmitExpression(expression.Left); + var right = EmitExpression(expression.Right); + + return expression.Operation switch + { + NodeExpressionBinary.Op.Add => $"({left} + {right})", + NodeExpressionBinary.Op.Subtract => $"({left} - {right})", + NodeExpressionBinary.Op.Multiply => $"({left} * {right})", + NodeExpressionBinary.Op.Divide => $"({left} / {right})", + NodeExpressionBinary.Op.Modulo => $"({left} % {right})", + NodeExpressionBinary.Op.Equal => $"({left} == {right})", + NodeExpressionBinary.Op.NotEqual => $"({left} != {right})", + NodeExpressionBinary.Op.LessThan => $"({left} < {right})", + NodeExpressionBinary.Op.LessThanOrEqual => $"({left} <= {right})", + NodeExpressionBinary.Op.GreaterThan => $"({left} > {right})", + NodeExpressionBinary.Op.GreaterThanOrEqual => $"({left} >= {right})", + NodeExpressionBinary.Op.LeftShift => $"({left} << {right})", + NodeExpressionBinary.Op.RightShift => $"({left} >> {right})", + NodeExpressionBinary.Op.LogicalAnd => $"({left} && {right})", + NodeExpressionBinary.Op.LogicalOr => $"({left} || {right})", + _ => throw new ArgumentOutOfRangeException() + }; + } + private static string CType(NodeType node, string? varName = null) { return node switch diff --git a/compiler/Compiler/Parser.cs b/compiler/Compiler/Parser.cs index b43bd4c..a7f632b 100644 --- a/compiler/Compiler/Parser.cs +++ b/compiler/Compiler/Parser.cs @@ -120,10 +120,65 @@ public sealed class Parser(List tokens) throw new Exception("Not a valid followup for expression statement"); } - private NodeExpression ParseExpression() + private NodeExpression ParseExpression(int minPrecedence = -1) { var startIndex = index; + var left = ParseExpressionLeaf(); + + while (TryPeekBinaryOperator(out var op) && GetPrecedence(op) >= minPrecedence) + { + Consume(); + var right = ParseExpression(GetPrecedence(op) + 1); + left = new NodeExpressionBinary(TokensFrom(startIndex), left, op, right); + } + + return left; + } + + private static int GetPrecedence(NodeExpressionBinary.Op operation) + { + return operation switch + { + NodeExpressionBinary.Op.Multiply => 10, + NodeExpressionBinary.Op.Divide => 10, + NodeExpressionBinary.Op.Modulo => 10, + + NodeExpressionBinary.Op.Add => 9, + NodeExpressionBinary.Op.Subtract => 9, + + NodeExpressionBinary.Op.LeftShift => 8, + NodeExpressionBinary.Op.RightShift => 8, + + NodeExpressionBinary.Op.GreaterThan => 7, + NodeExpressionBinary.Op.GreaterThanOrEqual => 7, + NodeExpressionBinary.Op.LessThan => 7, + NodeExpressionBinary.Op.LessThanOrEqual => 7, + + NodeExpressionBinary.Op.Equal => 7, + NodeExpressionBinary.Op.NotEqual => 7, + + // NodeExpressionBinary.Op.BitwiseAnd => 6, + // NodeExpressionBinary.Op.BitwiseXor => 5, + // NodeExpressionBinary.Op.BitwiseOr => 4, + + NodeExpressionBinary.Op.LogicalAnd => 3, + NodeExpressionBinary.Op.LogicalOr => 2, + _ => throw new ArgumentOutOfRangeException(nameof(operation), operation, null) + }; + } + + private NodeExpression ParseExpressionLeaf() + { + var startIndex = index; + + if (TryExpectSymbol(Symbol.OpenParen)) + { + var value = ParseExpression(); + ExpectSymbol(Symbol.CloseParen); + return value; + } + if (TryExpectIntLiteral(out var intLiteral)) { return new NodeExpressionIntLiteral(TokensFrom(startIndex), intLiteral); @@ -144,7 +199,7 @@ public sealed class Parser(List tokens) return new NodeExpressionIdent(TokensFrom(startIndex), ident); } - throw new Exception("Not a valid expression"); + throw new Exception("Not a valid expression leaf"); } private NodeType ParseType() @@ -323,6 +378,67 @@ public sealed class Parser(List tokens) return tokens[index + offset]; } + + private bool TryPeekBinaryOperator(out NodeExpressionBinary.Op op) + { + if (Peek() is not TokenSymbol token) + { + op = default; + return false; + } + + switch (token.Symbol) + { + case Symbol.Plus: + op = NodeExpressionBinary.Op.Add; + return true; + case Symbol.Minus: + op = NodeExpressionBinary.Op.Subtract; + return true; + case Symbol.Star: + op = NodeExpressionBinary.Op.Multiply; + return true; + case Symbol.ForwardSlash: + op = NodeExpressionBinary.Op.Divide; + return true; + case Symbol.Percent: + op = NodeExpressionBinary.Op.Modulo; + return true; + case Symbol.BangEqual: + op = NodeExpressionBinary.Op.NotEqual; + return true; + case Symbol.EqualEqual: + op = NodeExpressionBinary.Op.Equal; + return true; + case Symbol.LessThan: + op = NodeExpressionBinary.Op.LessThan; + return true; + case Symbol.LessThanEqual: + op = NodeExpressionBinary.Op.LessThanOrEqual; + return true; + case Symbol.GreaterThan: + op = NodeExpressionBinary.Op.GreaterThan; + return true; + case Symbol.GreaterThanEqual: + op = NodeExpressionBinary.Op.GreaterThanOrEqual; + return true; + case Symbol.LessThanLessThan: + op = NodeExpressionBinary.Op.LeftShift; + return true; + case Symbol.GreaterThanGreaterThan: + op = NodeExpressionBinary.Op.RightShift; + return true; + case Symbol.AmpersandAmpersand: + op = NodeExpressionBinary.Op.LogicalAnd; + return true; + case Symbol.PipePipe: + op = NodeExpressionBinary.Op.LogicalOr; + return true; + default: + op = default; + return false; + } + } } public abstract class Node(List tokens) @@ -359,32 +475,32 @@ public sealed class NodeStatementFuncCall(List tokens, NodeExpression tar public readonly List Parameters = parameters; } -internal class NodeStatementReturn(List tokens, NodeExpression value) : NodeStatement(tokens) +public class NodeStatementReturn(List tokens, NodeExpression value) : NodeStatement(tokens) { public readonly NodeExpression Value = value; } -internal class NodeStatementVariableDeclaration(List tokens, TokenIdent name, NodeType type, NodeExpression value) : NodeStatement(tokens) +public class NodeStatementVariableDeclaration(List tokens, TokenIdent name, NodeType type, NodeExpression value) : NodeStatement(tokens) { public readonly TokenIdent Name = name; public readonly NodeType Type = type; public readonly NodeExpression Value = value; } -internal class NodeStatementAssignment(List tokens, NodeExpression target, NodeExpression value) : NodeStatement(tokens) +public class NodeStatementAssignment(List tokens, NodeExpression target, NodeExpression value) : NodeStatement(tokens) { public readonly NodeExpression Target = target; public readonly NodeExpression Value = value; } -internal class NodeStatementIf(List tokens, NodeExpression condition, NodeStatement thenBlock, NodeStatement? elseBlock) : NodeStatement(tokens) +public class NodeStatementIf(List tokens, NodeExpression condition, NodeStatement thenBlock, NodeStatement? elseBlock) : NodeStatement(tokens) { public readonly NodeExpression Condition = condition; public readonly NodeStatement ThenBlock = thenBlock; public readonly NodeStatement? ElseBlock = elseBlock; } -internal class NodeStatementWhile(List tokens, NodeExpression condition, NodeStatement block) : NodeStatement(tokens) +public class NodeStatementWhile(List tokens, NodeExpression condition, NodeStatement block) : NodeStatement(tokens) { public readonly NodeExpression Condition = condition; public readonly NodeStatement Block = block; @@ -412,6 +528,40 @@ public sealed class NodeExpressionIdent(List tokens, TokenIdent value) : public readonly TokenIdent Value = value; } +public class NodeExpressionBinary(List tokens, NodeExpression left, NodeExpressionBinary.Op operation, NodeExpression right) : NodeExpression(tokens) +{ + public NodeExpression Left { get; } = left; + public readonly Op Operation = operation; + public NodeExpression Right { get; } = right; + + + public enum Op + { + Add, + Subtract, + Multiply, + Divide, + Modulo, + + Equal, + NotEqual, + LessThan, + LessThanOrEqual, + GreaterThan, + GreaterThanOrEqual, + + LeftShift, + RightShift, + + // BitwiseAnd, + // BitwiseXor, + // BitwiseOr, + + LogicalAnd, + LogicalOr, + } +} + public abstract class NodeType(List tokens) : Node(tokens); public sealed class NodeTypeVoid(List tokens) : NodeType(tokens); diff --git a/compiler/Compiler/Program.cs b/compiler/Compiler/Program.cs index e90ab37..7ca2a8c 100644 --- a/compiler/Compiler/Program.cs +++ b/compiler/Compiler/Program.cs @@ -11,8 +11,13 @@ const string contents = """ x = 3 } - while false { - x = 6 + let i: i32 = 0 + + x = 1 + 2 * 34 + + while i < 10 { + i = i + 1 + x = i } do_something("test") diff --git a/compiler/Compiler/Tokenizer.cs b/compiler/Compiler/Tokenizer.cs index 99dceb1..6403ca4 100644 --- a/compiler/Compiler/Tokenizer.cs +++ b/compiler/Compiler/Tokenizer.cs @@ -197,6 +197,12 @@ public sealed class Tokenizer(string contents) Consume(); return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Equal); } + case '<' when Peek(1) is '<': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.LessThanLessThan); + } case '<' when Peek(1) is '=': { Consume(); @@ -208,6 +214,12 @@ public sealed class Tokenizer(string contents) Consume(); return new TokenSymbol(line, startColumn, column - startColumn, Symbol.LessThan); } + case '>' when Peek(1) is '>': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.GreaterThanGreaterThan); + } case '>' when Peek(1) is '=': { Consume(); @@ -263,6 +275,39 @@ public sealed class Tokenizer(string contents) Consume(); return new TokenSymbol(line, startColumn, column - startColumn, Symbol.ForwardSlash); } + case '%' when Peek(1) is '=': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.PercentEqual); + } + case '%': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Percent); + } + case '&' when Peek(1) is '&': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.AmpersandAmpersand); + } + case '&': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Ampersand); + } + case '|' when Peek(1) is '|': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.PipePipe); + } + case '|': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Pipe); + } default: { if (char.IsLetter(c) || c == '_') @@ -377,8 +422,10 @@ public enum Symbol EqualEqual, BangEqual, LessThan, + LessThanLessThan, LessThanEqual, GreaterThan, + GreaterThanGreaterThan, GreaterThanEqual, Plus, PlusEqual, @@ -388,6 +435,12 @@ public enum Symbol StarEqual, ForwardSlash, ForwardSlashEqual, + Percent, + PercentEqual, + Ampersand, + AmpersandAmpersand, + Pipe, + PipePipe, } public sealed class TokenSymbol(int line, int column, int length, Symbol symbol) : Token(line, column, length)