java2ib
Version:
TypeScript library that converts Java code into IB Computer Science pseudocode format
1,032 lines • 39.7 kB
JavaScript
"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