UNPKG

@prism-lang/core

Version:

A programming language for uncertainty

1,374 lines (1,156 loc) 83.2 kB
import { Token, TokenType, tokenize } from './tokenizer'; import { Expression, Statement, Program, IdentifierExpression, NumberLiteral, StringLiteral, InterpolatedString, BooleanLiteral, NullLiteral, UndefinedLiteral, BinaryExpression, UnaryExpression, CallExpression, TernaryExpression, ConfidentTernaryExpression, ArrayLiteral, ObjectLiteral, PropertyAccess, OptionalChainAccess, IndexAccess, ConfidenceExpression, AssignmentStatement, AssignmentExpression, AwaitExpression, IfStatement, UncertainIfStatement, ContextStatement, AgentDeclaration, BlockStatement, ExpressionStatement, UncertainBranches, AgentConfig, BinaryOperator, UnaryOperator, LambdaExpression, LambdaParameter, SpreadElement, PlaceholderExpression, ForLoop, ForInLoop, WhileLoop, DoWhileLoop, BreakStatement, ContinueStatement, UncertainForLoop, UncertainWhileLoop, ArrayPattern, ObjectPattern, RestElement, DestructuringAssignment, ImportStatement, ExportStatement, ImportSpecifier, ExportSpecifier, FunctionDeclaration, ReturnStatement, VariableDeclaration, } from './ast'; export class ParseError extends Error { constructor(message: string, public token: Token, public sourceCode?: string) { const errorMessage = ParseError.formatError(message, token, sourceCode); super(errorMessage); this.name = 'ParseError'; } private static formatError(message: string, token: Token, sourceCode?: string): string { let errorMsg = `ParseError at line ${token.line}, column ${token.column}: ${message}`; if (sourceCode) { const lines = sourceCode.split('\n'); const errorLine = lines[token.line - 1]; if (errorLine) { errorMsg += '\n\n'; errorMsg += ` ${token.line} | ${errorLine}\n`; errorMsg += ` ${' '.repeat(token.column)}^`; // Add some context - show the token that caused the error if (token.type !== TokenType.EOF) { errorMsg += `\n\nFound: '${token.value}' (${TokenType[token.type]})`; } } } return errorMsg; } } export class Parser { private tokens: Token[]; private current: number = 0; private sourceCode?: string; constructor(tokens: Token[], sourceCode?: string) { this.tokens = tokens; this.sourceCode = sourceCode; } parse(): Program { const statements: Statement[] = []; while (!this.isAtEnd()) { const stmt = this.statement(); if (stmt) { statements.push(stmt); } } return new Program(statements); } private statement(): Statement | null { try { if (this.match(TokenType.ASYNC)) { // async can only be followed by function for now this.consume(TokenType.FUNCTION, "Expected 'function' after 'async'"); return this.functionDeclaration(true); } if (this.match(TokenType.FUNCTION)) { return this.functionDeclaration(false); } if (this.match(TokenType.RETURN)) { return this.returnStatement(); } if (this.match(TokenType.CONST, TokenType.LET)) { return this.variableDeclaration(); } if (this.match(TokenType.AGENTS)) { return this.agentsStatement(); } if (this.match(TokenType.UNCERTAIN)) { // Check what follows 'uncertain' if (this.check(TokenType.IF)) { return this.uncertainIfStatement(); } else if (this.check(TokenType.FOR)) { return this.uncertainForStatement(); } else if (this.check(TokenType.WHILE)) { return this.uncertainWhileStatement(); } else { throw new ParseError("Expected 'if', 'for', or 'while' after 'uncertain'", this.peek(), this.sourceCode); } } if (this.match(TokenType.IF)) { return this.ifStatement(); } if (this.match(TokenType.FOR)) { return this.forStatement(); } if (this.match(TokenType.WHILE)) { return this.whileStatement(); } if (this.match(TokenType.DO)) { return this.doWhileStatement(); } if (this.match(TokenType.BREAK)) { return this.breakStatement(); } if (this.match(TokenType.CONTINUE)) { return this.continueStatement(); } if (this.match(TokenType.IN)) { return this.contextStatement(); } if (this.match(TokenType.IMPORT)) { return this.importStatement(); } if (this.match(TokenType.EXPORT)) { return this.exportStatement(); } // Check for potential destructuring patterns if (this.check(TokenType.LEFT_BRACKET)) { // Array destructuring pattern - parse as expression statement return this.expressionStatement(); } if (this.check(TokenType.LEFT_BRACE)) { // Look ahead to determine if this is a block or object literal const checkpoint = this.current; this.advance(); // consume { let isObjectOrDestructuring = false; // Check various patterns that indicate object literal or destructuring if (this.check(TokenType.RIGHT_BRACE)) { // Empty object {} isObjectOrDestructuring = true; } else if (this.check(TokenType.SPREAD)) { // Spread in object {...x} isObjectOrDestructuring = true; } else if (this.check(TokenType.IDENTIFIER)) { // Look ahead one more token after identifier this.advance(); if (this.check(TokenType.COMMA) || this.check(TokenType.COLON) || this.check(TokenType.RIGHT_BRACE)) { // Object property syntax isObjectOrDestructuring = true; } else if (this.check(TokenType.CONFIDENCE_ARROW)) { // Confidence arrow after identifier in {} context // This is likely a destructuring pattern isObjectOrDestructuring = true; } else if (this.check(TokenType.EQUAL)) { // Could be destructuring with default value {x = default} or assignment {x = y} // Look further ahead to distinguish const checkPoint2 = this.current; this.advance(); // skip = // Skip the default value expression let depth = 0; while (!this.isAtEnd() && (depth > 0 || (!this.check(TokenType.COMMA) && !this.check(TokenType.RIGHT_BRACE)))) { if (this.check(TokenType.LEFT_PAREN) || this.check(TokenType.LEFT_BRACKET) || this.check(TokenType.LEFT_BRACE)) { depth++; } else if (this.check(TokenType.RIGHT_PAREN) || this.check(TokenType.RIGHT_BRACKET) || this.check(TokenType.RIGHT_BRACE)) { depth--; } this.advance(); } // If followed by comma or }, it's likely object destructuring with default if (this.check(TokenType.COMMA) || this.check(TokenType.RIGHT_BRACE)) { // Check if the closing } is followed by = if (this.check(TokenType.RIGHT_BRACE)) { const checkPoint3 = this.current; this.advance(); // skip } if (this.check(TokenType.EQUAL)) { isObjectOrDestructuring = true; } this.current = checkPoint3; } else { // Has comma, likely part of destructuring pattern isObjectOrDestructuring = true; } } this.current = checkPoint2; } } // Restore position this.current = checkpoint; if (isObjectOrDestructuring) { // Parse as expression statement (which will handle both object literals and destructuring) return this.expressionStatement(); } else { // Parse as block statement this.advance(); // consume { return this.blockStatement(); } } return this.expressionStatement(); } catch (error) { this.synchronize(); throw error; } } private agentsStatement(): Statement { this.consume(TokenType.LEFT_BRACE, "Expected '{' after 'agents'"); const statements: Statement[] = []; while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) { const agentDecl = this.agentDeclaration(); statements.push(agentDecl); } this.consume(TokenType.RIGHT_BRACE, "Expected '}' after agents block"); return new BlockStatement(statements); } private agentDeclaration(): AgentDeclaration { const name = this.consume(TokenType.IDENTIFIER, "Expected agent name").value; this.consume(TokenType.COLON, "Expected ':' after agent name"); this.consume(TokenType.AGENT, "Expected 'Agent' keyword"); const config: AgentConfig = {}; if (this.match(TokenType.LEFT_BRACE)) { while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) { const key = this.consume(TokenType.IDENTIFIER, "Expected config key").value; this.consume(TokenType.COLON, "Expected ':' after config key"); if (key === 'confidence') { const value = this.consume(TokenType.NUMBER, "Expected number for confidence").value; config.confidence = parseFloat(value); } else if (key === 'role') { const value = this.consume(TokenType.STRING, "Expected string for role").value; config.role = value; } if (this.check(TokenType.COMMA)) { this.advance(); } } this.consume(TokenType.RIGHT_BRACE, "Expected '}' after agent config"); } return new AgentDeclaration(name, config); } private uncertainIfStatement(): UncertainIfStatement { this.consume(TokenType.IF, "Expected 'if' after 'uncertain'"); this.consume(TokenType.LEFT_PAREN, "Expected '(' after 'uncertain if'"); const condition = this.expression(); const threshold = 0.5; // default threshold, actual evaluation happens at runtime this.consume(TokenType.RIGHT_PAREN, "Expected ')' after condition"); this.consume(TokenType.LEFT_BRACE, "Expected '{' after condition"); const branches: UncertainBranches = {}; while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) { if (this.match(TokenType.HIGH)) { this.consume(TokenType.LEFT_BRACE, "Expected '{' after 'high'"); const statements = this.blockContents(); branches.high = new BlockStatement(statements); this.consume(TokenType.RIGHT_BRACE, "Expected '}' after high branch"); } else if (this.match(TokenType.MEDIUM)) { this.consume(TokenType.LEFT_BRACE, "Expected '{' after 'medium'"); const statements = this.blockContents(); branches.medium = new BlockStatement(statements); this.consume(TokenType.RIGHT_BRACE, "Expected '}' after medium branch"); } else if (this.match(TokenType.LOW)) { this.consume(TokenType.LEFT_BRACE, "Expected '{' after 'low'"); const statements = this.blockContents(); branches.low = new BlockStatement(statements); this.consume(TokenType.RIGHT_BRACE, "Expected '}' after low branch"); } else if (this.match(TokenType.DEFAULT)) { this.consume(TokenType.LEFT_BRACE, "Expected '{' after 'default'"); const statements = this.blockContents(); branches.default = new BlockStatement(statements); this.consume(TokenType.RIGHT_BRACE, "Expected '}' after default branch"); } else { throw new ParseError("Expected 'high', 'medium', 'low', or 'default' branch", this.peek(), this.sourceCode); } } this.consume(TokenType.RIGHT_BRACE, "Expected '}' after uncertain if branches"); return new UncertainIfStatement(condition!, threshold, branches); } private ifStatement(): IfStatement { this.consume(TokenType.LEFT_PAREN, "Expected '(' after 'if'"); const condition = this.expression(); this.consume(TokenType.RIGHT_PAREN, "Expected ')' after if condition"); // For if statements, always treat { as a block statement let thenStatement: Statement; if (this.check(TokenType.LEFT_BRACE)) { this.advance(); // consume { thenStatement = this.blockStatement(); } else { const stmt = this.statement(); if (!stmt) throw new ParseError("Expected statement", this.peek(), this.sourceCode); thenStatement = stmt; } let elseStatement: Statement | undefined = undefined; if (this.match(TokenType.ELSE)) { // For else, also handle blocks directly if (this.check(TokenType.LEFT_BRACE)) { this.advance(); // consume { elseStatement = this.blockStatement(); } else { elseStatement = this.statement() || undefined; } } return new IfStatement(condition!, thenStatement!, elseStatement); } private contextStatement(): ContextStatement { this.consume(TokenType.CONTEXT, "Expected 'context' after 'in'"); const contextName = this.consume(TokenType.IDENTIFIER, "Expected context name").value; // For context statements, handle blocks directly let body: Statement; if (this.check(TokenType.LEFT_BRACE)) { this.advance(); // consume { body = this.blockStatement(); } else { const stmt = this.statement(); if (!stmt) throw new ParseError("Expected statement", this.peek(), this.sourceCode); body = stmt; } let shiftTo: string | undefined = undefined; if (this.match(TokenType.SHIFTING)) { this.consume(TokenType.TO, "Expected 'to' after 'shifting'"); shiftTo = this.consume(TokenType.IDENTIFIER, "Expected context name after 'to'").value; // Parse the shifting target block if (this.check(TokenType.LEFT_BRACE)) { this.advance(); // consume { this.blockStatement(); // consume the shifting target block } else { this.statement(); // consume the shifting target statement } } return new ContextStatement(contextName, body!, shiftTo); } private blockStatement(): BlockStatement { const statements = this.blockContents(); this.consume(TokenType.RIGHT_BRACE, "Expected '}' after block"); return new BlockStatement(statements); } private blockContents(): Statement[] { const statements: Statement[] = []; while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) { const stmt = this.statement(); if (stmt) { statements.push(stmt); } } return statements; } private expressionStatement(): Statement { // Look for destructuring pattern at the start if (this.check(TokenType.LEFT_BRACKET) || this.check(TokenType.LEFT_BRACE)) { // Use lookahead to check if this is likely a destructuring assignment const isLikelyDestructuring = this.isLikelyDestructuringPattern(); // console.log('At start of expressionStatement, token:', this.peek(), 'isLikelyDestructuring:', isLikelyDestructuring); if (isLikelyDestructuring) { const pattern = this.tryParseDestructuringPattern(); // console.log('Parsed pattern:', pattern); if (pattern) { // Check for confidence threshold (Option 1: [a, b] ~> 0.8 = array) let confidenceThreshold: Expression | undefined; if (this.match(TokenType.CONFIDENCE_ARROW)) { const threshold = this.expression(); if (threshold) { confidenceThreshold = threshold; } } if (this.match(TokenType.EQUAL)) { const value = this.expression(); this.match(TokenType.SEMICOLON); return new DestructuringAssignment(pattern, value!, confidenceThreshold); } else { throw new ParseError("Expected '=' after destructuring pattern", this.peek(), this.sourceCode); } } } } const expr = this.expression(); // Check if this is an assignment or compound assignment if (expr instanceof IdentifierExpression) { if (this.match(TokenType.EQUAL)) { const value = this.expression(); // Consume optional semicolon this.match(TokenType.SEMICOLON); return new AssignmentStatement(expr.name, value!); } else if (this.match(TokenType.PLUS_EQUAL)) { // x += y becomes x = x + y const right = this.expression(); const value = new BinaryExpression('+', expr, right!); this.match(TokenType.SEMICOLON); return new AssignmentStatement(expr.name, value); } else if (this.match(TokenType.MINUS_EQUAL)) { // x -= y becomes x = x - y const right = this.expression(); const value = new BinaryExpression('-', expr, right!); this.match(TokenType.SEMICOLON); return new AssignmentStatement(expr.name, value); } else if (this.match(TokenType.STAR_EQUAL)) { // x *= y becomes x = x * y const right = this.expression(); const value = new BinaryExpression('*', expr, right!); this.match(TokenType.SEMICOLON); return new AssignmentStatement(expr.name, value); } else if (this.match(TokenType.SLASH_EQUAL)) { // x /= y becomes x = x / y const right = this.expression(); const value = new BinaryExpression('/', expr, right!); this.match(TokenType.SEMICOLON); return new AssignmentStatement(expr.name, value); } else if (this.match(TokenType.PERCENT_EQUAL)) { // x %= y becomes x = x % y const right = this.expression(); const value = new BinaryExpression('%', expr, right!); this.match(TokenType.SEMICOLON); return new AssignmentStatement(expr.name, value); } else if (this.match(TokenType.CONFIDENCE_PLUS_EQUAL)) { // x ~+= y becomes x = x ~+ y const right = this.expression(); const value = new BinaryExpression('~+', expr, right!); this.match(TokenType.SEMICOLON); return new AssignmentStatement(expr.name, value); } else if (this.match(TokenType.CONFIDENCE_MINUS_EQUAL)) { // x ~-= y becomes x = x ~- y const right = this.expression(); const value = new BinaryExpression('~-', expr, right!); this.match(TokenType.SEMICOLON); return new AssignmentStatement(expr.name, value); } else if (this.match(TokenType.CONFIDENCE_STAR_EQUAL)) { // x ~*= y becomes x = x ~* y const right = this.expression(); const value = new BinaryExpression('~*', expr, right!); this.match(TokenType.SEMICOLON); return new AssignmentStatement(expr.name, value); } else if (this.match(TokenType.CONFIDENCE_SLASH_EQUAL)) { // x ~/= y becomes x = x ~/ y const right = this.expression(); const value = new BinaryExpression('~/', expr, right!); this.match(TokenType.SEMICOLON); return new AssignmentStatement(expr.name, value); } } // Consume optional semicolon this.match(TokenType.SEMICOLON); // Wrap expression in an ExpressionStatement return new ExpressionStatement(expr!); } private forStatement(): Statement { // for statement can be either: // 1. C-style: for i = 0; i < 10; i++ { ... } // 2. For-in: for item in array { ... } // 3. For-in with index: for item, index in array { ... } // Check if this is a for-in loop by looking ahead const checkPoint = this.current; // Try to parse as for-in first if (this.check(TokenType.IDENTIFIER)) { this.advance(); if (this.check(TokenType.COMMA)) { // for item, index in array this.advance(); // consume comma if (this.check(TokenType.IDENTIFIER)) { this.advance(); if (this.check(TokenType.IN)) { // Reset and parse as for-in with index this.current = checkPoint; return this.forInStatement(); } } } else if (this.check(TokenType.IN)) { // for item in array this.current = checkPoint; return this.forInStatement(); } } // Reset position and parse as C-style for loop this.current = checkPoint; // C-style for loop: for init; condition; update { body } let init: Statement | null = null; let condition: Expression | null = null; let update: Expression | null = null; // Parse init if (!this.check(TokenType.SEMICOLON)) { if (this.check(TokenType.IDENTIFIER) && this.peekNext().type === TokenType.EQUAL) { // Variable assignment const identifier = this.advance().value; this.consume(TokenType.EQUAL, "Expected '=' in assignment"); const value = this.expression(); if (!value) { throw new ParseError("Expected expression after '='", this.previous(), this.sourceCode); } init = new AssignmentStatement(identifier, value); } else { // Expression const expr = this.expression(); if (expr) { init = new ExpressionStatement(expr); } } } if (!this.match(TokenType.SEMICOLON)) { throw new ParseError("Expected ';' after for loop initializer", this.peek(), this.sourceCode); } // Parse condition if (!this.check(TokenType.SEMICOLON)) { condition = this.expression(); } if (!this.match(TokenType.SEMICOLON)) { throw new ParseError("Expected ';' after for loop condition", this.peek(), this.sourceCode); } // Parse update if (!this.check(TokenType.LEFT_BRACE)) { // Check if this is an assignment if (this.check(TokenType.IDENTIFIER)) { const checkPoint = this.current; const identifier = this.advance(); if (this.match(TokenType.EQUAL)) { // It's an assignment expression const value = this.expression(); if (!value) { throw new ParseError("Expected expression after '='", this.previous(), this.sourceCode); } update = new AssignmentExpression(identifier.value, value); } else { // Not an assignment, reset and parse as regular expression this.current = checkPoint; update = this.expression(); } } else { update = this.expression(); } } // Parse body const body = this.statement(); if (!body) { throw new ParseError("Expected body for for loop", this.peek(), this.sourceCode); } return new ForLoop(init, condition, update, body); } private forInStatement(): Statement { // Parse variable name if (!this.check(TokenType.IDENTIFIER)) { throw new ParseError("Expected identifier in for-in loop", this.peek(), this.sourceCode); } const variable = this.advance().value; // Check for optional index variable let index: string | null = null; if (this.match(TokenType.COMMA)) { if (!this.check(TokenType.IDENTIFIER)) { throw new ParseError("Expected identifier after comma in for-in loop", this.peek(), this.sourceCode); } index = this.advance().value; } // Expect 'in' if (!this.match(TokenType.IN)) { throw new ParseError("Expected 'in' in for-in loop", this.peek(), this.sourceCode); } // Parse iterable expression const iterable = this.expression(); if (!iterable) { throw new ParseError("Expected expression after 'in'", this.peek(), this.sourceCode); } // Parse body const body = this.statement(); if (!body) { throw new ParseError("Expected body for for-in loop", this.peek(), this.sourceCode); } return new ForInLoop(variable, index, iterable, body); } private whileStatement(): Statement { // Parse condition const condition = this.expression(); if (!condition) { throw new ParseError("Expected condition in while loop", this.peek(), this.sourceCode); } // Parse body const body = this.statement(); if (!body) { throw new ParseError("Expected body for while loop", this.peek(), this.sourceCode); } return new WhileLoop(condition, body); } private doWhileStatement(): Statement { // Parse body const body = this.statement(); if (!body) { throw new ParseError("Expected body for do-while loop", this.peek(), this.sourceCode); } // Expect 'while' if (!this.match(TokenType.WHILE)) { throw new ParseError("Expected 'while' after do-while body", this.peek(), this.sourceCode); } // Parse condition const condition = this.expression(); if (!condition) { throw new ParseError("Expected condition in do-while loop", this.peek(), this.sourceCode); } return new DoWhileLoop(body, condition); } private breakStatement(): Statement { return new BreakStatement(); } private continueStatement(): Statement { return new ContinueStatement(); } private uncertainForStatement(): Statement { this.consume(TokenType.FOR, "Expected 'for' after 'uncertain'"); // Parse init, condition, update like regular for loop let init: Statement | null = null; let condition: Expression | null = null; let update: Expression | null = null; // Parse init if (!this.check(TokenType.SEMICOLON)) { if (this.check(TokenType.IDENTIFIER) && this.peekNext().type === TokenType.EQUAL) { const identifier = this.advance().value; this.consume(TokenType.EQUAL, "Expected '=' in assignment"); const value = this.expression(); if (!value) { throw new ParseError("Expected expression after '='", this.previous(), this.sourceCode); } init = new AssignmentStatement(identifier, value); } else { const expr = this.expression(); if (expr) { init = new ExpressionStatement(expr); } } } if (!this.match(TokenType.SEMICOLON)) { throw new ParseError("Expected ';' after for loop initializer", this.peek(), this.sourceCode); } // Parse condition if (!this.check(TokenType.SEMICOLON)) { condition = this.expression(); } if (!this.match(TokenType.SEMICOLON)) { throw new ParseError("Expected ';' after for loop condition", this.peek(), this.sourceCode); } // Parse update if (!this.check(TokenType.LEFT_BRACE)) { if (this.check(TokenType.IDENTIFIER)) { const checkPoint = this.current; const identifier = this.advance(); if (this.match(TokenType.EQUAL)) { const value = this.expression(); if (!value) { throw new ParseError("Expected expression after '='", this.previous(), this.sourceCode); } update = new AssignmentExpression(identifier.value, value); } else { this.current = checkPoint; update = this.expression(); } } else { update = this.expression(); } } // Parse uncertain branches this.consume(TokenType.LEFT_BRACE, "Expected '{' after uncertain for loop header"); const branches = this.parseUncertainBranches(); this.consume(TokenType.RIGHT_BRACE, "Expected '}' after uncertain for branches"); return new UncertainForLoop(init, condition, update, branches); } private uncertainWhileStatement(): Statement { this.consume(TokenType.WHILE, "Expected 'while' after 'uncertain'"); // Parse condition - must be a confident expression const condition = this.expression(); if (!condition) { throw new ParseError("Expected condition in uncertain while loop", this.peek(), this.sourceCode); } // Parse uncertain branches this.consume(TokenType.LEFT_BRACE, "Expected '{' after uncertain while condition"); const branches = this.parseUncertainBranches(); this.consume(TokenType.RIGHT_BRACE, "Expected '}' after uncertain while branches"); return new UncertainWhileLoop(condition, branches); } private parseUncertainBranches(): UncertainBranches { const branches: UncertainBranches = {}; while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) { if (this.match(TokenType.HIGH)) { if (branches.high) { throw new ParseError("Duplicate 'high' branch in uncertain statement", this.previous(), this.sourceCode); } this.consume(TokenType.LEFT_BRACE, "Expected '{' after 'high'"); const statements = this.blockContents(); branches.high = new BlockStatement(statements); this.consume(TokenType.RIGHT_BRACE, "Expected '}' after high branch"); } else if (this.match(TokenType.MEDIUM)) { if (branches.medium) { throw new ParseError("Duplicate 'medium' branch in uncertain statement", this.previous(), this.sourceCode); } this.consume(TokenType.LEFT_BRACE, "Expected '{' after 'medium'"); const statements = this.blockContents(); branches.medium = new BlockStatement(statements); this.consume(TokenType.RIGHT_BRACE, "Expected '}' after medium branch"); } else if (this.match(TokenType.LOW)) { if (branches.low) { throw new ParseError("Duplicate 'low' branch in uncertain statement", this.previous(), this.sourceCode); } this.consume(TokenType.LEFT_BRACE, "Expected '{' after 'low'"); const statements = this.blockContents(); branches.low = new BlockStatement(statements); this.consume(TokenType.RIGHT_BRACE, "Expected '}' after low branch"); } else if (this.match(TokenType.DEFAULT)) { if (branches.default) { throw new ParseError("Duplicate 'default' branch in uncertain statement", this.previous(), this.sourceCode); } this.consume(TokenType.LEFT_BRACE, "Expected '{' after 'default'"); const statements = this.blockContents(); branches.default = new BlockStatement(statements); this.consume(TokenType.RIGHT_BRACE, "Expected '}' after default branch"); } else { throw new ParseError("Expected 'high', 'medium', 'low', or 'default' branch in uncertain statement", this.peek(), this.sourceCode); } } return branches; } private importStatement(): ImportStatement { const specifiers: ImportSpecifier[] = []; let defaultImport: string | undefined; let namespaceImport: string | undefined; let source: string; // Check for namespace import: import * as name from "module" if (this.match(TokenType.STAR)) { this.consume(TokenType.AS, "Expected 'as' after '*'"); namespaceImport = this.consume(TokenType.IDENTIFIER, "Expected namespace name after 'as'").value; } // Check for named imports: import {name1, name2} from "module" else if (this.check(TokenType.LEFT_BRACE)) { this.advance(); // consume { while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) { const imported = this.consume(TokenType.IDENTIFIER, "Expected import name").value; let local = imported; // Default to same name // Check for renaming: import {original as renamed} if (this.match(TokenType.AS)) { local = this.consume(TokenType.IDENTIFIER, "Expected local name after 'as'").value; } specifiers.push(new ImportSpecifier(imported, local !== imported ? local : undefined)); if (this.match(TokenType.COMMA)) { // Allow trailing comma if (this.check(TokenType.RIGHT_BRACE)) { break; } } else if (!this.check(TokenType.RIGHT_BRACE)) { throw new ParseError("Expected ',' or '}' after import specifier", this.peek(), this.sourceCode); } } this.consume(TokenType.RIGHT_BRACE, "Expected '}' after import specifiers"); } // Check for default import: import defaultName from "module" else if (this.check(TokenType.IDENTIFIER)) { defaultImport = this.advance().value; // Check for mixed import: import defaultName, {named} from "module" if (this.match(TokenType.COMMA)) { if (this.match(TokenType.LEFT_BRACE)) { while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) { const imported = this.consume(TokenType.IDENTIFIER, "Expected import name").value; let local = imported; if (this.match(TokenType.AS)) { local = this.consume(TokenType.IDENTIFIER, "Expected local name after 'as'").value; } specifiers.push(new ImportSpecifier(imported, local !== imported ? local : undefined)); if (this.match(TokenType.COMMA)) { if (this.check(TokenType.RIGHT_BRACE)) { break; } } else if (!this.check(TokenType.RIGHT_BRACE)) { throw new ParseError("Expected ',' or '}' after import specifier", this.peek(), this.sourceCode); } } this.consume(TokenType.RIGHT_BRACE, "Expected '}' after import specifiers"); } } } else { // No valid import pattern found - this is an error throw new ParseError("Expected import specifier", this.peek(), this.sourceCode); } // Expect 'from' keyword this.consume(TokenType.FROM, "Expected 'from' after import specifiers"); // Parse module source source = this.consume(TokenType.STRING, "Expected module path after 'from'").value; this.match(TokenType.SEMICOLON); // Optional semicolon return new ImportStatement(specifiers, source, defaultImport, namespaceImport); } private exportStatement(): ExportStatement { // Check for different export patterns // export default expression if (this.match(TokenType.DEFAULT)) { const declaration = this.expressionStatement(); return new ExportStatement(undefined, undefined, declaration, true); } // export * from "module" if (this.match(TokenType.STAR)) { this.consume(TokenType.FROM, "Expected 'from' after 'export *'"); const source = this.consume(TokenType.STRING, "Expected module path after 'from'").value; this.match(TokenType.SEMICOLON); // Optional semicolon return new ExportStatement(undefined, source, undefined, false, true); } // export {name1, name2} or export {name1, name2} from "module" if (this.match(TokenType.LEFT_BRACE)) { const specifiers: ExportSpecifier[] = []; while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) { const local = this.consume(TokenType.IDENTIFIER, "Expected export name").value; let exported = local; // Default to same name // Check for renaming: export {original as renamed} if (this.match(TokenType.AS)) { exported = this.consume(TokenType.IDENTIFIER, "Expected exported name after 'as'").value; } specifiers.push(new ExportSpecifier(local, exported !== local ? exported : undefined)); if (this.match(TokenType.COMMA)) { // Allow trailing comma if (this.check(TokenType.RIGHT_BRACE)) { break; } } else if (!this.check(TokenType.RIGHT_BRACE)) { throw new ParseError("Expected ',' or '}' after export specifier", this.peek(), this.sourceCode); } } this.consume(TokenType.RIGHT_BRACE, "Expected '}' after export specifiers"); // Check for re-export: export {names} from "module" let source: string | undefined; if (this.match(TokenType.FROM)) { source = this.consume(TokenType.STRING, "Expected module path after 'from'").value; } this.match(TokenType.SEMICOLON); // Optional semicolon return new ExportStatement(specifiers, source); } // export statement (direct export) const declaration = this.statement(); if (!declaration) { throw new ParseError("Expected declaration after 'export'", this.peek(), this.sourceCode); } return new ExportStatement(undefined, undefined, declaration); } private functionDeclaration(isAsync: boolean = false): FunctionDeclaration { // Parse function name const name = this.consume(TokenType.IDENTIFIER, "Expected function name").value; // Parse parameters this.consume(TokenType.LEFT_PAREN, "Expected '(' after function name"); const parameters: LambdaParameter[] = []; let restParameter: string | ArrayPattern | ObjectPattern | undefined = undefined; if (!this.check(TokenType.RIGHT_PAREN)) { do { if (this.match(TokenType.SPREAD)) { // Rest parameter if (this.check(TokenType.IDENTIFIER)) { restParameter = this.advance().value; } else if (this.check(TokenType.LEFT_BRACKET) || this.check(TokenType.LEFT_BRACE)) { // Rest with destructuring pattern const pattern = this.tryParseDestructuringPattern(); if (!pattern) { throw new ParseError("Expected parameter pattern after '...'", this.peek(), this.sourceCode); } restParameter = pattern; } else { throw new ParseError("Expected parameter name or pattern after '...'", this.peek(), this.sourceCode); } // Rest parameter must be last if (this.match(TokenType.COMMA)) { throw new ParseError("Rest parameter must be last formal parameter", this.previous(), this.sourceCode); } } else if (this.match(TokenType.IDENTIFIER)) { if (restParameter) { throw new ParseError("Rest parameter must be last formal parameter", this.previous(), this.sourceCode); } parameters.push(this.previous().value); } else if (this.check(TokenType.LEFT_BRACKET) || this.check(TokenType.LEFT_BRACE)) { // Destructuring pattern parameter if (restParameter) { throw new ParseError("Rest parameter must be last formal parameter", this.previous(), this.sourceCode); } const pattern = this.tryParseDestructuringPattern(); if (!pattern) { throw new ParseError("Expected parameter pattern", this.peek(), this.sourceCode); } parameters.push(pattern); } else { throw new ParseError("Expected parameter name or pattern", this.peek(), this.sourceCode); } } while (this.match(TokenType.COMMA)); } this.consume(TokenType.RIGHT_PAREN, "Expected ')' after parameters"); // Parse optional confidence annotation let confidenceAnnotation: Expression | undefined; if (this.match(TokenType.CONFIDENCE_ARROW)) { const confExpr = this.expression(); if (!confExpr) { throw new ParseError("Expected confidence expression after '~>'", this.previous(), this.sourceCode); } confidenceAnnotation = confExpr; } // Parse function body (must be a block) this.consume(TokenType.LEFT_BRACE, "Expected '{' before function body"); const body = this.blockStatement(); return new FunctionDeclaration(name, parameters, body, restParameter, confidenceAnnotation, isAsync); } private returnStatement(): ReturnStatement { let value: Expression | undefined; // Check if there's a return value (not followed by semicolon or EOF) if (!this.check(TokenType.SEMICOLON) && !this.isAtEnd() && !this.check(TokenType.RIGHT_BRACE)) { value = this.expression() || undefined; } this.match(TokenType.SEMICOLON); // Optional semicolon return new ReturnStatement(value); } private variableDeclaration(): VariableDeclaration { // Get the declaration kind (const or let) const kind = this.previous().value as 'const' | 'let'; // Check for destructuring pattern if (this.check(TokenType.LEFT_BRACKET) || this.check(TokenType.LEFT_BRACE)) { const pattern = this.tryParseDestructuringPattern(); if (pattern) { // Pattern destructuring: const [a, b] = array or const {x, y} = obj this.consume(TokenType.EQUAL, "Expected '=' after destructuring pattern"); const initializer = this.expression(); if (!initializer) { throw new ParseError("Expected expression after '='", this.peek(), this.sourceCode); } this.match(TokenType.SEMICOLON); // Optional semicolon return new VariableDeclaration(kind, '', initializer, pattern); } } // Regular variable declaration: const/let name = value const identifier = this.consume(TokenType.IDENTIFIER, "Expected variable name").value; // const requires an initializer, let allows optional initializer let initializer: Expression | undefined; if (this.match(TokenType.EQUAL)) { const expr = this.expression(); if (!expr) { throw new ParseError("Expected expression after '='", this.previous(), this.sourceCode); } initializer = expr; } else if (kind === 'const') { throw new ParseError("const declarations must have an initializer", this.peek(), this.sourceCode); } this.match(TokenType.SEMICOLON); // Optional semicolon return new VariableDeclaration(kind, identifier, initializer); } private expression(): Expression | null { return this.pipeline(); } private pipeline(): Expression | null { let expr = this.ternary(); while (this.match(TokenType.PIPELINE, TokenType.CONFIDENCE_PIPELINE, TokenType.CONFIDENCE_THRESHOLD_GATE)) { const operator = this.previous(); if (operator.type === TokenType.CONFIDENCE_THRESHOLD_GATE) { // Handle threshold gate operator ~?> const threshold = this.ternary(); if (!threshold) { throw new ParseError("Expected threshold expression after '~?>'", this.previous(), this.sourceCode); } // Create threshold gate expression expr = new BinaryExpression('~?>' as BinaryOperator, expr!, threshold); } else { // Handle regular and confidence pipeline operators const isConfidencePipeline = operator.type === TokenType.CONFIDENCE_PIPELINE; const right = this.ternary(); if (!right) { throw new ParseError("Expected expression after pipeline operator", this.previous(), this.sourceCode); } // Replace placeholders in the right expression with the left expression const pipelineExpr = this.replacePlaceholders(right, expr!); // For confidence pipeline, wrap in a binary expression to preserve confidence if (isConfidencePipeline) { expr = new BinaryExpression('~|>' as BinaryOperator, expr!, pipelineExpr); } else { expr = pipelineExpr; } } } return expr; } private ternary(): Expression | null { const expr = this.confidenceExpression(); if (this.match(TokenType.CONFIDENCE_QUESTION)) { const trueBranch = this.expression(); if (!trueBranch) { throw new ParseError("Expected expression after '~?'", this.previous(), this.sourceCode); } if (!this.match(TokenType.COLON)) { throw new ParseError("Expected ':' after true branch of confident ternary operator", this.peek(), this.sourceCode); } const falseBranch = this.expression(); if (!falseBranch) { throw new ParseError("Expected expression after ':'", this.previous(), this.sourceCode); } return new ConfidentTernaryExpression(expr!, trueBranch, falseBranch); } if (this.match(TokenType.QUESTION)) { const trueBranch = this.expression(); if (!trueBranch) { throw new ParseError("Expected expression after '?'", this.previous(), this.sourceCode); } if (!this.match(TokenType.COLON)) { throw new ParseError("Expected ':' after true branch of ternary operator", this.peek(), this.sourceCode); } const falseBranch = this.expression(); if (!falseBranch) { throw new ParseError("Expected expression after ':'", this.previous(), this.sourceCode); } return new TernaryExpression(expr!, trueBranch, falseBranch); } return expr; } private confidenceExpression(): Expression | null { let expr = this.logicalOr(); if (this.match(TokenType.CONFIDENCE_ARROW)) { const confidence = this.primary(); if (confidence) { return new ConfidenceExpression(expr!, confidence); } throw new ParseError("Expected expression after '~>'", this.previous(), this.sourceCode); } // Handle confidence chaining operator (~~) while (this.match(TokenType.CONFIDENCE_CHAIN)) { const operator = this.previous().value as BinaryOperator; const right = this.logicalOr(); expr = new BinaryExpression(operator, expr!, right!); } return expr; } private logicalOr(): Expression | null { let expr = this.nullishCoalesce(); while (this.match(TokenType.OR, TokenType.CONFIDENCE_OR, TokenType.PARALLEL_CONFIDENCE, TokenType.THRESHOLD_GATE)) { const operator = this.previous().value as BinaryOperator; const right = this.nullishCoalesce(); expr = new BinaryExpression(operator, expr!, right!); } return expr; } private nullishCoalesce(): Expression | null { let expr = this.confidenceCoalesce(); while (this.match(TokenType.QUESTION_QUESTION)) { const operator = this.previous().value as BinaryOperator; const right = this.confidenceCoalesce(); expr = new BinaryExpression(operator, expr!, right!); } return expr; } private confidenceCoalesce(): Expression | null { let expr = this.logicalAnd(); while (this.match(TokenType.CONFIDENCE_COALESCE)) { const operator = this.previous().value as BinaryOperator; const right = this.logicalAnd(); expr = new BinaryExpression(operator, expr!, right!); } return expr; } private logicalAnd(): Expression | null { let expr = this.equality(); while (this.match(TokenType.AND, TokenType.CONFIDENCE_AND)) { const operatorToken = this.previous(); const operator = operatorToken.value as BinaryOperator; const right = this.equality(); expr = new BinaryExpression(operator, expr!, right!).setLocation(operatorToken.line, operatorToken.column); } return expr; } private equality(): Expression | null { let expr = this.comparison(); while (this.match(TokenType.NOT_EQUAL, TokenType.EQUAL_EQUAL, TokenType.NOT_EQUAL_EQUAL, TokenType.EQUAL_EQUAL_EQUAL, TokenType.CONFIDENCE_EQUAL, TokenType.CONFIDENCE_NOT_EQUAL)) { const operator = this.previous().value as BinaryOperator; const right = this.comparison(); expr = new BinaryExpression(operator, expr!, right!); } return expr; } private comparison(): Expression | null { let expr = this.term(); while (this.match(TokenType.GREATER, TokenType.GREATER_EQUAL, TokenType.LESS, TokenType.LESS_EQUAL, TokenType.CONFIDENCE_GREATER_EQUAL, TokenType.CONFIDENCE_LESS, TokenType.CONFIDENCE_LESS_EQUAL, TokenType.INSTANCEOF)) { const operator = this.previous().value as BinaryOperator; const right = this.term(); expr = new BinaryExpression(operator, expr!, right!); } return expr; } private term(): Expression | null { let expr = this.factor(); while (this.match(TokenType.MINUS, TokenType.PLUS, TokenType.CONFIDENCE_MINUS, TokenType.CONFIDENCE_PLUS)) { const operator = this.previous().value as BinaryOperator; const right = this.factor(); expr = new BinaryExpression(operator, expr!, right!); } return expr; } private factor(): Expression | null { let expr = this.exponent(); while (this.match(TokenType.SLASH, TokenType.STAR, TokenType.PERCENT, TokenType.CONFIDENCE_SLASH, TokenType.CONFIDENCE_STAR)) { const operator = this.previous().value as BinaryOperator; const right = this.exponent(); expr = new BinaryExpression(operator, expr!, right!); } return expr; } private exponent(): Expression | null { let expr = this.unary(); // Right-associative: parse from right to left if (this.match(TokenType.STAR_STAR)) { const operator = this.previous().value as BinaryOperator; const right = this.exponent(); // Recursive for right-associativity expr = new BinaryExpression(operator, expr!, right!); } return expr; } private unary(): Expression | null { if (this.match(TokenType.NOT, TokenType.MINUS, TokenType.TILDE, TokenType.CONFIDENCE_EXTRACT, TokenType.TYPEOF)) { const operator = this.previous().value as UnaryOperator; const right = this.unary(); return new UnaryExpression(operator, right!); } if (this.match(TokenType.AWAIT)) { const expr = this.unary(); if (!expr) { throw new ParseError("Expected expression after 'await'", this.previous(), this.sourceCode); } return new AwaitExpression(expr); } return this.call(); } private call(): Expression | null { const expr = this.primary(); let current = expr; while (current) { if (this.match(TokenType.LEFT_PAREN)) { current = this.finishCall(current);