using System.Diagnostics.CodeAnalysis; using Nub.Lang.Frontend.Lexing; namespace Nub.Lang.Frontend.Parsing; public class Parser { private List _tokens = []; private int _index; public ModuleNode ParseModule(List tokens, string path) { _index = 0; _tokens = tokens; List definitions = []; List imports = []; while (Peek().HasValue) { if (TryExpectSymbol(Symbol.Import)) { var name = ExpectLiteral(); if (!name.Type.Equals(NubType.String)) { throw new Exception("Import statements must have a string literal value"); } TryExpectSymbol(Symbol.Semicolon); imports.Add(name.Value); } else { definitions.Add(ParseDefinition()); } } return new ModuleNode(path, imports, definitions); } private DefinitionNode ParseDefinition() { var keyword = ExpectSymbol(); return keyword.Symbol switch { Symbol.Func => ParseFuncDefinition(), Symbol.Extern => ParseExternFuncDefinition(), Symbol.Struct => ParseStruct(), _ => throw new Exception("Unexpected symbol: " + keyword.Symbol) }; } private LocalFuncDefinitionNode ParseFuncDefinition() { var name = ExpectIdentifier(); List parameters = []; ExpectSymbol(Symbol.OpenParen); if (!TryExpectSymbol(Symbol.CloseParen)) { while (!TryExpectSymbol(Symbol.CloseParen)) { parameters.Add(ParseFuncParameter()); TryExpectSymbol(Symbol.Comma); } } var returnType = Optional.Empty(); if (TryExpectSymbol(Symbol.Colon)) { returnType = ParseTypeInstance(); } var body = ParseBlock(); return new LocalFuncDefinitionNode(name.Value, parameters, body, returnType); } private ExternFuncDefinitionNode ParseExternFuncDefinition() { ExpectSymbol(Symbol.Func); var name = ExpectIdentifier(); List parameters = []; ExpectSymbol(Symbol.OpenParen); if (!TryExpectSymbol(Symbol.CloseParen)) { while (!TryExpectSymbol(Symbol.CloseParen)) { parameters.Add(ParseFuncParameter()); TryExpectSymbol(Symbol.Comma); } } var returnType = Optional.Empty(); if (TryExpectSymbol(Symbol.Colon)) { returnType = ParseTypeInstance(); } ExpectSymbol(Symbol.Semicolon); return new ExternFuncDefinitionNode(name.Value, parameters, returnType); } private StructDefinitionNode ParseStruct() { var name = ExpectIdentifier().Value; ExpectSymbol(Symbol.OpenBrace); List variables = []; while (!TryExpectSymbol(Symbol.CloseBrace)) { var variableName = ExpectIdentifier().Value; ExpectSymbol(Symbol.Colon); var variableType = ParseTypeInstance(); var variableValue = Optional.Empty(); if (TryExpectSymbol(Symbol.Assign)) { variableValue = ParseExpression(); } ExpectSymbol(Symbol.Semicolon); variables.Add(new StructField(variableName, variableType, variableValue)); } return new StructDefinitionNode(name, variables); } private FuncParameter ParseFuncParameter() { var name = ExpectIdentifier(); ExpectSymbol(Symbol.Colon); var type = ParseTypeInstance(); return new FuncParameter(name.Value, type); } private StatementNode ParseStatement() { var token = ExpectToken(); switch (token) { case IdentifierToken identifier: { var symbol = ExpectSymbol(); switch (symbol.Symbol) { case Symbol.OpenParen: { var parameters = new List(); while (!TryExpectSymbol(Symbol.CloseParen)) { parameters.Add(ParseExpression()); TryExpectSymbol(Symbol.Comma); } ExpectSymbol(Symbol.Semicolon); if (identifier.Value == "syscall") { return new SyscallStatementNode(new Syscall(parameters)); } return new FuncCallStatementNode(new FuncCall(identifier.Value, parameters)); } case Symbol.Assign: { var value = ParseExpression(); ExpectSymbol(Symbol.Semicolon); return new VariableReassignmentNode(identifier.Value, value); } default: { throw new Exception($"Unexpected symbol {symbol.Symbol}"); } } } case SymbolToken symbol: { return symbol.Symbol switch { Symbol.Return => ParseReturn(), Symbol.Let => ParseVariableAssignment(), Symbol.If => ParseIf(), Symbol.While => ParseWhile(), Symbol.Break => ParseBreak(), Symbol.Continue => ParseContinue(), _ => throw new Exception($"Unexpected symbol {symbol.Symbol}") }; } default: { throw new Exception($"Unexpected token type {token.GetType().Name}"); } } } private ReturnNode ParseReturn() { var value = Optional.Empty(); if (!TryExpectSymbol(Symbol.Semicolon)) { value = ParseExpression(); ExpectSymbol(Symbol.Semicolon); } return new ReturnNode(value); } private VariableAssignmentNode ParseVariableAssignment() { var name = ExpectIdentifier().Value; ExpectSymbol(Symbol.Assign); var value = ParseExpression(); ExpectSymbol(Symbol.Semicolon); return new VariableAssignmentNode(name, value); } private IfNode ParseIf() { var condition = ParseExpression(); var body = ParseBlock(); var elseStatement = Optional>.Empty(); if (TryExpectSymbol(Symbol.Else)) { elseStatement = TryExpectSymbol(Symbol.If) ? (Variant)ParseIf() : (Variant)ParseBlock(); } return new IfNode(condition, body, elseStatement); } private WhileNode ParseWhile() { var condition = ParseExpression(); var body = ParseBlock(); return new WhileNode(condition, body); } private BreakNode ParseBreak() { ExpectSymbol(Symbol.Semicolon); return new BreakNode(); } private ContinueNode ParseContinue() { ExpectSymbol(Symbol.Semicolon); return new ContinueNode(); } private ExpressionNode ParseExpression(int precedence = 0) { var left = ParsePrimaryExpression(); while (true) { var token = Peek(); if (!token.HasValue || token.Value is not SymbolToken symbolToken || !TryGetBinaryOperator(symbolToken.Symbol, out var op) || GetBinaryOperatorPrecedence(op.Value) < precedence) break; Next(); var right = ParseExpression(GetBinaryOperatorPrecedence(op.Value) + 1); left = new BinaryExpressionNode(left, op.Value, right); } return left; } private static int GetBinaryOperatorPrecedence(BinaryExpressionOperator binaryExpressionOperator) { return binaryExpressionOperator switch { BinaryExpressionOperator.Multiply => 3, BinaryExpressionOperator.Divide => 3, BinaryExpressionOperator.Plus => 2, BinaryExpressionOperator.Minus => 2, BinaryExpressionOperator.GreaterThan => 1, BinaryExpressionOperator.GreaterThanOrEqual => 1, BinaryExpressionOperator.LessThan => 1, BinaryExpressionOperator.LessThanOrEqual => 1, BinaryExpressionOperator.Equal => 0, BinaryExpressionOperator.NotEqual => 0, _ => throw new ArgumentOutOfRangeException(nameof(binaryExpressionOperator), binaryExpressionOperator, null) }; } private static bool TryGetBinaryOperator(Symbol symbol, [NotNullWhen(true)] out BinaryExpressionOperator? binaryExpressionOperator) { switch (symbol) { case Symbol.Equal: binaryExpressionOperator = BinaryExpressionOperator.Equal; return true; case Symbol.NotEqual: binaryExpressionOperator = BinaryExpressionOperator.NotEqual; return true; case Symbol.LessThan: binaryExpressionOperator = BinaryExpressionOperator.LessThan; return true; case Symbol.LessThanOrEqual: binaryExpressionOperator = BinaryExpressionOperator.LessThanOrEqual; return true; case Symbol.GreaterThan: binaryExpressionOperator = BinaryExpressionOperator.GreaterThan; return true; case Symbol.GreaterThanOrEqual: binaryExpressionOperator = BinaryExpressionOperator.GreaterThanOrEqual; return true; case Symbol.Plus: binaryExpressionOperator = BinaryExpressionOperator.Plus; return true; case Symbol.Minus: binaryExpressionOperator = BinaryExpressionOperator.Minus; return true; case Symbol.Star: binaryExpressionOperator = BinaryExpressionOperator.Multiply; return true; case Symbol.ForwardSlash: binaryExpressionOperator = BinaryExpressionOperator.Divide; return true; default: binaryExpressionOperator = null; return false; } } private ExpressionNode ParsePrimaryExpression() { var token = ExpectToken(); switch (token) { case LiteralToken literal: { return new LiteralNode(literal.Value, literal.Type); } case IdentifierToken identifier: { return ParseExpressionIdentifier(identifier); } case SymbolToken symbolToken: { switch (symbolToken.Symbol) { case Symbol.OpenParen: { var expression = ParseExpression(); ExpectSymbol(Symbol.CloseParen); return expression; } case Symbol.New: { var type = ParseTypeInstance(); Dictionary initializers = []; ExpectSymbol(Symbol.OpenBrace); while (!TryExpectSymbol(Symbol.CloseBrace)) { var name = ExpectIdentifier().Value; ExpectSymbol(Symbol.Assign); var value = ParseExpression(); TryExpectSymbol(Symbol.Semicolon); initializers.Add(name, value); } return new StructInitializerNode(type, initializers); } default: throw new Exception($"Unknown symbol: {symbolToken.Symbol}"); } } default: throw new Exception($"Unexpected token type {token.GetType().Name}"); } } private ExpressionNode ParseExpressionIdentifier(IdentifierToken identifier) { var token = Peek(); if (!token.HasValue) { return new IdentifierNode(identifier.Value); } switch (token.Value) { case SymbolToken symbolToken: { switch (symbolToken.Symbol) { case Symbol.Period: { Next(); List members = [ identifier.Value, ExpectIdentifier().Value ]; while (TryExpectSymbol(Symbol.Period)) { members.Add(ExpectIdentifier().Value); } return new StructMemberAccessorNode(members); } case Symbol.OpenParen: { Next(); List parameters = []; while (!TryExpectSymbol(Symbol.CloseParen)) { parameters.Add(ParseExpression()); TryExpectSymbol(Symbol.Comma); } if (identifier.Value == "syscall") { return new SyscallExpressionNode(new Syscall(parameters)); } return new FuncCallExpressionNode(new FuncCall(identifier.Value, parameters)); } } break; } } return new IdentifierNode(identifier.Value); } private BlockNode ParseBlock() { ExpectSymbol(Symbol.OpenBrace); List statements = []; while (!TryExpectSymbol(Symbol.CloseBrace)) { statements.Add(ParseStatement()); } return new BlockNode(statements); } private NubType ParseTypeInstance() { var parameters = new List(); var name = ExpectIdentifier().Value; if (TryExpectSymbol(Symbol.LessThan)) { do { parameters.Add(ParseTypeInstance()); } while (TryExpectSymbol(Symbol.Comma)); ExpectSymbol(Symbol.GreaterThan); } return new NubType(name, parameters.ToArray()); } private Token ExpectToken() { var token = Peek(); if (!token.HasValue) { throw new Exception("Reached end of tokens"); } Next(); return token.Value; } private SymbolToken ExpectSymbol() { var token = ExpectToken(); if (token is not SymbolToken symbol) { throw new Exception($"Expected {nameof(SymbolToken)} but got {token.GetType().Name}"); } return symbol; } private void ExpectSymbol(Symbol symbol) { var token = ExpectSymbol(); if (token.Symbol != symbol) { throw new Exception($"Expected symbol {symbol} but got {token.Symbol}"); } } private bool TryExpectSymbol(Symbol symbol) { var result = Peek() is { HasValue: true, Value: SymbolToken symbolToken } && symbolToken.Symbol == symbol; if (result) Next(); return result; } private IdentifierToken ExpectIdentifier() { var token = ExpectToken(); if (token is not IdentifierToken identifier) { throw new Exception($"Expected {nameof(IdentifierToken)} but got {token.GetType().Name}"); } return identifier; } private LiteralToken ExpectLiteral() { var token = ExpectToken(); if (token is not LiteralToken literal) { throw new Exception($"Expected {nameof(LiteralToken)} but got {token.GetType().Name}"); } return literal; } private Optional Peek() { while (_index < _tokens.Count && _tokens.ElementAt(_index) is SymbolToken { Symbol: Symbol.Whitespace }) { Next(); } if (_index < _tokens.Count) { return _tokens.ElementAt(_index); } return Optional.Empty(); } private void Next() { _index++; } }