UNPKG

java2ib

Version:

TypeScript library that converts Java code into IB Computer Science pseudocode format

1,032 lines 39.7 kB
"use strict"; /** * Parser for Java source code that builds an Abstract Syntax Tree (AST) */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Parser = void 0; const types_1 = require("./types"); class Parser { constructor(tokens) { this.current = 0; this.errors = []; // Filter out whitespace tokens for easier parsing this.tokens = tokens.filter(token => token.type !== types_1.TokenType.WHITESPACE); } /** * Parse tokens into an Abstract Syntax Tree * @returns Parse result with AST and any syntax errors */ parse() { this.errors = []; this.current = 0; try { const ast = this.parseProgram(); return { ast, errors: this.errors }; } catch (error) { // Add any uncaught parsing errors if (error instanceof Error) { this.addError(types_1.ErrorType.SYNTAX_ERROR, error.message, this.getCurrentLocation()); } return { ast: null, errors: this.errors }; } } parseProgram() { const declarations = []; const location = this.getCurrentLocation(); while (!this.isAtEnd()) { try { const declaration = this.parseDeclaration(); if (declaration) { declarations.push(declaration); } } catch (error) { // Synchronize on error - skip to next likely declaration start this.synchronize(); } } return { type: types_1.NodeType.PROGRAM, location, declarations, children: declarations }; } parseDeclaration() { try { // Skip comments if (this.check(types_1.TokenType.COMMENT)) { this.advance(); return null; } // Check for class declaration (with or without modifiers) if (this.match('class')) { return this.parseClassDeclaration(); } // Check for declarations with modifiers (class, method, or field) if (this.checkModifiers()) { const savedPosition = this.current; // Skip modifiers while (this.checkModifiers()) { this.advance(); } // Check if next token is 'class' if (this.check(types_1.TokenType.KEYWORD, 'class')) { // Reset position and parse as class declaration with modifiers this.current = savedPosition; return this.parseClassDeclarationWithModifiers(); } // Check if this is a method or field declaration // Look for: [modifiers] type name ( -> method // Look for: [modifiers] type name ; -> field if (this.checkDataType() || this.check(types_1.TokenType.KEYWORD, 'void') || this.check(types_1.TokenType.IDENTIFIER)) { this.advance(); // consume type if (this.check(types_1.TokenType.IDENTIFIER)) { this.advance(); // consume name if (this.check(types_1.TokenType.PUNCTUATION, '(')) { // This is a method declaration this.current = savedPosition; return this.parseMethodDeclaration(); } else if (this.check(types_1.TokenType.PUNCTUATION, ';') || this.check(types_1.TokenType.OPERATOR, '=')) { // This is a field declaration this.current = savedPosition; return this.parseFieldDeclaration(); } } } // Reset position and try as method declaration (fallback) this.current = savedPosition; return this.parseMethodDeclaration(); } // Check for variable declaration or method declaration if (this.checkDataType() || this.check(types_1.TokenType.KEYWORD, 'void')) { // Look ahead to determine if this is a method or variable const savedPosition = this.current; // Skip modifiers if any while (this.checkModifiers()) { this.advance(); } // Skip return type if (this.checkDataType() || this.check(types_1.TokenType.KEYWORD, 'void')) { this.advance(); } // Check if next token is identifier followed by '(' if (this.check(types_1.TokenType.IDENTIFIER)) { this.advance(); if (this.check(types_1.TokenType.PUNCTUATION, '(')) { // This is a method declaration this.current = savedPosition; return this.parseMethodDeclaration(); } } // Reset position and parse as variable declaration this.current = savedPosition; return this.parseVariableDeclaration(); } // Parse as statement if not a declaration return this.parseStatement(); } catch (error) { this.synchronize(); return null; } } parseClassDeclaration() { const location = this.getCurrentLocation(); // 'class' keyword already consumed const name = this.consume(types_1.TokenType.IDENTIFIER, "Expected class name").value; // Check for inheritance (extends keyword) let superClass; if (this.match('extends')) { superClass = this.consume(types_1.TokenType.IDENTIFIER, "Expected superclass name after 'extends'").value; } this.consume(types_1.TokenType.PUNCTUATION, "Expected '{' after class name", '{'); const methods = []; const fields = []; while (!this.check(types_1.TokenType.PUNCTUATION, '}') && !this.isAtEnd()) { const declaration = this.parseDeclaration(); if (declaration) { if (declaration.type === types_1.NodeType.METHOD_DECLARATION) { methods.push(declaration); } else if (declaration.type === types_1.NodeType.VARIABLE_DECLARATION) { fields.push(declaration); } } } this.consume(types_1.TokenType.PUNCTUATION, "Expected '}' after class body", '}'); return { type: types_1.NodeType.CLASS_DECLARATION, location, name, superClass, methods, fields, children: [...fields, ...methods] }; } parseClassDeclarationWithModifiers() { const location = this.getCurrentLocation(); // Parse and skip modifiers (we don't use them for class declarations in pseudocode) while (this.checkModifiers()) { this.advance(); } // Consume 'class' keyword this.consume(types_1.TokenType.KEYWORD, "Expected 'class' keyword", 'class'); // Parse class name const name = this.consume(types_1.TokenType.IDENTIFIER, "Expected class name").value; // Check for inheritance (extends keyword) let superClass; if (this.match('extends')) { superClass = this.consume(types_1.TokenType.IDENTIFIER, "Expected superclass name after 'extends'").value; } this.consume(types_1.TokenType.PUNCTUATION, "Expected '{' after class name", '{'); const methods = []; const fields = []; while (!this.check(types_1.TokenType.PUNCTUATION, '}') && !this.isAtEnd()) { const declaration = this.parseDeclaration(); if (declaration) { if (declaration.type === types_1.NodeType.METHOD_DECLARATION) { methods.push(declaration); } else if (declaration.type === types_1.NodeType.VARIABLE_DECLARATION) { fields.push(declaration); } } } this.consume(types_1.TokenType.PUNCTUATION, "Expected '}' after class body", '}'); return { type: types_1.NodeType.CLASS_DECLARATION, location, name, superClass, methods, fields, children: [...fields, ...methods] }; } parseMethodDeclaration() { const location = this.getCurrentLocation(); let isPublic = false; let isStatic = false; // Parse modifiers while (this.checkModifiers()) { const modifier = this.advance().value; if (modifier === 'public') isPublic = true; if (modifier === 'static') isStatic = true; } // Parse return type (can be keyword like 'void', 'int' or identifier like 'String') let returnType; if (this.check(types_1.TokenType.KEYWORD) || this.check(types_1.TokenType.IDENTIFIER)) { returnType = this.advance().value; } else { throw new Error("Expected return type (void, int, double, boolean, String, etc.)"); } const isVoid = returnType === 'void'; // Parse method name const name = this.consume(types_1.TokenType.IDENTIFIER, "Expected method name").value; // Parse parameters this.consume(types_1.TokenType.PUNCTUATION, "Expected '(' after method name", '('); const parameters = []; if (!this.check(types_1.TokenType.PUNCTUATION, ')')) { do { // Parse parameter type (can be keyword like 'int' or identifier like 'String') let paramType; if (this.check(types_1.TokenType.KEYWORD) || this.check(types_1.TokenType.IDENTIFIER)) { paramType = this.advance().value; } else { throw new Error("Expected parameter type (int, double, boolean, String, etc.)"); } // Handle array types (e.g., String[], int[]) if (this.check(types_1.TokenType.PUNCTUATION, '[')) { this.advance(); // consume '[' this.consume(types_1.TokenType.PUNCTUATION, "Expected ']' after '['", ']'); paramType += '[]'; } const paramName = this.consume(types_1.TokenType.IDENTIFIER, "Expected parameter name").value; parameters.push({ type: types_1.NodeType.IDENTIFIER, // Using IDENTIFIER as base type for parameters location: this.getCurrentLocation(), name: paramName, paramType: paramType }); } while (this.match(',')); } this.consume(types_1.TokenType.PUNCTUATION, "Expected ')' after parameters", ')'); // Parse method body const body = this.parseBlock(); return { type: types_1.NodeType.METHOD_DECLARATION, location, name, returnType, parameters, body, isVoid, isStatic, isPublic, children: body }; } parseVariableDeclaration() { const varDecl = this.parseVariableDeclarationWithoutSemicolon(); this.consume(types_1.TokenType.PUNCTUATION, "Expected ';' after variable declaration", ';'); return varDecl; } parseFieldDeclaration() { const location = this.getCurrentLocation(); // Skip modifiers (private, public, static, etc.) while (this.checkModifiers()) { this.advance(); } // Parse data type let dataType; if (this.check(types_1.TokenType.KEYWORD) || this.check(types_1.TokenType.IDENTIFIER)) { dataType = this.advance().value; } else { throw new Error("Expected field type (int, double, boolean, String, etc.)"); } // Handle array types (e.g., int[], String[]) if (this.check(types_1.TokenType.PUNCTUATION, '[')) { this.advance(); // consume '[' this.consume(types_1.TokenType.PUNCTUATION, "Expected ']' after '['", ']'); dataType += '[]'; } const name = this.consume(types_1.TokenType.IDENTIFIER, "Expected field name").value; let initializer; if (this.matchOperator('=')) { initializer = this.parseExpression(); } this.consume(types_1.TokenType.PUNCTUATION, "Expected ';' after field declaration", ';'); return { type: types_1.NodeType.VARIABLE_DECLARATION, location, name, dataType, initializer, children: initializer ? [initializer] : undefined }; } parseVariableDeclarationWithoutSemicolon() { const location = this.getCurrentLocation(); let dataType = this.advance().value; // Handle array types (e.g., int[], String[]) if (this.check(types_1.TokenType.PUNCTUATION, '[')) { this.advance(); // consume '[' this.consume(types_1.TokenType.PUNCTUATION, "Expected ']' after '['", ']'); dataType += '[]'; } const name = this.consume(types_1.TokenType.IDENTIFIER, "Expected variable name").value; let initializer; if (this.matchOperator('=')) { initializer = this.parseExpression(); } return { type: types_1.NodeType.VARIABLE_DECLARATION, location, name, dataType, initializer, children: initializer ? [initializer] : undefined }; } parseStatement() { // Return statement if (this.match('return')) { return this.parseReturnStatement(); } // If statement if (this.match('if')) { return this.parseIfStatement(); } // While loop if (this.match('while')) { return this.parseWhileLoop(); } // For loop if (this.match('for')) { return this.parseForLoop(); } // Switch statement if (this.match('switch')) { return this.parseSwitchStatement(); } // Break statement if (this.match('break')) { return this.parseBreakStatement(); } // Continue statement if (this.match('continue')) { return this.parseContinueStatement(); } // Block statement if (this.check(types_1.TokenType.PUNCTUATION, '{')) { return this.parseBlockStatement(); } // Variable declaration (for switch cases and other contexts) if (this.checkDataType()) { return this.parseVariableDeclaration(); } // Expression statement (assignment, method call, etc.) return this.parseExpressionStatement(); } parseIfStatement() { const location = this.getCurrentLocation(); this.consume(types_1.TokenType.PUNCTUATION, "Expected '(' after 'if'", '('); const condition = this.parseExpression(); this.consume(types_1.TokenType.PUNCTUATION, "Expected ')' after if condition", ')'); const thenStatement = this.parseStatement(); let elseStatement; if (this.match('else')) { elseStatement = this.parseStatement(); } return { type: types_1.NodeType.IF_STATEMENT, location, condition, thenStatement, elseStatement, children: elseStatement ? [condition, thenStatement, elseStatement] : [condition, thenStatement] }; } parseWhileLoop() { const location = this.getCurrentLocation(); this.consume(types_1.TokenType.PUNCTUATION, "Expected '(' after 'while'", '('); const condition = this.parseExpression(); this.consume(types_1.TokenType.PUNCTUATION, "Expected ')' after while condition", ')'); const body = this.parseStatement(); return { type: types_1.NodeType.WHILE_LOOP, location, condition, body, children: [condition, body] }; } parseForLoop() { const location = this.getCurrentLocation(); this.consume(types_1.TokenType.PUNCTUATION, "Expected '(' after 'for'", '('); // Check for enhanced for loop (for-each): for (Type var : iterable) // Look ahead to see if this is an enhanced for loop pattern const savedPosition = this.current; let isEnhancedFor = false; if (this.checkDataTypeOrIdentifier()) { this.advance(); // consume type if (this.check(types_1.TokenType.IDENTIFIER)) { this.advance(); // consume variable name if (this.check(types_1.TokenType.OPERATOR, ':')) { isEnhancedFor = true; } } } // Reset position this.current = savedPosition; if (isEnhancedFor) { // Parse as enhanced for loop this.advance(); // consume type const variable = this.consume(types_1.TokenType.IDENTIFIER, "Expected variable name").value; this.consume(types_1.TokenType.OPERATOR, "Expected ':' in enhanced for loop", ':'); const iterable = this.parseExpression(); this.consume(types_1.TokenType.PUNCTUATION, "Expected ')' after enhanced for loop", ')'); const body = this.parseStatement(); const variableNode = { type: types_1.NodeType.IDENTIFIER, location: this.getCurrentLocation(), name: variable, children: [] }; return { type: types_1.NodeType.ENHANCED_FOR_LOOP, location, variable: variableNode, iterable, body, children: [variableNode, iterable, body] }; } // Parse regular for loop // Parse initialization (optional) let initialization; if (!this.check(types_1.TokenType.PUNCTUATION, ';')) { if (this.checkDataType()) { initialization = this.parseVariableDeclarationWithoutSemicolon(); } else { initialization = this.parseExpression(); } } this.consume(types_1.TokenType.PUNCTUATION, "Expected ';' after for loop initialization", ';'); // Parse condition (optional) let condition; if (!this.check(types_1.TokenType.PUNCTUATION, ';')) { condition = this.parseExpression(); } this.consume(types_1.TokenType.PUNCTUATION, "Expected ';' after for loop condition", ';'); // Parse update (optional) let update; if (!this.check(types_1.TokenType.PUNCTUATION, ')')) { update = this.parseExpression(); } this.consume(types_1.TokenType.PUNCTUATION, "Expected ')' after for loop clauses", ')'); const body = this.parseStatement(); const children = []; if (initialization) children.push(initialization); if (condition) children.push(condition); if (update) children.push(update); children.push(body); return { type: types_1.NodeType.FOR_LOOP, location, initialization, condition, update, body, children }; } parseReturnStatement() { const location = this.getCurrentLocation(); // 'return' keyword already consumed let expression; // Check if there's an expression to return if (!this.check(types_1.TokenType.PUNCTUATION, ';')) { expression = this.parseExpression(); } this.consume(types_1.TokenType.PUNCTUATION, "Expected ';' after return statement", ';'); return { type: types_1.NodeType.RETURN_STATEMENT, location, expression, children: expression ? [expression] : [] }; } parseSwitchStatement() { const location = this.getCurrentLocation(); this.consume(types_1.TokenType.PUNCTUATION, "Expected '(' after 'switch'", '('); const discriminant = this.parseExpression(); this.consume(types_1.TokenType.PUNCTUATION, "Expected ')' after switch expression", ')'); this.consume(types_1.TokenType.PUNCTUATION, "Expected '{' after switch", '{'); const cases = []; while (!this.check(types_1.TokenType.PUNCTUATION, '}') && !this.isAtEnd()) { if (this.match('case')) { const caseLocation = this.getCurrentLocation(); const test = this.parseExpression(); this.consume(types_1.TokenType.OPERATOR, "Expected ':' after case value", ':'); const consequent = []; while (!this.check(types_1.TokenType.KEYWORD, 'case') && !this.check(types_1.TokenType.KEYWORD, 'default') && !this.check(types_1.TokenType.PUNCTUATION, '}') && !this.isAtEnd()) { const stmt = this.parseStatement(); if (stmt) { consequent.push(stmt); } } cases.push({ type: types_1.NodeType.CASE_CLAUSE, location: caseLocation, test, consequent, children: [test, ...consequent] }); } else if (this.match('default')) { const defaultLocation = this.getCurrentLocation(); this.consume(types_1.TokenType.OPERATOR, "Expected ':' after 'default'", ':'); const consequent = []; while (!this.check(types_1.TokenType.KEYWORD, 'case') && !this.check(types_1.TokenType.KEYWORD, 'default') && !this.check(types_1.TokenType.PUNCTUATION, '}') && !this.isAtEnd()) { const stmt = this.parseStatement(); if (stmt) { consequent.push(stmt); } } cases.push({ type: types_1.NodeType.DEFAULT_CLAUSE, location: defaultLocation, consequent, children: consequent }); } else { // Skip unexpected tokens this.advance(); } } this.consume(types_1.TokenType.PUNCTUATION, "Expected '}' after switch body", '}'); return { type: types_1.NodeType.SWITCH_STATEMENT, location, discriminant, cases, children: [discriminant, ...cases] }; } parseBreakStatement() { const location = this.getCurrentLocation(); this.consume(types_1.TokenType.PUNCTUATION, "Expected ';' after 'break'", ';'); return { type: types_1.NodeType.BREAK_STATEMENT, location, children: [] }; } parseContinueStatement() { const location = this.getCurrentLocation(); this.consume(types_1.TokenType.PUNCTUATION, "Expected ';' after 'continue'", ';'); return { type: types_1.NodeType.CONTINUE_STATEMENT, location, children: [] }; } parseBlockStatement() { const statements = this.parseBlock(); // Return a program-like node to represent the block return { type: types_1.NodeType.PROGRAM, // Using PROGRAM type for block statements location: this.getCurrentLocation(), children: statements }; } parseBlock() { this.consume(types_1.TokenType.PUNCTUATION, "Expected '{'", '{'); const statements = []; while (!this.check(types_1.TokenType.PUNCTUATION, '}') && !this.isAtEnd()) { const stmt = this.parseDeclaration(); if (stmt) { statements.push(stmt); } } this.consume(types_1.TokenType.PUNCTUATION, "Expected '}'", '}'); return statements; } parseExpressionStatement() { const expr = this.parseExpression(); this.consume(types_1.TokenType.PUNCTUATION, "Expected ';' after expression", ';'); return expr; } parseExpression() { return this.parseAssignment(); } parseAssignment() { const expr = this.parseLogicalOr(); if (this.matchOperator('=', '+=', '-=', '*=', '/=', '%=')) { const operator = this.previous().value; const right = this.parseAssignment(); return { type: types_1.NodeType.ASSIGNMENT, location: expr.location, left: expr, right, operator, children: [expr, right] }; } return expr; } parseLogicalOr() { let expr = this.parseLogicalAnd(); while (this.matchOperator('||')) { const operator = this.previous().value; const right = this.parseLogicalAnd(); expr = { type: types_1.NodeType.BINARY_EXPRESSION, location: expr.location, left: expr, right, operator, children: [expr, right] }; } return expr; } parseLogicalAnd() { let expr = this.parseEquality(); while (this.matchOperator('&&')) { const operator = this.previous().value; const right = this.parseEquality(); expr = { type: types_1.NodeType.BINARY_EXPRESSION, location: expr.location, left: expr, right, operator, children: [expr, right] }; } return expr; } parseEquality() { let expr = this.parseComparison(); while (this.matchOperator('==', '!=')) { const operator = this.previous().value; const right = this.parseComparison(); expr = { type: types_1.NodeType.BINARY_EXPRESSION, location: expr.location, left: expr, right, operator, children: [expr, right] }; } return expr; } parseComparison() { let expr = this.parseTerm(); while (this.matchOperator('>', '>=', '<', '<=')) { const operator = this.previous().value; const right = this.parseTerm(); expr = { type: types_1.NodeType.BINARY_EXPRESSION, location: expr.location, left: expr, right, operator, children: [expr, right] }; } return expr; } parseTerm() { let expr = this.parseFactor(); while (this.matchOperator('+', '-')) { const operator = this.previous().value; const right = this.parseFactor(); expr = { type: types_1.NodeType.BINARY_EXPRESSION, location: expr.location, left: expr, right, operator, children: [expr, right] }; } return expr; } parseFactor() { let expr = this.parseUnary(); while (this.matchOperator('*', '/', '%')) { const operator = this.previous().value; const right = this.parseUnary(); expr = { type: types_1.NodeType.BINARY_EXPRESSION, location: expr.location, left: expr, right, operator, children: [expr, right] }; } return expr; } parseUnary() { if (this.matchOperator('!', '-', '+', '++', '--')) { const operator = this.previous().value; const right = this.parseUnary(); return { type: types_1.NodeType.BINARY_EXPRESSION, // Using BINARY_EXPRESSION for unary as well location: this.getCurrentLocation(), left: { type: types_1.NodeType.LITERAL, location: this.getCurrentLocation(), value: '', children: [] }, // Empty left operand for unary right, operator, children: [right] }; } return this.parsePostfix(); } parsePostfix() { let expr = this.parsePrimary(); while (true) { if (this.matchOperator('++', '--')) { // Postfix increment/decrement const operator = this.previous().value; expr = { type: types_1.NodeType.BINARY_EXPRESSION, location: expr.location, left: expr, right: { type: types_1.NodeType.LITERAL, location: this.getCurrentLocation(), value: '', children: [] }, operator, children: [expr] }; } else if (this.match('[')) { // Array access const index = this.parseExpression(); this.consume(types_1.TokenType.PUNCTUATION, "Expected ']' after array index", ']'); expr = { type: types_1.NodeType.ARRAY_ACCESS, location: expr.location, array: expr, index, children: [expr, index] }; } else if (this.match('.')) { // Method call or field access const name = this.consume(types_1.TokenType.IDENTIFIER, "Expected method or field name after '.'").value; if (this.match('(')) { // Method call const args = []; if (!this.check(types_1.TokenType.PUNCTUATION, ')')) { do { args.push(this.parseExpression()); } while (this.match(',')); } this.consume(types_1.TokenType.PUNCTUATION, "Expected ')' after method arguments", ')'); expr = { type: types_1.NodeType.METHOD_CALL, location: expr.location, object: expr, methodName: name, arguments: args, children: [expr, ...args] }; } else { // Field access - treat as identifier for now expr = { type: types_1.NodeType.IDENTIFIER, location: expr.location, name: `${this.getExpressionName(expr)}.${name}`, children: [expr] }; } } else if (this.match('(')) { // Method call without object const args = []; if (!this.check(types_1.TokenType.PUNCTUATION, ')')) { do { args.push(this.parseExpression()); } while (this.match(',')); } this.consume(types_1.TokenType.PUNCTUATION, "Expected ')' after method arguments", ')'); expr = { type: types_1.NodeType.METHOD_CALL, location: expr.location, object: undefined, methodName: this.getExpressionName(expr), arguments: args, children: args }; } else { break; } } return expr; } parsePrimary() { const location = this.getCurrentLocation(); // Boolean literals (keywords true/false) if (this.check(types_1.TokenType.KEYWORD, 'true') || this.check(types_1.TokenType.KEYWORD, 'false')) { const token = this.advance(); return { type: types_1.NodeType.LITERAL, location, value: token.value, dataType: 'boolean', children: [] }; } // Literals if (this.check(types_1.TokenType.LITERAL)) { const token = this.advance(); let dataType; if (token.value.startsWith('"')) { dataType = 'string'; } else if (token.value.startsWith("'")) { dataType = 'char'; } else if (token.value === 'true' || token.value === 'false') { dataType = 'boolean'; } else { dataType = 'number'; } return { type: types_1.NodeType.LITERAL, location, value: token.value, dataType, children: [] }; } // Identifiers if (this.check(types_1.TokenType.IDENTIFIER)) { const name = this.advance().value; return { type: types_1.NodeType.IDENTIFIER, location, name, children: [] }; } // Array initialization: {1, 2, 3} if (this.match('{')) { const elements = []; if (!this.check(types_1.TokenType.PUNCTUATION, '}')) { do { elements.push(this.parseExpression()); } while (this.match(',')); } this.consume(types_1.TokenType.PUNCTUATION, "Expected '}' after array elements", '}'); return { type: types_1.NodeType.ARRAY_INITIALIZATION, location, elements, children: elements }; } // Parenthesized expressions if (this.match('(')) { const expr = this.parseExpression(); this.consume(types_1.TokenType.PUNCTUATION, "Expected ')' after expression", ')'); return expr; } const token = this.peek(); if (token) { throw new Error(`Unexpected token '${token.value}' of type ${token.type}. Expected a literal, identifier, or '(' to start an expression.`); } else { throw new Error('Unexpected end of file. Expected a literal, identifier, or \'(\' to start an expression.'); } } // Helper methods match(...values) { for (const value of values) { if (this.check(types_1.TokenType.KEYWORD, value) || this.check(types_1.TokenType.PUNCTUATION, value)) { this.advance(); return true; } } return false; } matchOperator(...operators) { for (const op of operators) { if (this.check(types_1.TokenType.OPERATOR, op)) { this.advance(); return true; } } return false; } check(type, value) { if (this.isAtEnd()) return false; const token = this.peek(); return token.type === type && (value === undefined || token.value === value); } checkModifiers() { return this.check(types_1.TokenType.KEYWORD, 'public') || this.check(types_1.TokenType.KEYWORD, 'private') || this.check(types_1.TokenType.KEYWORD, 'static'); } checkReturnType() { return this.check(types_1.TokenType.KEYWORD, 'void') || this.checkDataType(); } checkDataType() { // Check for basic data types (including array types) const isBasicType = this.check(types_1.TokenType.KEYWORD, 'int') || this.check(types_1.TokenType.KEYWORD, 'double') || this.check(types_1.TokenType.KEYWORD, 'boolean') || this.check(types_1.TokenType.KEYWORD, 'String') || this.check(types_1.TokenType.IDENTIFIER, 'String') || this.check(types_1.TokenType.KEYWORD, 'char') || this.check(types_1.TokenType.KEYWORD, 'float') || this.check(types_1.TokenType.KEYWORD, 'long') || this.check(types_1.TokenType.KEYWORD, 'short') || this.check(types_1.TokenType.KEYWORD, 'byte'); return isBasicType; } checkDataTypeOrIdentifier() { // Check for basic data types or any identifier (for custom classes) return this.checkDataType() || this.check(types_1.TokenType.IDENTIFIER); } advance() { if (!this.isAtEnd()) this.current++; return this.previous(); } isAtEnd() { return this.current >= this.tokens.length; } peek() { return this.tokens[this.current]; } previous() { return this.tokens[this.current - 1]; } consume(type, message, value) { if (this.check(type, value)) { return this.advance(); } const currentToken = this.peek(); const location = currentToken ? currentToken.location : this.getCurrentLocation(); this.addError(types_1.ErrorType.SYNTAX_ERROR, message, location); throw new Error(message); } synchronize() { this.advance(); while (!this.isAtEnd()) { if (this.previous().type === types_1.TokenType.PUNCTUATION && this.previous().value === ';') { return; } const current = this.peek(); if (current.type === types_1.TokenType.KEYWORD) { const keyword = current.value; if (['class', 'public', 'private', 'static', 'if', 'while', 'for', 'return'].includes(keyword)) { return; } } this.advance(); } } getCurrentLocation() { if (this.isAtEnd() && this.tokens.length > 0) { return this.tokens[this.tokens.length - 1].location; } return this.peek()?.location || { line: 1, column: 1 }; } addError(type, message, location) { this.errors.push({ type, message, location, severity: types_1.ErrorSeverity.ERROR }); } getExpressionName(expr) { if (expr.type === types_1.NodeType.IDENTIFIER) { return expr.name; } return 'unknown'; } } exports.Parser = Parser; //# sourceMappingURL=parser.js.map