using System.Diagnostics.CodeAnalysis; namespace Compiler; public sealed class Parser(List tokens) { public static List Parse(List tokens) { return new Parser(tokens).Parse(); } private int index; private List Parse() { var nodes = new List(); while (Peek() != null) nodes.Add(ParseDefinition()); return nodes; } private NodeDefinition ParseDefinition() { var startIndex = index; if (TryExpectKeyword(Keyword.Func)) { var name = ExpectIdent(); var parameters = new List(); ExpectSymbol(Symbol.OpenParen); while (!TryExpectSymbol(Symbol.CloseParen)) { var paramStartIndex = index; var parameterName = ExpectIdent(); ExpectSymbol(Symbol.Colon); var parameterType = ParseType(); parameters.Add(new NodeDefinitionFunc.Param(TokensFrom(paramStartIndex), parameterName, parameterType)); } ExpectSymbol(Symbol.Colon); var returnType = ParseType(); var body = ParseStatement(); return new NodeDefinitionFunc(TokensFrom(startIndex), name, parameters, body, returnType); } throw new Exception("Not a valid definition"); } private NodeStatement ParseStatement() { var startIndex = index; if (TryExpectSymbol(Symbol.OpenCurly)) { var statements = new List(); while (!TryExpectSymbol(Symbol.CloseCurly)) statements.Add(ParseStatement()); return new NodeStatementBlock(TokensFrom(startIndex), statements); } if (TryExpectKeyword(Keyword.Return)) { var value = ParseExpression(); return new NodeStatementReturn(TokensFrom(startIndex), value); } if (TryExpectKeyword(Keyword.Let)) { var name = ExpectIdent(); ExpectSymbol(Symbol.Colon); var type = ParseType(); ExpectSymbol(Symbol.Equal); var value = ParseExpression(); return new NodeStatementVariableDeclaration(TokensFrom(startIndex), name, type, value); } if (TryExpectKeyword(Keyword.If)) { var condition = ParseExpression(); var thenBlock = ParseStatement(); NodeStatement? elseBlock = null; if (TryExpectKeyword(Keyword.Else)) elseBlock = ParseStatement(); return new NodeStatementIf(TokensFrom(startIndex), condition, thenBlock, elseBlock); } if (TryExpectKeyword(Keyword.While)) { var condition = ParseExpression(); var thenBlock = ParseStatement(); return new NodeStatementWhile(TokensFrom(startIndex), condition, thenBlock); } var target = ParseExpression(); if (TryExpectSymbol(Symbol.OpenParen)) { var parameters = new List(); while (!TryExpectSymbol(Symbol.CloseParen)) parameters.Add(ParseExpression()); return new NodeStatementFuncCall(TokensFrom(startIndex), target, parameters); } if (TryExpectSymbol(Symbol.Equal)) { var value = ParseExpression(); return new NodeStatementAssignment(TokensFrom(startIndex), target, value); } throw new Exception("Not a valid followup for expression statement"); } 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); } if (TryExpectStringLiteral(out var stringLiteral)) { return new NodeExpressionStringLiteral(TokensFrom(startIndex), stringLiteral); } if (TryExpectBoolLiteral(out var boolLiteral)) { return new NodeExpressionBoolLiteral(TokensFrom(startIndex), boolLiteral); } if (TryExpectIdent(out var ident)) { return new NodeExpressionIdent(TokensFrom(startIndex), ident); } throw new Exception("Not a valid expression leaf"); } private NodeType ParseType() { var startIndex = index; if (TryExpectSymbol(Symbol.Caret)) { var to = ParseType(); return new NodeTypePointer(TokensFrom(startIndex), to); } if (TryExpectKeyword(Keyword.Func)) { var parameters = new List(); ExpectSymbol(Symbol.OpenParen); while (!TryExpectSymbol(Symbol.CloseParen)) { parameters.Add(ParseType()); } ExpectSymbol(Symbol.Colon); var returnType = ParseType(); return new NodeTypeFunc(TokensFrom(startIndex), parameters, returnType); } if (TryExpectIdent(out var ident)) { switch (ident.Ident) { case "void": return new NodeTypeVoid(TokensFrom(startIndex)); case "string": return new NodeTypeString(TokensFrom(startIndex)); case "bool": return new NodeTypeBool(TokensFrom(startIndex)); case "i8": return new NodeTypeSInt(TokensFrom(startIndex), 8); case "i16": return new NodeTypeSInt(TokensFrom(startIndex), 16); case "i32": return new NodeTypeSInt(TokensFrom(startIndex), 32); case "i64": return new NodeTypeSInt(TokensFrom(startIndex), 64); case "u8": return new NodeTypeUInt(TokensFrom(startIndex), 8); case "u16": return new NodeTypeUInt(TokensFrom(startIndex), 16); case "u32": return new NodeTypeUInt(TokensFrom(startIndex), 32); case "u64": return new NodeTypeUInt(TokensFrom(startIndex), 64); default: return new NodeTypeCustom(TokensFrom(startIndex), ident); } } throw new Exception("Not a valid type"); } private List TokensFrom(int startIndex) { return tokens.GetRange(startIndex, index - startIndex); } private bool TryExpectKeyword(Keyword keyword) { if (Peek() is TokenKeyword token && token.Keyword == keyword) { Consume(); return true; } return false; } private void ExpectSymbol(Symbol symbol) { if (Peek() is TokenSymbol token && token.Symbol == symbol) { Consume(); return; } throw new Exception($"Expected symbol '{symbol}'"); } private bool TryExpectSymbol(Symbol symbol) { if (Peek() is TokenSymbol token && token.Symbol == symbol) { Consume(); return true; } return false; } private TokenIdent ExpectIdent() { if (Peek() is TokenIdent token) { Consume(); return token; } throw new Exception("Expected ident"); } private bool TryExpectIdent([NotNullWhen(true)] out TokenIdent? ident) { if (Peek() is TokenIdent token) { Consume(); ident = token; return true; } ident = null; return false; } private bool TryExpectIntLiteral([NotNullWhen(true)] out TokenIntLiteral? intLiteral) { if (Peek() is TokenIntLiteral token) { Consume(); intLiteral = token; return true; } intLiteral = null; return false; } private bool TryExpectStringLiteral([NotNullWhen(true)] out TokenStringLiteral? stringLiteral) { if (Peek() is TokenStringLiteral token) { Consume(); stringLiteral = token; return true; } stringLiteral = null; return false; } private bool TryExpectBoolLiteral([NotNullWhen(true)] out TokenBoolLiteral? boolLiteral) { if (Peek() is TokenBoolLiteral token) { Consume(); boolLiteral = token; return true; } boolLiteral = null; return false; } private Token Consume() { if (index >= tokens.Count) throw new Exception("End of tokens"); return tokens[index++]; } private Token? Peek(int offset = 0) { if (index + offset >= tokens.Count) return null; 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) { public readonly List Tokens = tokens; } public abstract class NodeDefinition(List tokens) : Node(tokens); public sealed class NodeDefinitionFunc(List tokens, TokenIdent name, List parameters, NodeStatement body, NodeType returnType) : NodeDefinition(tokens) { public readonly TokenIdent Name = name; public readonly List Parameters = parameters; public readonly NodeStatement Body = body; public readonly NodeType ReturnType = returnType; public sealed class Param(List tokens, TokenIdent name, NodeType type) : Node(tokens) { public readonly TokenIdent Name = name; public readonly NodeType Type = type; } } public abstract class NodeStatement(List tokens) : Node(tokens); public sealed class NodeStatementBlock(List tokens, List statements) : NodeStatement(tokens) { public readonly List Statements = statements; } public sealed class NodeStatementFuncCall(List tokens, NodeExpression target, List parameters) : NodeStatement(tokens) { public readonly NodeExpression Target = target; public readonly List Parameters = parameters; } public class NodeStatementReturn(List tokens, NodeExpression value) : NodeStatement(tokens) { public readonly NodeExpression Value = value; } 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; } public class NodeStatementAssignment(List tokens, NodeExpression target, NodeExpression value) : NodeStatement(tokens) { public readonly NodeExpression Target = target; public readonly NodeExpression Value = value; } 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; } public class NodeStatementWhile(List tokens, NodeExpression condition, NodeStatement block) : NodeStatement(tokens) { public readonly NodeExpression Condition = condition; public readonly NodeStatement Block = block; } public abstract class NodeExpression(List tokens) : Node(tokens); public sealed class NodeExpressionIntLiteral(List tokens, TokenIntLiteral value) : NodeExpression(tokens) { public readonly TokenIntLiteral Value = value; } public sealed class NodeExpressionStringLiteral(List tokens, TokenStringLiteral value) : NodeExpression(tokens) { public readonly TokenStringLiteral Value = value; } public sealed class NodeExpressionBoolLiteral(List tokens, TokenBoolLiteral value) : NodeExpression(tokens) { public readonly TokenBoolLiteral Value = value; } public sealed class NodeExpressionIdent(List tokens, TokenIdent value) : NodeExpression(tokens) { 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); public sealed class NodeTypeUInt(List tokens, int width) : NodeType(tokens) { public readonly int Width = width; } public sealed class NodeTypeSInt(List tokens, int width) : NodeType(tokens) { public readonly int Width = width; } public sealed class NodeTypeBool(List tokens) : NodeType(tokens); public sealed class NodeTypeString(List tokens) : NodeType(tokens); public sealed class NodeTypeCustom(List tokens, TokenIdent name) : NodeType(tokens) { public readonly TokenIdent Name = name; } public sealed class NodeTypePointer(List tokens, NodeType to) : NodeType(tokens) { public readonly NodeType To = to; } public sealed class NodeTypeFunc(List tokens, List parameters, NodeType returnType) : NodeType(tokens) { public readonly List Parameters = parameters; public readonly NodeType ReturnType = returnType; }