@prism-lang/core
Version:
A programming language for uncertainty
993 lines • 96.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Parser = exports.ParseError = void 0;
exports.parse = parse;
const tokenizer_1 = require("./tokenizer");
const ast_1 = require("./ast");
class ParseError extends Error {
token;
sourceCode;
constructor(message, token, sourceCode) {
const errorMessage = ParseError.formatError(message, token, sourceCode);
super(errorMessage);
this.token = token;
this.sourceCode = sourceCode;
this.name = 'ParseError';
}
static formatError(message, token, sourceCode) {
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 !== tokenizer_1.TokenType.EOF) {
errorMsg += `\n\nFound: '${token.value}' (${tokenizer_1.TokenType[token.type]})`;
}
}
}
return errorMsg;
}
}
exports.ParseError = ParseError;
class Parser {
tokens;
current = 0;
sourceCode;
constructor(tokens, sourceCode) {
this.tokens = tokens;
this.sourceCode = sourceCode;
}
parse() {
const statements = [];
while (!this.isAtEnd()) {
const stmt = this.statement();
if (stmt) {
statements.push(stmt);
}
}
return new ast_1.Program(statements);
}
statement() {
try {
if (this.match(tokenizer_1.TokenType.ASYNC)) {
// async can only be followed by function for now
this.consume(tokenizer_1.TokenType.FUNCTION, "Expected 'function' after 'async'");
return this.functionDeclaration(true);
}
if (this.match(tokenizer_1.TokenType.FUNCTION)) {
return this.functionDeclaration(false);
}
if (this.match(tokenizer_1.TokenType.RETURN)) {
return this.returnStatement();
}
if (this.match(tokenizer_1.TokenType.CONST, tokenizer_1.TokenType.LET)) {
return this.variableDeclaration();
}
if (this.match(tokenizer_1.TokenType.AGENTS)) {
return this.agentsStatement();
}
if (this.match(tokenizer_1.TokenType.UNCERTAIN)) {
// Check what follows 'uncertain'
if (this.check(tokenizer_1.TokenType.IF)) {
return this.uncertainIfStatement();
}
else if (this.check(tokenizer_1.TokenType.FOR)) {
return this.uncertainForStatement();
}
else if (this.check(tokenizer_1.TokenType.WHILE)) {
return this.uncertainWhileStatement();
}
else {
throw new ParseError("Expected 'if', 'for', or 'while' after 'uncertain'", this.peek(), this.sourceCode);
}
}
if (this.match(tokenizer_1.TokenType.IF)) {
return this.ifStatement();
}
if (this.match(tokenizer_1.TokenType.FOR)) {
return this.forStatement();
}
if (this.match(tokenizer_1.TokenType.WHILE)) {
return this.whileStatement();
}
if (this.match(tokenizer_1.TokenType.DO)) {
return this.doWhileStatement();
}
if (this.match(tokenizer_1.TokenType.BREAK)) {
return this.breakStatement();
}
if (this.match(tokenizer_1.TokenType.CONTINUE)) {
return this.continueStatement();
}
if (this.match(tokenizer_1.TokenType.IN)) {
return this.contextStatement();
}
if (this.match(tokenizer_1.TokenType.IMPORT)) {
return this.importStatement();
}
if (this.match(tokenizer_1.TokenType.EXPORT)) {
return this.exportStatement();
}
// Check for potential destructuring patterns
if (this.check(tokenizer_1.TokenType.LEFT_BRACKET)) {
// Array destructuring pattern - parse as expression statement
return this.expressionStatement();
}
if (this.check(tokenizer_1.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(tokenizer_1.TokenType.RIGHT_BRACE)) {
// Empty object {}
isObjectOrDestructuring = true;
}
else if (this.check(tokenizer_1.TokenType.SPREAD)) {
// Spread in object {...x}
isObjectOrDestructuring = true;
}
else if (this.check(tokenizer_1.TokenType.IDENTIFIER)) {
// Look ahead one more token after identifier
this.advance();
if (this.check(tokenizer_1.TokenType.COMMA) || this.check(tokenizer_1.TokenType.COLON) ||
this.check(tokenizer_1.TokenType.RIGHT_BRACE)) {
// Object property syntax
isObjectOrDestructuring = true;
}
else if (this.check(tokenizer_1.TokenType.CONFIDENCE_ARROW)) {
// Confidence arrow after identifier in {} context
// This is likely a destructuring pattern
isObjectOrDestructuring = true;
}
else if (this.check(tokenizer_1.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(tokenizer_1.TokenType.COMMA) &&
!this.check(tokenizer_1.TokenType.RIGHT_BRACE)))) {
if (this.check(tokenizer_1.TokenType.LEFT_PAREN) || this.check(tokenizer_1.TokenType.LEFT_BRACKET) ||
this.check(tokenizer_1.TokenType.LEFT_BRACE)) {
depth++;
}
else if (this.check(tokenizer_1.TokenType.RIGHT_PAREN) || this.check(tokenizer_1.TokenType.RIGHT_BRACKET) ||
this.check(tokenizer_1.TokenType.RIGHT_BRACE)) {
depth--;
}
this.advance();
}
// If followed by comma or }, it's likely object destructuring with default
if (this.check(tokenizer_1.TokenType.COMMA) || this.check(tokenizer_1.TokenType.RIGHT_BRACE)) {
// Check if the closing } is followed by =
if (this.check(tokenizer_1.TokenType.RIGHT_BRACE)) {
const checkPoint3 = this.current;
this.advance(); // skip }
if (this.check(tokenizer_1.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;
}
}
agentsStatement() {
this.consume(tokenizer_1.TokenType.LEFT_BRACE, "Expected '{' after 'agents'");
const statements = [];
while (!this.check(tokenizer_1.TokenType.RIGHT_BRACE) && !this.isAtEnd()) {
const agentDecl = this.agentDeclaration();
statements.push(agentDecl);
}
this.consume(tokenizer_1.TokenType.RIGHT_BRACE, "Expected '}' after agents block");
return new ast_1.BlockStatement(statements);
}
agentDeclaration() {
const name = this.consume(tokenizer_1.TokenType.IDENTIFIER, "Expected agent name").value;
this.consume(tokenizer_1.TokenType.COLON, "Expected ':' after agent name");
this.consume(tokenizer_1.TokenType.AGENT, "Expected 'Agent' keyword");
const config = {};
if (this.match(tokenizer_1.TokenType.LEFT_BRACE)) {
while (!this.check(tokenizer_1.TokenType.RIGHT_BRACE) && !this.isAtEnd()) {
const key = this.consume(tokenizer_1.TokenType.IDENTIFIER, "Expected config key").value;
this.consume(tokenizer_1.TokenType.COLON, "Expected ':' after config key");
if (key === 'confidence') {
const value = this.consume(tokenizer_1.TokenType.NUMBER, "Expected number for confidence").value;
config.confidence = parseFloat(value);
}
else if (key === 'role') {
const value = this.consume(tokenizer_1.TokenType.STRING, "Expected string for role").value;
config.role = value;
}
if (this.check(tokenizer_1.TokenType.COMMA)) {
this.advance();
}
}
this.consume(tokenizer_1.TokenType.RIGHT_BRACE, "Expected '}' after agent config");
}
return new ast_1.AgentDeclaration(name, config);
}
uncertainIfStatement() {
this.consume(tokenizer_1.TokenType.IF, "Expected 'if' after 'uncertain'");
this.consume(tokenizer_1.TokenType.LEFT_PAREN, "Expected '(' after 'uncertain if'");
const condition = this.expression();
const threshold = 0.5; // default threshold, actual evaluation happens at runtime
this.consume(tokenizer_1.TokenType.RIGHT_PAREN, "Expected ')' after condition");
this.consume(tokenizer_1.TokenType.LEFT_BRACE, "Expected '{' after condition");
const branches = {};
while (!this.check(tokenizer_1.TokenType.RIGHT_BRACE) && !this.isAtEnd()) {
if (this.match(tokenizer_1.TokenType.HIGH)) {
this.consume(tokenizer_1.TokenType.LEFT_BRACE, "Expected '{' after 'high'");
const statements = this.blockContents();
branches.high = new ast_1.BlockStatement(statements);
this.consume(tokenizer_1.TokenType.RIGHT_BRACE, "Expected '}' after high branch");
}
else if (this.match(tokenizer_1.TokenType.MEDIUM)) {
this.consume(tokenizer_1.TokenType.LEFT_BRACE, "Expected '{' after 'medium'");
const statements = this.blockContents();
branches.medium = new ast_1.BlockStatement(statements);
this.consume(tokenizer_1.TokenType.RIGHT_BRACE, "Expected '}' after medium branch");
}
else if (this.match(tokenizer_1.TokenType.LOW)) {
this.consume(tokenizer_1.TokenType.LEFT_BRACE, "Expected '{' after 'low'");
const statements = this.blockContents();
branches.low = new ast_1.BlockStatement(statements);
this.consume(tokenizer_1.TokenType.RIGHT_BRACE, "Expected '}' after low branch");
}
else if (this.match(tokenizer_1.TokenType.DEFAULT)) {
this.consume(tokenizer_1.TokenType.LEFT_BRACE, "Expected '{' after 'default'");
const statements = this.blockContents();
branches.default = new ast_1.BlockStatement(statements);
this.consume(tokenizer_1.TokenType.RIGHT_BRACE, "Expected '}' after default branch");
}
else {
throw new ParseError("Expected 'high', 'medium', 'low', or 'default' branch", this.peek(), this.sourceCode);
}
}
this.consume(tokenizer_1.TokenType.RIGHT_BRACE, "Expected '}' after uncertain if branches");
return new ast_1.UncertainIfStatement(condition, threshold, branches);
}
ifStatement() {
this.consume(tokenizer_1.TokenType.LEFT_PAREN, "Expected '(' after 'if'");
const condition = this.expression();
this.consume(tokenizer_1.TokenType.RIGHT_PAREN, "Expected ')' after if condition");
// For if statements, always treat { as a block statement
let thenStatement;
if (this.check(tokenizer_1.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 = undefined;
if (this.match(tokenizer_1.TokenType.ELSE)) {
// For else, also handle blocks directly
if (this.check(tokenizer_1.TokenType.LEFT_BRACE)) {
this.advance(); // consume {
elseStatement = this.blockStatement();
}
else {
elseStatement = this.statement() || undefined;
}
}
return new ast_1.IfStatement(condition, thenStatement, elseStatement);
}
contextStatement() {
this.consume(tokenizer_1.TokenType.CONTEXT, "Expected 'context' after 'in'");
const contextName = this.consume(tokenizer_1.TokenType.IDENTIFIER, "Expected context name").value;
// For context statements, handle blocks directly
let body;
if (this.check(tokenizer_1.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 = undefined;
if (this.match(tokenizer_1.TokenType.SHIFTING)) {
this.consume(tokenizer_1.TokenType.TO, "Expected 'to' after 'shifting'");
shiftTo = this.consume(tokenizer_1.TokenType.IDENTIFIER, "Expected context name after 'to'").value;
// Parse the shifting target block
if (this.check(tokenizer_1.TokenType.LEFT_BRACE)) {
this.advance(); // consume {
this.blockStatement(); // consume the shifting target block
}
else {
this.statement(); // consume the shifting target statement
}
}
return new ast_1.ContextStatement(contextName, body, shiftTo);
}
blockStatement() {
const statements = this.blockContents();
this.consume(tokenizer_1.TokenType.RIGHT_BRACE, "Expected '}' after block");
return new ast_1.BlockStatement(statements);
}
blockContents() {
const statements = [];
while (!this.check(tokenizer_1.TokenType.RIGHT_BRACE) && !this.isAtEnd()) {
const stmt = this.statement();
if (stmt) {
statements.push(stmt);
}
}
return statements;
}
expressionStatement() {
// Look for destructuring pattern at the start
if (this.check(tokenizer_1.TokenType.LEFT_BRACKET) || this.check(tokenizer_1.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;
if (this.match(tokenizer_1.TokenType.CONFIDENCE_ARROW)) {
const threshold = this.expression();
if (threshold) {
confidenceThreshold = threshold;
}
}
if (this.match(tokenizer_1.TokenType.EQUAL)) {
const value = this.expression();
this.match(tokenizer_1.TokenType.SEMICOLON);
return new ast_1.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 ast_1.IdentifierExpression) {
if (this.match(tokenizer_1.TokenType.EQUAL)) {
const value = this.expression();
// Consume optional semicolon
this.match(tokenizer_1.TokenType.SEMICOLON);
return new ast_1.AssignmentStatement(expr.name, value);
}
else if (this.match(tokenizer_1.TokenType.PLUS_EQUAL)) {
// x += y becomes x = x + y
const right = this.expression();
const value = new ast_1.BinaryExpression('+', expr, right);
this.match(tokenizer_1.TokenType.SEMICOLON);
return new ast_1.AssignmentStatement(expr.name, value);
}
else if (this.match(tokenizer_1.TokenType.MINUS_EQUAL)) {
// x -= y becomes x = x - y
const right = this.expression();
const value = new ast_1.BinaryExpression('-', expr, right);
this.match(tokenizer_1.TokenType.SEMICOLON);
return new ast_1.AssignmentStatement(expr.name, value);
}
else if (this.match(tokenizer_1.TokenType.STAR_EQUAL)) {
// x *= y becomes x = x * y
const right = this.expression();
const value = new ast_1.BinaryExpression('*', expr, right);
this.match(tokenizer_1.TokenType.SEMICOLON);
return new ast_1.AssignmentStatement(expr.name, value);
}
else if (this.match(tokenizer_1.TokenType.SLASH_EQUAL)) {
// x /= y becomes x = x / y
const right = this.expression();
const value = new ast_1.BinaryExpression('/', expr, right);
this.match(tokenizer_1.TokenType.SEMICOLON);
return new ast_1.AssignmentStatement(expr.name, value);
}
else if (this.match(tokenizer_1.TokenType.PERCENT_EQUAL)) {
// x %= y becomes x = x % y
const right = this.expression();
const value = new ast_1.BinaryExpression('%', expr, right);
this.match(tokenizer_1.TokenType.SEMICOLON);
return new ast_1.AssignmentStatement(expr.name, value);
}
else if (this.match(tokenizer_1.TokenType.CONFIDENCE_PLUS_EQUAL)) {
// x ~+= y becomes x = x ~+ y
const right = this.expression();
const value = new ast_1.BinaryExpression('~+', expr, right);
this.match(tokenizer_1.TokenType.SEMICOLON);
return new ast_1.AssignmentStatement(expr.name, value);
}
else if (this.match(tokenizer_1.TokenType.CONFIDENCE_MINUS_EQUAL)) {
// x ~-= y becomes x = x ~- y
const right = this.expression();
const value = new ast_1.BinaryExpression('~-', expr, right);
this.match(tokenizer_1.TokenType.SEMICOLON);
return new ast_1.AssignmentStatement(expr.name, value);
}
else if (this.match(tokenizer_1.TokenType.CONFIDENCE_STAR_EQUAL)) {
// x ~*= y becomes x = x ~* y
const right = this.expression();
const value = new ast_1.BinaryExpression('~*', expr, right);
this.match(tokenizer_1.TokenType.SEMICOLON);
return new ast_1.AssignmentStatement(expr.name, value);
}
else if (this.match(tokenizer_1.TokenType.CONFIDENCE_SLASH_EQUAL)) {
// x ~/= y becomes x = x ~/ y
const right = this.expression();
const value = new ast_1.BinaryExpression('~/', expr, right);
this.match(tokenizer_1.TokenType.SEMICOLON);
return new ast_1.AssignmentStatement(expr.name, value);
}
}
// Consume optional semicolon
this.match(tokenizer_1.TokenType.SEMICOLON);
// Wrap expression in an ExpressionStatement
return new ast_1.ExpressionStatement(expr);
}
forStatement() {
// 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(tokenizer_1.TokenType.IDENTIFIER)) {
this.advance();
if (this.check(tokenizer_1.TokenType.COMMA)) {
// for item, index in array
this.advance(); // consume comma
if (this.check(tokenizer_1.TokenType.IDENTIFIER)) {
this.advance();
if (this.check(tokenizer_1.TokenType.IN)) {
// Reset and parse as for-in with index
this.current = checkPoint;
return this.forInStatement();
}
}
}
else if (this.check(tokenizer_1.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 = null;
let condition = null;
let update = null;
// Parse init
if (!this.check(tokenizer_1.TokenType.SEMICOLON)) {
if (this.check(tokenizer_1.TokenType.IDENTIFIER) && this.peekNext().type === tokenizer_1.TokenType.EQUAL) {
// Variable assignment
const identifier = this.advance().value;
this.consume(tokenizer_1.TokenType.EQUAL, "Expected '=' in assignment");
const value = this.expression();
if (!value) {
throw new ParseError("Expected expression after '='", this.previous(), this.sourceCode);
}
init = new ast_1.AssignmentStatement(identifier, value);
}
else {
// Expression
const expr = this.expression();
if (expr) {
init = new ast_1.ExpressionStatement(expr);
}
}
}
if (!this.match(tokenizer_1.TokenType.SEMICOLON)) {
throw new ParseError("Expected ';' after for loop initializer", this.peek(), this.sourceCode);
}
// Parse condition
if (!this.check(tokenizer_1.TokenType.SEMICOLON)) {
condition = this.expression();
}
if (!this.match(tokenizer_1.TokenType.SEMICOLON)) {
throw new ParseError("Expected ';' after for loop condition", this.peek(), this.sourceCode);
}
// Parse update
if (!this.check(tokenizer_1.TokenType.LEFT_BRACE)) {
// Check if this is an assignment
if (this.check(tokenizer_1.TokenType.IDENTIFIER)) {
const checkPoint = this.current;
const identifier = this.advance();
if (this.match(tokenizer_1.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 ast_1.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 ast_1.ForLoop(init, condition, update, body);
}
forInStatement() {
// Parse variable name
if (!this.check(tokenizer_1.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 = null;
if (this.match(tokenizer_1.TokenType.COMMA)) {
if (!this.check(tokenizer_1.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(tokenizer_1.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 ast_1.ForInLoop(variable, index, iterable, body);
}
whileStatement() {
// 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 ast_1.WhileLoop(condition, body);
}
doWhileStatement() {
// 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(tokenizer_1.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 ast_1.DoWhileLoop(body, condition);
}
breakStatement() {
return new ast_1.BreakStatement();
}
continueStatement() {
return new ast_1.ContinueStatement();
}
uncertainForStatement() {
this.consume(tokenizer_1.TokenType.FOR, "Expected 'for' after 'uncertain'");
// Parse init, condition, update like regular for loop
let init = null;
let condition = null;
let update = null;
// Parse init
if (!this.check(tokenizer_1.TokenType.SEMICOLON)) {
if (this.check(tokenizer_1.TokenType.IDENTIFIER) && this.peekNext().type === tokenizer_1.TokenType.EQUAL) {
const identifier = this.advance().value;
this.consume(tokenizer_1.TokenType.EQUAL, "Expected '=' in assignment");
const value = this.expression();
if (!value) {
throw new ParseError("Expected expression after '='", this.previous(), this.sourceCode);
}
init = new ast_1.AssignmentStatement(identifier, value);
}
else {
const expr = this.expression();
if (expr) {
init = new ast_1.ExpressionStatement(expr);
}
}
}
if (!this.match(tokenizer_1.TokenType.SEMICOLON)) {
throw new ParseError("Expected ';' after for loop initializer", this.peek(), this.sourceCode);
}
// Parse condition
if (!this.check(tokenizer_1.TokenType.SEMICOLON)) {
condition = this.expression();
}
if (!this.match(tokenizer_1.TokenType.SEMICOLON)) {
throw new ParseError("Expected ';' after for loop condition", this.peek(), this.sourceCode);
}
// Parse update
if (!this.check(tokenizer_1.TokenType.LEFT_BRACE)) {
if (this.check(tokenizer_1.TokenType.IDENTIFIER)) {
const checkPoint = this.current;
const identifier = this.advance();
if (this.match(tokenizer_1.TokenType.EQUAL)) {
const value = this.expression();
if (!value) {
throw new ParseError("Expected expression after '='", this.previous(), this.sourceCode);
}
update = new ast_1.AssignmentExpression(identifier.value, value);
}
else {
this.current = checkPoint;
update = this.expression();
}
}
else {
update = this.expression();
}
}
// Parse uncertain branches
this.consume(tokenizer_1.TokenType.LEFT_BRACE, "Expected '{' after uncertain for loop header");
const branches = this.parseUncertainBranches();
this.consume(tokenizer_1.TokenType.RIGHT_BRACE, "Expected '}' after uncertain for branches");
return new ast_1.UncertainForLoop(init, condition, update, branches);
}
uncertainWhileStatement() {
this.consume(tokenizer_1.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(tokenizer_1.TokenType.LEFT_BRACE, "Expected '{' after uncertain while condition");
const branches = this.parseUncertainBranches();
this.consume(tokenizer_1.TokenType.RIGHT_BRACE, "Expected '}' after uncertain while branches");
return new ast_1.UncertainWhileLoop(condition, branches);
}
parseUncertainBranches() {
const branches = {};
while (!this.check(tokenizer_1.TokenType.RIGHT_BRACE) && !this.isAtEnd()) {
if (this.match(tokenizer_1.TokenType.HIGH)) {
if (branches.high) {
throw new ParseError("Duplicate 'high' branch in uncertain statement", this.previous(), this.sourceCode);
}
this.consume(tokenizer_1.TokenType.LEFT_BRACE, "Expected '{' after 'high'");
const statements = this.blockContents();
branches.high = new ast_1.BlockStatement(statements);
this.consume(tokenizer_1.TokenType.RIGHT_BRACE, "Expected '}' after high branch");
}
else if (this.match(tokenizer_1.TokenType.MEDIUM)) {
if (branches.medium) {
throw new ParseError("Duplicate 'medium' branch in uncertain statement", this.previous(), this.sourceCode);
}
this.consume(tokenizer_1.TokenType.LEFT_BRACE, "Expected '{' after 'medium'");
const statements = this.blockContents();
branches.medium = new ast_1.BlockStatement(statements);
this.consume(tokenizer_1.TokenType.RIGHT_BRACE, "Expected '}' after medium branch");
}
else if (this.match(tokenizer_1.TokenType.LOW)) {
if (branches.low) {
throw new ParseError("Duplicate 'low' branch in uncertain statement", this.previous(), this.sourceCode);
}
this.consume(tokenizer_1.TokenType.LEFT_BRACE, "Expected '{' after 'low'");
const statements = this.blockContents();
branches.low = new ast_1.BlockStatement(statements);
this.consume(tokenizer_1.TokenType.RIGHT_BRACE, "Expected '}' after low branch");
}
else if (this.match(tokenizer_1.TokenType.DEFAULT)) {
if (branches.default) {
throw new ParseError("Duplicate 'default' branch in uncertain statement", this.previous(), this.sourceCode);
}
this.consume(tokenizer_1.TokenType.LEFT_BRACE, "Expected '{' after 'default'");
const statements = this.blockContents();
branches.default = new ast_1.BlockStatement(statements);
this.consume(tokenizer_1.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;
}
importStatement() {
const specifiers = [];
let defaultImport;
let namespaceImport;
let source;
// Check for namespace import: import * as name from "module"
if (this.match(tokenizer_1.TokenType.STAR)) {
this.consume(tokenizer_1.TokenType.AS, "Expected 'as' after '*'");
namespaceImport = this.consume(tokenizer_1.TokenType.IDENTIFIER, "Expected namespace name after 'as'").value;
}
// Check for named imports: import {name1, name2} from "module"
else if (this.check(tokenizer_1.TokenType.LEFT_BRACE)) {
this.advance(); // consume {
while (!this.check(tokenizer_1.TokenType.RIGHT_BRACE) && !this.isAtEnd()) {
const imported = this.consume(tokenizer_1.TokenType.IDENTIFIER, "Expected import name").value;
let local = imported; // Default to same name
// Check for renaming: import {original as renamed}
if (this.match(tokenizer_1.TokenType.AS)) {
local = this.consume(tokenizer_1.TokenType.IDENTIFIER, "Expected local name after 'as'").value;
}
specifiers.push(new ast_1.ImportSpecifier(imported, local !== imported ? local : undefined));
if (this.match(tokenizer_1.TokenType.COMMA)) {
// Allow trailing comma
if (this.check(tokenizer_1.TokenType.RIGHT_BRACE)) {
break;
}
}
else if (!this.check(tokenizer_1.TokenType.RIGHT_BRACE)) {
throw new ParseError("Expected ',' or '}' after import specifier", this.peek(), this.sourceCode);
}
}
this.consume(tokenizer_1.TokenType.RIGHT_BRACE, "Expected '}' after import specifiers");
}
// Check for default import: import defaultName from "module"
else if (this.check(tokenizer_1.TokenType.IDENTIFIER)) {
defaultImport = this.advance().value;
// Check for mixed import: import defaultName, {named} from "module"
if (this.match(tokenizer_1.TokenType.COMMA)) {
if (this.match(tokenizer_1.TokenType.LEFT_BRACE)) {
while (!this.check(tokenizer_1.TokenType.RIGHT_BRACE) && !this.isAtEnd()) {
const imported = this.consume(tokenizer_1.TokenType.IDENTIFIER, "Expected import name").value;
let local = imported;
if (this.match(tokenizer_1.TokenType.AS)) {
local = this.consume(tokenizer_1.TokenType.IDENTIFIER, "Expected local name after 'as'").value;
}
specifiers.push(new ast_1.ImportSpecifier(imported, local !== imported ? local : undefined));
if (this.match(tokenizer_1.TokenType.COMMA)) {
if (this.check(tokenizer_1.TokenType.RIGHT_BRACE)) {
break;
}
}
else if (!this.check(tokenizer_1.TokenType.RIGHT_BRACE)) {
throw new ParseError("Expected ',' or '}' after import specifier", this.peek(), this.sourceCode);
}
}
this.consume(tokenizer_1.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(tokenizer_1.TokenType.FROM, "Expected 'from' after import specifiers");
// Parse module source
source = this.consume(tokenizer_1.TokenType.STRING, "Expected module path after 'from'").value;
this.match(tokenizer_1.TokenType.SEMICOLON); // Optional semicolon
return new ast_1.ImportStatement(specifiers, source, defaultImport, namespaceImport);
}
exportStatement() {
// Check for different export patterns
// export default expression
if (this.match(tokenizer_1.TokenType.DEFAULT)) {
const declaration = this.expressionStatement();
return new ast_1.ExportStatement(undefined, undefined, declaration, true);
}
// export * from "module"
if (this.match(tokenizer_1.TokenType.STAR)) {
this.consume(tokenizer_1.TokenType.FROM, "Expected 'from' after 'export *'");
const source = this.consume(tokenizer_1.TokenType.STRING, "Expected module path after 'from'").value;
this.match(tokenizer_1.TokenType.SEMICOLON); // Optional semicolon
return new ast_1.ExportStatement(undefined, source, undefined, false, true);
}
// export {name1, name2} or export {name1, name2} from "module"
if (this.match(tokenizer_1.TokenType.LEFT_BRACE)) {
const specifiers = [];
while (!this.check(tokenizer_1.TokenType.RIGHT_BRACE) && !this.isAtEnd()) {
const local = this.consume(tokenizer_1.TokenType.IDENTIFIER, "Expected export name").value;
let exported = local; // Default to same name
// Check for renaming: export {original as renamed}
if (this.match(tokenizer_1.TokenType.AS)) {
exported = this.consume(tokenizer_1.TokenType.IDENTIFIER, "Expected exported name after 'as'").value;
}
specifiers.push(new ast_1.ExportSpecifier(local, exported !== local ? exported : undefined));
if (this.match(tokenizer_1.TokenType.COMMA)) {
// Allow trailing comma
if (this.check(tokenizer_1.TokenType.RIGHT_BRACE)) {
break;
}
}
else if (!this.check(tokenizer_1.TokenType.RIGHT_BRACE)) {
throw new ParseError("Expected ',' or '}' after export specifier", this.peek(), this.sourceCode);
}
}
this.consume(tokenizer_1.TokenType.RIGHT_BRACE, "Expected '}' after export specifiers");
// Check for re-export: export {names} from "module"
let source;
if (this.match(tokenizer_1.TokenType.FROM)) {
source = this.consume(tokenizer_1.TokenType.STRING, "Expected module path after 'from'").value;
}
this.match(tokenizer_1.TokenType.SEMICOLON); // Optional semicolon
return new ast_1.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 ast_1.ExportStatement(undefined, undefined, declaration);
}
functionDeclaration(isAsync = false) {
// Parse function name
const name = this.consume(tokenizer_1.TokenType.IDENTIFIER, "Expected function name").value;
// Parse parameters
this.consume(tokenizer_1.TokenType.LEFT_PAREN, "Expected '(' after function name");
const parameters = [];
let restParameter = undefined;
if (!this.check(tokenizer_1.TokenType.RIGHT_PAREN)) {
do {
if (this.match(tokenizer_1.TokenType.SPREAD)) {
// Rest parameter
if (this.check(tokenizer_1.TokenType.IDENTIFIER)) {
restParameter = this.advance().value;
}
else if (this.check(tokenizer_1.TokenType.LEFT_BRACKET) || this.check(tokenizer_1.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(tokenizer_1.TokenType.COMMA)) {
throw new ParseError("Rest parameter must be last formal parameter", this.previous(), this.sourceCode);
}
}
else if (this.match(tokenizer_1.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(tokenizer_1.TokenType.LEFT_BRACKET) || this.check(tokenizer_1.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(tokenizer_1.TokenType.COMMA));
}
this.consume(tokenizer_1.TokenType.RIGHT_PAREN, "Expected ')' after parameters");
// Parse optional confidence annotation
let confidenceAnnotation;
if (this.match(tokenizer_1.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(tokenizer_1.TokenType.LEFT_BRACE, "Expected '{' before function body");
const body = this.blockStatement();
return new ast_1.FunctionDeclaration(name, parameters, body, restParameter, confidenceAnnotation, isAsync);
}
returnStatement() {
let value;
// Check if there's a return value (not followed by semicolon or EOF)
if (!this.check(tokenizer_1.TokenType.SEMICOLON) && !this.isAtEnd() &&
!this.check(tokenizer_1.TokenType.RIGHT_BRACE)) {
value = this.expression() || undefined;
}
this.match(tokenizer_1.TokenType.SEMICOLON); // Optional semicolon
return new ast_1.ReturnStatement(value);
}
variableDeclaration() {
// Get the declaration kind (const or let)
const kind = this.previous().value;
// Check for destructuring pattern
if (this.check(tokenizer_1.TokenType.LEFT_BRACKET) || this.check(tokenizer_1.TokenType.LEFT_BRACE)) {
const pattern = this.tryParseDestructuringPattern();
if (pattern) {
// Pattern destructuring: const [a, b] = array or const {x, y} = obj
this.consume(tokenizer_1.TokenType.EQUAL, "Expected '=' after destructuring pattern");
const initializer = this.expression();
if (!initializer) {
throw new ParseError("Expected expression after '='", this.peek(), this.sourceCode);
}
this.match(tokenizer_1.TokenType.SEMICOLON); // Optional semicolon
return new ast_1.VariableDeclaration(kind, '', initializer, pattern);
}
}
// Regular variable declaration: const/let name = value
const identifier = this.consume(tokenizer_1.TokenType.IDENTIFIER, "Expected variable name").value;
// const requires an initializer, let allows optional initializer
let initializer;
if (this.match(tokenizer_1.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(tokenizer_1.TokenType.SEMICOLON); // Optional semicolon
return new ast_1.VariableDeclaration(kind, identifier, initializer);
}
expression() {
return this.pipeline();
}
pipeline() {
let expr = this.ternary();
while (this.match(tokenizer_1.TokenType.PIPELINE, tokenizer_1.TokenType.CONFIDENCE_PIPELINE, tokenizer_1.TokenType.CONFIDENCE_THRESHOLD_GATE)) {
const operator = this.previous();
if (operator.type === tokenizer_1.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 ast_1.BinaryExpression('~?>', expr, threshold);
}
else {
// Handle regular and confidence pipeline operators
const isConfidencePipeline = operator.type === tokenizer_1.TokenType.CONFIDENCE_PIPELINE;
const right = this.ternary();
if (!right) {
throw new ParseError("Expected expression after pipeline op