UNPKG

@ordojs/core

Version:

Core compiler and runtime for OrdoJS framework

946 lines 35.4 kB
/** * @fileoverview OrdoJS Parser - Refactored modular implementation * @author OrdoJS Framework Team */ import { ExpressionType, LifecycleType, StatementType, SyntaxError, TokenType } from '../types/index.js'; /** * Expression precedence levels for operator precedence parsing */ var Precedence; (function (Precedence) { Precedence[Precedence["NONE"] = 0] = "NONE"; Precedence[Precedence["ASSIGNMENT"] = 1] = "ASSIGNMENT"; Precedence[Precedence["OR"] = 2] = "OR"; Precedence[Precedence["AND"] = 3] = "AND"; Precedence[Precedence["EQUALITY"] = 4] = "EQUALITY"; Precedence[Precedence["COMPARISON"] = 5] = "COMPARISON"; Precedence[Precedence["TERM"] = 6] = "TERM"; Precedence[Precedence["FACTOR"] = 7] = "FACTOR"; Precedence[Precedence["UNARY"] = 8] = "UNARY"; Precedence[Precedence["CALL"] = 9] = "CALL"; Precedence[Precedence["PRIMARY"] = 10] = "PRIMARY"; })(Precedence || (Precedence = {})); /** * Enhanced OrdoJS Parser with modular architecture */ export class OrdoJSParser { state; options; parseRules; constructor(tokenStream, options = {}, filename = 'unknown') { this.options = { allowRecovery: false, maxErrors: 10, strictMode: true, generateSourceMaps: true, nodeProcessors: [], experimentalFeatures: [], ...options }; this.state = { tokenStream, current: 0, filename, errors: [], warnings: [], context: { blockContext: 'component', symbolTable: new Map(), scopeDepth: 0, options: this.options } }; this.parseRules = this.initializeParseRules(); } /** * Parse token stream into Component AST */ parse() { try { const component = this.parseComponent(); if (this.state.errors.length > 0 && !this.options.allowRecovery) { throw this.state.errors[0]; } // Process AST through registered processors this.processAST(component); return { component, dependencies: this.extractDependencies(component), exports: this.extractExports(component), sourceMap: this.options.generateSourceMaps ? this.generateSourceMap() : { version: 3, sources: [this.state.filename], names: [], mappings: '', sourcesContent: [] } }; } catch (error) { if (error instanceof SyntaxError) { throw error; } throw new SyntaxError(`Parser error: ${error instanceof Error ? error.message : 'Unknown error'}`, this.getCurrentPosition(), ['valid component syntax'], this.peek().value, this.state.filename); } } /** * Get all errors encountered during parsing */ getErrors() { return [...this.state.errors]; } /** * Get all warnings encountered during parsing */ getWarnings() { return [...this.state.warnings]; } /** * Add custom AST node processor */ addNodeProcessor(processor) { this.options.nodeProcessors.push(processor); } /** * Remove AST node processor by name */ removeNodeProcessor(name) { const index = this.options.nodeProcessors.findIndex(p => p.name === name); if (index >= 0) { this.options.nodeProcessors.splice(index, 1); return true; } return false; } initializeParseRules() { const rules = new Map(); // Literals rules.set(TokenType.BOOLEAN, { prefix: this.parseLiteral.bind(this), precedence: Precedence.NONE }); rules.set(TokenType.NUMBER, { prefix: this.parseLiteral.bind(this), precedence: Precedence.NONE }); rules.set(TokenType.STRING, { prefix: this.parseLiteral.bind(this), precedence: Precedence.NONE }); rules.set(TokenType.IDENTIFIER, { prefix: this.parseIdentifier.bind(this), precedence: Precedence.NONE }); // Grouping rules.set(TokenType.LEFT_PAREN, { prefix: this.parseGrouping.bind(this), precedence: Precedence.NONE }); // Unary operators rules.set(TokenType.LOGICAL_NOT, { prefix: this.parseUnary.bind(this), precedence: Precedence.NONE }); rules.set(TokenType.MINUS, { prefix: this.parseUnary.bind(this), infix: this.parseBinary.bind(this), precedence: Precedence.TERM }); // Binary operators rules.set(TokenType.PLUS, { infix: this.parseBinary.bind(this), precedence: Precedence.TERM }); rules.set(TokenType.MULTIPLY, { infix: this.parseBinary.bind(this), precedence: Precedence.FACTOR }); rules.set(TokenType.DIVIDE, { infix: this.parseBinary.bind(this), precedence: Precedence.FACTOR }); rules.set(TokenType.MODULO, { infix: this.parseBinary.bind(this), precedence: Precedence.FACTOR }); // Comparison operators rules.set(TokenType.EQUALS, { infix: this.parseBinary.bind(this), precedence: Precedence.EQUALITY }); rules.set(TokenType.NOT_EQUALS, { infix: this.parseBinary.bind(this), precedence: Precedence.EQUALITY }); rules.set(TokenType.LESS_THAN, { infix: this.parseBinary.bind(this), precedence: Precedence.COMPARISON }); rules.set(TokenType.GREATER_THAN, { infix: this.parseBinary.bind(this), precedence: Precedence.COMPARISON }); rules.set(TokenType.LESS_EQUAL, { infix: this.parseBinary.bind(this), precedence: Precedence.COMPARISON }); rules.set(TokenType.GREATER_EQUAL, { infix: this.parseBinary.bind(this), precedence: Precedence.COMPARISON }); // Logical operators rules.set(TokenType.LOGICAL_AND, { infix: this.parseBinary.bind(this), precedence: Precedence.AND }); rules.set(TokenType.LOGICAL_OR, { infix: this.parseBinary.bind(this), precedence: Precedence.OR }); // Assignment rules.set(TokenType.ASSIGN, { infix: this.parseAssignment.bind(this), precedence: Precedence.ASSIGNMENT }); // Call and member access rules.set(TokenType.LEFT_PAREN, { infix: this.parseCall.bind(this), precedence: Precedence.CALL }); rules.set(TokenType.DOT, { infix: this.parseMember.bind(this), precedence: Precedence.CALL }); return rules; } parseComponent() { const start = this.getCurrentPosition(); this.state.context.blockContext = 'component'; // Expect 'component' keyword if (!this.match(TokenType.COMPONENT)) { this.throwError("Expected 'component' keyword", ['component']); } // Parse component name const nameToken = this.consume(TokenType.IDENTIFIER, 'Expected component name'); const name = nameToken.value; // Validate component name if (!this.isValidComponentName(name)) { this.throwError('Component name must start with uppercase letter and contain only alphanumeric characters', []); } // Add component to symbol table this.addSymbol(name, 'function', undefined, this.getCurrentPosition()); // Parse optional props const props = this.parseProps(); // Expect component body this.consume(TokenType.LEFT_BRACE, "Expected '{' after component declaration"); this.enterScope(); // Parse component blocks let clientBlock; let serverBlock; let markupBlock; while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) { if (this.match(TokenType.CLIENT)) { if (clientBlock) { this.throwError('Duplicate client block', []); } clientBlock = this.parseClientBlock(); } else if (this.match(TokenType.SERVER)) { if (serverBlock) { this.throwError('Duplicate server block', []); } serverBlock = this.parseServerBlock(); } else if (this.match(TokenType.MARKUP)) { if (markupBlock) { this.throwError('Duplicate markup block', []); } markupBlock = this.parseMarkupBlock(); } else { this.throwError('Expected client, server, or markup block', ['client', 'server', 'markup']); } } this.exitScope(); this.consume(TokenType.RIGHT_BRACE, "Expected '}' after component body"); // Markup block is required if (!markupBlock) { this.throwError('Component must have a markup block', ['markup']); } const end = this.getCurrentPosition(); const component = { type: 'Component', name, props, clientBlock, serverBlock, markupBlock: markupBlock, range: { start, end } }; this.state.context.component = component; return component; } parseProps() { const props = []; if (!this.check(TokenType.LEFT_PAREN)) { return props; } this.advance(); // consume '(' while (!this.check(TokenType.RIGHT_PAREN) && !this.isAtEnd()) { const prop = this.parseProp(); props.push(prop); // Add prop to symbol table this.addSymbol(prop.name, 'prop', prop.dataType, prop.range.start); if (!this.match(TokenType.COMMA)) { break; } } this.consume(TokenType.RIGHT_PAREN, "Expected ')' after props"); return props; } parseProp() { const start = this.getCurrentPosition(); const nameToken = this.consume(TokenType.IDENTIFIER, 'Expected prop name'); this.consume(TokenType.COLON, "Expected ':' after prop name"); const dataType = this.parseTypeAnnotation(); let defaultValue; let isRequired = true; if (this.match(TokenType.ASSIGN)) { isRequired = false; defaultValue = this.parseExpression(); } const end = this.getCurrentPosition(); return { type: 'PropDefinition', name: nameToken.value, dataType, defaultValue, isRequired, range: { start, end } }; } parseTypeAnnotation() { const nameToken = this.consume(TokenType.IDENTIFIER, 'Expected type name'); let isArray = false; let isOptional = false; const genericTypes = []; // Handle array types if (this.match(TokenType.LEFT_BRACKET)) { this.consume(TokenType.RIGHT_BRACKET, "Expected ']' after '['"); isArray = true; } // Handle optional types if (this.match(TokenType.QUESTION)) { isOptional = true; } // Handle generic types (simplified) if (this.match(TokenType.LESS_THAN)) { do { genericTypes.push(this.parseTypeAnnotation()); } while (this.match(TokenType.COMMA)); this.consume(TokenType.GREATER_THAN, "Expected '>' after generic types"); } return { name: nameToken.value, isArray, isOptional, genericTypes }; } parseClientBlock() { const start = this.getCurrentPosition(); this.state.context.blockContext = 'client'; this.consume(TokenType.LEFT_BRACE, "Expected '{' after 'client'"); this.enterScope(); const reactiveVariables = []; const computedValues = []; const eventHandlers = []; const functions = []; const lifecycle = []; while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) { if (this.check(TokenType.LET) || this.check(TokenType.CONST)) { reactiveVariables.push(this.parseReactiveVariable()); } else if (this.checkLifecycleHook()) { lifecycle.push(this.parseLifecycleHook()); } else if (this.check(TokenType.IDENTIFIER)) { functions.push(this.parseFunction()); } else { this.throwError('Expected variable declaration, function, or lifecycle hook', ['let', 'const', 'function', 'onMount', 'onUnmount', 'onUpdate']); } } this.exitScope(); this.consume(TokenType.RIGHT_BRACE, "Expected '}' after client block"); const end = this.getCurrentPosition(); return { type: 'ClientBlock', reactiveVariables, computedValues, eventHandlers, functions, lifecycle, range: { start, end } }; } parseServerBlock() { const start = this.getCurrentPosition(); this.state.context.blockContext = 'server'; this.consume(TokenType.LEFT_BRACE, "Expected '{' after 'server'"); this.enterScope(); const functions = []; const middleware = []; const dataFetchers = []; const imports = []; while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) { if (this.check(TokenType.PUBLIC) || this.check(TokenType.IDENTIFIER)) { functions.push(this.parseServerFunction()); } else { this.throwError('Expected function declaration', ['public', 'function']); } } this.exitScope(); this.consume(TokenType.RIGHT_BRACE, "Expected '}' after server block"); const end = this.getCurrentPosition(); return { type: 'ServerBlock', functions, middleware, dataFetchers, imports, range: { start, end } }; } parseMarkupBlock() { const start = this.getCurrentPosition(); this.state.context.blockContext = 'markup'; this.consume(TokenType.LEFT_BRACE, "Expected '{' after 'markup'"); const elements = []; const textNodes = []; const interpolations = []; while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) { if (this.check(TokenType.HTML_TAG_OPEN)) { elements.push(this.parseHTMLElement()); } else if (this.check(TokenType.INTERPOLATION_START)) { interpolations.push(this.parseInterpolation()); } else if (this.check(TokenType.HTML_TEXT)) { textNodes.push(this.parseTextNode()); } else { // Skip unknown tokens in markup context this.advance(); } } this.consume(TokenType.RIGHT_BRACE, "Expected '}' after markup block"); const end = this.getCurrentPosition(); return { type: 'MarkupBlock', elements, textNodes, interpolations, range: { start, end } }; } parseReactiveVariable() { const start = this.getCurrentPosition(); const isConst = this.match(TokenType.CONST); if (!isConst) { this.consume(TokenType.LET, "Expected 'let' or 'const'"); } const nameToken = this.consume(TokenType.IDENTIFIER, 'Expected variable name'); // Check for duplicate declaration if (this.hasSymbolInCurrentScope(nameToken.value)) { this.throwError(`Variable '${nameToken.value}' is already declared in this scope`, []); } let dataType = { name: 'any', isArray: false, isOptional: false, genericTypes: [] }; if (this.match(TokenType.COLON)) { dataType = this.parseTypeAnnotation(); } this.consume(TokenType.ASSIGN, "Expected '=' after variable declaration"); const initialValue = this.parseExpression(); this.match(TokenType.SEMICOLON); // Optional semicolon // Add variable to symbol table this.addSymbol(nameToken.value, 'variable', dataType, start, isConst); const end = this.getCurrentPosition(); return { type: 'ReactiveVariable', name: nameToken.value, initialValue, dataType, isConst, range: { start, end } }; } parseFunction() { const start = this.getCurrentPosition(); const nameToken = this.consume(TokenType.IDENTIFIER, 'Expected function name'); // Check for duplicate declaration if (this.hasSymbolInCurrentScope(nameToken.value)) { this.throwError(`Function '${nameToken.value}' is already declared in this scope`, []); } this.consume(TokenType.LEFT_PAREN, "Expected '(' after function name"); this.enterScope(); const parameters = this.parseParameters(); this.consume(TokenType.RIGHT_PAREN, "Expected ')' after parameters"); this.consume(TokenType.COLON, "Expected ':' after parameters"); const returnType = this.parseTypeAnnotation(); this.consume(TokenType.LEFT_BRACE, "Expected '{' after function signature"); const body = this.parseStatements(); this.consume(TokenType.RIGHT_BRACE, "Expected '}' after function body"); this.exitScope(); // Add function to symbol table this.addSymbol(nameToken.value, 'function', returnType, start); const end = this.getCurrentPosition(); return { type: 'Function', name: nameToken.value, parameters, body, returnType, isAsync: false, range: { start, end } }; } parseServerFunction() { const start = this.getCurrentPosition(); const isPublic = this.match(TokenType.PUBLIC); const nameToken = this.consume(TokenType.IDENTIFIER, 'Expected function name'); // Check for duplicate declaration if (this.hasSymbolInCurrentScope(nameToken.value)) { this.throwError(`Function '${nameToken.value}' is already declared in this scope`, []); } this.consume(TokenType.LEFT_PAREN, "Expected '(' after function name"); this.enterScope(); const parameters = this.parseParameters(); this.consume(TokenType.RIGHT_PAREN, "Expected ')' after parameters"); this.consume(TokenType.COLON, "Expected ':' after parameters"); const returnType = this.parseTypeAnnotation(); this.consume(TokenType.LEFT_BRACE, "Expected '{' after function signature"); const body = this.parseStatements(); this.consume(TokenType.RIGHT_BRACE, "Expected '}' after function body"); this.exitScope(); // Add function to symbol table this.addSymbol(nameToken.value, 'function', returnType, start, false, isPublic); const end = this.getCurrentPosition(); return { type: 'ServerFunction', name: nameToken.value, parameters, body, returnType, isAsync: false, isPublic, middleware: [], permissions: [], range: { start, end } }; } parseLifecycleHook() { const start = this.getCurrentPosition(); const hookName = this.advance().value; let hookType; switch (hookName) { case 'onMount': hookType = LifecycleType.ON_MOUNT; break; case 'onUnmount': hookType = LifecycleType.ON_UNMOUNT; break; case 'onUpdate': hookType = LifecycleType.ON_UPDATE; break; case 'beforeUpdate': hookType = LifecycleType.BEFORE_UPDATE; break; case 'afterUpdate': hookType = LifecycleType.AFTER_UPDATE; break; default: this.throwError(`Unknown lifecycle hook: ${hookName}`, ['onMount', 'onUnmount', 'onUpdate', 'beforeUpdate', 'afterUpdate']); hookType = LifecycleType.ON_MOUNT; } this.consume(TokenType.LEFT_PAREN, "Expected '(' after lifecycle hook"); this.consume(TokenType.RIGHT_PAREN, "Expected ')' after lifecycle hook"); this.consume(TokenType.LEFT_BRACE, "Expected '{' after lifecycle hook"); this.enterScope(); const handler = this.parseStatements(); this.exitScope(); this.consume(TokenType.RIGHT_BRACE, "Expected '}' after lifecycle hook body"); const end = this.getCurrentPosition(); return { type: 'LifecycleHook', hookType, handler, range: { start, end } }; } parseParameters() { const parameters = []; while (!this.check(TokenType.RIGHT_PAREN) && !this.isAtEnd()) { const param = this.parseParameter(); parameters.push(param); // Add parameter to symbol table this.addSymbol(param.name, 'parameter', param.dataType, param.range.start); if (!this.match(TokenType.COMMA)) { break; } } return parameters; } parseParameter() { const start = this.getCurrentPosition(); const nameToken = this.consume(TokenType.IDENTIFIER, 'Expected parameter name'); this.consume(TokenType.COLON, "Expected ':' after parameter name"); const dataType = this.parseTypeAnnotation(); let defaultValue; let isOptional = dataType.isOptional; if (this.match(TokenType.ASSIGN)) { isOptional = true; defaultValue = this.parseExpression(); } const end = this.getCurrentPosition(); return { type: 'Parameter', name: nameToken.value, dataType, defaultValue, isOptional, range: { start, end } }; } parseStatements() { const statements = []; while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) { statements.push(this.parseStatement()); } return statements; } parseStatement() { const start = this.getCurrentPosition(); // For now, treat everything as expression statements const expression = this.parseExpression(); this.match(TokenType.SEMICOLON); // Optional semicolon const end = this.getCurrentPosition(); return { type: 'Statement', statementType: StatementType.EXPRESSION, expression, range: { start, end } }; } parseExpression() { return this.parsePrecedence(Precedence.ASSIGNMENT); } parsePrecedence(precedence) { const prefixRule = this.parseRules.get(this.peek().type)?.prefix; if (!prefixRule) { this.throwError('Expected expression', ['identifier', 'number', 'string', 'boolean', '(']); // Return dummy expression for error recovery return this.createDummyExpression(); } let left = prefixRule(this); while (precedence <= this.getPrecedence()) { const infixRule = this.parseRules.get(this.peek().type)?.infix; if (!infixRule) break; left = infixRule(this, left); } return left; } getPrecedence() { const rule = this.parseRules.get(this.peek().type); return rule?.precedence || Precedence.NONE; } // Expression parsing methods parseLiteral() { const token = this.previous(); let value = token.value; if (token.type === TokenType.BOOLEAN) { value = token.value === 'true'; } else if (token.type === TokenType.NUMBER) { value = parseFloat(token.value); } return { type: 'Expression', expressionType: ExpressionType.LITERAL, value, range: token.range }; } parseIdentifier() { const token = this.previous(); // Check if identifier is declared if (this.options.strictMode && !this.hasSymbol(token.value)) { this.addWarning(`Undefined variable: '${token.value}'`, token.range.start); } return { type: 'Expression', expressionType: ExpressionType.IDENTIFIER, identifier: token.value, range: token.range }; } parseGrouping() { const expr = this.parseExpression(); this.consume(TokenType.RIGHT_PAREN, "Expected ')' after expression"); return expr; } parseUnary() { const operator = this.previous(); const right = this.parsePrecedence(Precedence.UNARY); return { type: 'Expression', expressionType: ExpressionType.UNARY, operator: operator.value, right, range: { start: operator.range.start, end: right.range.end } }; } parseBinary(parser, left) { const operator = this.previous(); const rule = this.parseRules.get(operator.type); const right = this.parsePrecedence((rule?.precedence || Precedence.NONE) + 1); return { type: 'Expression', expressionType: ExpressionType.BINARY, operator: operator.value, left, right, range: { start: left.range.start, end: right.range.end } }; } parseAssignment(parser, left) { const operator = this.previous(); const right = this.parseExpression(); return { type: 'Expression', expressionType: ExpressionType.ASSIGNMENT, operator: operator.value, left, right, range: { start: left.range.start, end: right.range.end } }; } parseCall(parser, left) { const args = []; if (!this.check(TokenType.RIGHT_PAREN)) { do { args.push(this.parseExpression()); } while (this.match(TokenType.COMMA)); } const paren = this.consume(TokenType.RIGHT_PAREN, "Expected ')' after arguments"); return { type: 'Expression', expressionType: ExpressionType.CALL, callee: left, arguments: args, range: { start: left.range.start, end: paren.range.end } }; } parseMember(parser, left) { const name = this.consume(TokenType.IDENTIFIER, "Expected property name after '.'"); return { type: 'Expression', expressionType: ExpressionType.MEMBER, object: left, property: { type: 'Expression', expressionType: ExpressionType.IDENTIFIER, identifier: name.value, range: name.range }, range: { start: left.range.start, end: name.range.end } }; } parseHTMLElement() { const start = this.getCurrentPosition(); // Simplified HTML parsing - would need much more sophisticated implementation const tagName = 'div'; // Placeholder const attributes = []; const children = []; const end = this.getCurrentPosition(); return { type: 'HTMLElement', tagName, attributes, children, isSelfClosing: false, isVoidElement: false, range: { start, end } }; } parseInterpolation() { const start = this.getCurrentPosition(); this.consume(TokenType.INTERPOLATION_START, "Expected '{' for interpolation"); const expression = this.parseExpression(); this.consume(TokenType.INTERPOLATION_END, "Expected '}' after interpolation"); const end = this.getCurrentPosition(); return { type: 'Interpolation', expression, range: { start, end } }; } parseTextNode() { const start = this.getCurrentPosition(); const content = this.consume(TokenType.HTML_TEXT, 'Expected text content').value; const end = this.getCurrentPosition(); return { type: 'Text', content, range: { start, end } }; } // Symbol table management enterScope() { this.state.context.scopeDepth++; } exitScope() { // Remove symbols from current scope for (const [name, symbol] of this.state.context.symbolTable) { if (symbol.scope === this.state.context.scopeDepth) { this.state.context.symbolTable.delete(name); } } this.state.context.scopeDepth--; } addSymbol(name, type, dataType, position, isConst = false, isPublic = false) { this.state.context.symbolTable.set(name, { name, type, dataType, scope: this.state.context.scopeDepth, position, isConst, isPublic }); } hasSymbol(name) { return this.state.context.symbolTable.has(name); } hasSymbolInCurrentScope(name) { const symbol = this.state.context.symbolTable.get(name); return symbol?.scope === this.state.context.scopeDepth; } getSymbol(name) { return this.state.context.symbolTable.get(name); } // AST processing processAST(component) { if (this.options.nodeProcessors.length === 0) return; this.processNode(component); } processNode(node) { for (const processor of this.options.nodeProcessors) { if (processor.handles.includes(node.type)) { node = processor.process(node, this.state.context); } } // Recursively process child nodes if (node.children) { for (const child of node.children) { this.processNode(child); } } // Process specific node types if (node.clientBlock) this.processNode(node.clientBlock); if (node.serverBlock) this.processNode(node.serverBlock); if (node.markupBlock) this.processNode(node.markupBlock); if (node.functions) { for (const func of node.functions) { this.processNode(func); } } if (node.reactiveVariables) { for (const variable of node.reactiveVariables) { this.processNode(variable); } } } // Utility methods checkLifecycleHook() { const value = this.peek().value; return ['onMount', 'onUnmount', 'onUpdate', 'beforeUpdate', 'afterUpdate'].includes(value); } isValidComponentName(name) { return /^[A-Z][a-zA-Z0-9]*$/.test(name); } createDummyExpression() { return { type: 'Expression', expressionType: ExpressionType.LITERAL, value: null, range: { start: this.getCurrentPosition(), end: this.getCurrentPosition() } }; } extractDependencies(component) { // Extract import dependencies from the component const dependencies = []; // Implementation would analyze the AST for imports return dependencies; } extractExports(component) { // Extract exports from the component const exports = [component.name]; // Implementation would analyze the AST for exports return exports; } generateSourceMap() { // Generate source map for debugging return { version: 3, sources: [this.state.filename], names: [], mappings: '', sourcesContent: [] }; } // Token stream utilities match(...types) { for (const type of types) { if (this.check(type)) { this.advance(); return true; } } return false; } check(type) { if (this.isAtEnd()) return false; return this.peek().type === type; } advance() { if (!this.isAtEnd()) this.state.current++; return this.previous(); } isAtEnd() { return this.peek().type === TokenType.EOF; } peek() { return this.state.tokenStream.tokens[this.state.current] || this.createEOFToken(); } previous() { return this.state.tokenStream.tokens[this.state.current - 1] || this.createEOFToken(); } consume(type, message) { if (this.check(type)) return this.advance(); this.throwError(message, [type.toString()]); // Return dummy token for error recovery return this.createEOFToken(); } createEOFToken() { return { type: TokenType.EOF, value: '', position: this.getCurrentPosition(), range: { start: this.getCurrentPosition(), end: this.getCurrentPosition() } }; } throwError(message, expected) { const error = new SyntaxError(message, this.getCurrentPosition(), expected, this.peek().value, this.state.filename); if (this.options.allowRecovery && this.state.errors.length < this.options.maxErrors) { this.state.errors.push(error); this.synchronize(); throw error; } throw error; } addWarning(message, position) { const warning = new SyntaxError(message, position, [], '', this.state.filename); this.state.warnings.push(warning); } synchronize() { this.advance(); while (!this.isAtEnd()) { if (this.previous().type === TokenType.SEMICOLON) return; switch (this.peek().type) { case TokenType.COMPONENT: case TokenType.CLIENT: case TokenType.SERVER: case TokenType.MARKUP: case TokenType.LET: case TokenType.CONST: return; } this.advance(); } } getCurrentPosition() { return this.peek().position; } } /** * Default AST node processors */ export const defaultNodeProcessors = [ { name: 'symbol-validator', handles: ['Expression'], process: (node, context) => { if (node.type === 'Expression') { const expr = node; if (expr.expressionType === ExpressionType.IDENTIFIER && expr.identifier) { if (!context.symbolTable.has(expr.identifier)) { // Could emit warning about undefined variable } } } return node; } }, { name: 'type-checker', handles: ['ReactiveVariable', 'Function', 'ServerFunction'], process: (node, context) => { // Could perform type checking here return node; } } ]; //# sourceMappingURL=parser-refactored.js.map