| | |
| | | #include "Interpreter/OperationsFactory.hpp" |
| | | #include "Lexer/Operators.hpp" |
| | | // Statements and expression building for conditional and block parsing |
| | | #include "Interpreter/ConditionalStatementNode.hpp" |
| | | // #include "Interpreter/ForStatementNode.hpp" // removed until for-in loops are implemented |
| | | #include "Interpreter/CallStatementNode.hpp" |
| | | #include "Interpreter/AssignmentStatementNode.hpp" |
| | | #include "Interpreter/CallStatementNode.hpp" |
| | | #include "Interpreter/ConditionalStatementNode.hpp" |
| | | #include "Interpreter/DeclareVariableStatementNode.hpp" |
| | | #include "Interpreter/ExpressionBuilder.hpp" |
| | | #include "Interpreter/ForStatementNode.hpp" |
| | | #include "Interpreter/ReturnStatementNode.hpp" |
| | | #include "Symbols/SymbolContainer.hpp" |
| | | #include "Interpreter/ExpressionBuilder.hpp" |
| | | |
| | | // Additional necessary includes, if needed |
| | | namespace Parser { |
| | |
| | | // Parse a top-level assignment statement and record it |
| | | void Parser::parseAssignmentStatement() { |
| | | auto stmt = parseStatementNode(); |
| | | Operations::Container::instance()->add( |
| | | Symbols::SymbolContainer::instance()->currentScopeName(), |
| | | Operations::Operation{Operations::Type::Assignment, "", std::move(stmt)} |
| | | ); |
| | | Operations::Container::instance()->add(Symbols::SymbolContainer::instance()->currentScopeName(), |
| | | Operations::Operation{ Operations::Type::Assignment, "", std::move(stmt) }); |
| | | } |
| | | |
| | | // Parse an if-else conditional statement |
| | |
| | | thenBranch.push_back(parseStatementNode()); |
| | | } |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "}"); |
| | | // else branch |
| | | // else / else-if branch |
| | | std::vector<std::unique_ptr<Interpreter::StatementNode>> elseBranch; |
| | | if (match(Lexer::Tokens::Type::KEYWORD, "else")) { |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "{"); |
| | | while (!(currentToken().type == Lexer::Tokens::Type::PUNCTUATION && currentToken().value == "}")) { |
| | | elseBranch.push_back(parseStatementNode()); |
| | | // else-if: nested conditional |
| | | if (currentToken().type == Lexer::Tokens::Type::KEYWORD && currentToken().value == "if") { |
| | | elseBranch.push_back(parseIfStatementNode()); |
| | | } else { |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "{"); |
| | | while (!(currentToken().type == Lexer::Tokens::Type::PUNCTUATION && currentToken().value == "}")) { |
| | | elseBranch.push_back(parseStatementNode()); |
| | | } |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "}"); |
| | | } |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "}"); |
| | | } |
| | | // build condition node |
| | | auto condNode = buildExpressionFromParsed(condExpr); |
| | | auto stmt = std::make_unique<Interpreter::ConditionalStatementNode>( |
| | | std::move(condNode), std::move(thenBranch), std::move(elseBranch), |
| | | this->current_filename_, ifToken.line_number, ifToken.column_number); |
| | | auto stmt = std::make_unique<Interpreter::ConditionalStatementNode>(std::move(condNode), std::move(thenBranch), |
| | | std::move(elseBranch), this->current_filename_, |
| | | ifToken.line_number, ifToken.column_number); |
| | | // add conditional operation |
| | | Operations::Container::instance()->add( |
| | | Symbols::SymbolContainer::instance()->currentScopeName(), |
| | | Operations::Operation{Operations::Type::Conditional, "", std::move(stmt)}); |
| | | Operations::Container::instance()->add(Symbols::SymbolContainer::instance()->currentScopeName(), |
| | | Operations::Operation{ Operations::Type::Conditional, "", std::move(stmt) }); |
| | | } |
| | | |
| | | // Parse an if-else conditional block and return a StatementNode (for nested blocks) |
| | | std::unique_ptr<Interpreter::StatementNode> Parser::parseIfStatementNode() { |
| | | auto ifToken = expect(Lexer::Tokens::Type::KEYWORD, "if"); |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "("); |
| | | auto condExpr = parseParsedExpression(Symbols::Variables::Type::NULL_TYPE); |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, ")"); |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "{"); |
| | | std::vector<std::unique_ptr<Interpreter::StatementNode>> thenBranch; |
| | | while (!(currentToken().type == Lexer::Tokens::Type::PUNCTUATION && currentToken().value == "}")) { |
| | | thenBranch.push_back(parseStatementNode()); |
| | | } |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "}"); |
| | | std::vector<std::unique_ptr<Interpreter::StatementNode>> elseBranch; |
| | | if (match(Lexer::Tokens::Type::KEYWORD, "else")) { |
| | | // else-if: nested conditional |
| | | if (currentToken().type == Lexer::Tokens::Type::KEYWORD && currentToken().value == "if") { |
| | | elseBranch.push_back(parseIfStatementNode()); |
| | | } else { |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "{"); |
| | | while (!(currentToken().type == Lexer::Tokens::Type::PUNCTUATION && currentToken().value == "}")) { |
| | | elseBranch.push_back(parseStatementNode()); |
| | | } |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "}"); |
| | | } |
| | | } |
| | | auto condNode = buildExpressionFromParsed(condExpr); |
| | | auto * node = |
| | | new Interpreter::ConditionalStatementNode(std::move(condNode), std::move(thenBranch), std::move(elseBranch), |
| | | this->current_filename_, ifToken.line_number, ifToken.column_number); |
| | | return std::unique_ptr<Interpreter::StatementNode>(node); |
| | | } |
| | | |
| | | // Parse a for-in loop over object members and return a StatementNode (for nested blocks) |
| | | std::unique_ptr<Interpreter::StatementNode> Parser::parseForStatementNode() { |
| | | auto forToken = expect(Lexer::Tokens::Type::KEYWORD, "for"); |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "("); |
| | | Symbols::Variables::Type keyType = parseType(); |
| | | auto keyTok = expect(Lexer::Tokens::Type::VARIABLE_IDENTIFIER); |
| | | std::string keyName = keyTok.value; |
| | | if (!keyName.empty() && keyName[0] == '$') { |
| | | keyName = keyName.substr(1); |
| | | } |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, ","); |
| | | if (!(currentToken().type == Lexer::Tokens::Type::IDENTIFIER && currentToken().value == "auto")) { |
| | | reportError("Expected 'auto' in for-in loop"); |
| | | } |
| | | consumeToken(); |
| | | auto valTok = expect(Lexer::Tokens::Type::VARIABLE_IDENTIFIER); |
| | | std::string valName = valTok.value; |
| | | if (!valName.empty() && valName[0] == '$') { |
| | | valName = valName.substr(1); |
| | | } |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, ":"); |
| | | auto iterableExpr = parseParsedExpression(Symbols::Variables::Type::NULL_TYPE); |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, ")"); |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "{"); |
| | | std::vector<std::unique_ptr<Interpreter::StatementNode>> body; |
| | | while (!(currentToken().type == Lexer::Tokens::Type::PUNCTUATION && currentToken().value == "}")) { |
| | | body.push_back(parseStatementNode()); |
| | | } |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "}"); |
| | | auto iterableExprNode = buildExpressionFromParsed(iterableExpr); |
| | | auto * node = |
| | | new Interpreter::ForStatementNode(keyType, keyName, valName, std::move(iterableExprNode), std::move(body), |
| | | this->current_filename_, forToken.line_number, forToken.column_number); |
| | | return std::unique_ptr<Interpreter::StatementNode>(node); |
| | | } |
| | | |
| | | // Parse a single statement and return its StatementNode (for use in blocks) |
| | | std::unique_ptr<Interpreter::StatementNode> Parser::parseStatementNode() { |
| | | // Handle nested if statements in blocks |
| | | if (currentToken().type == Lexer::Tokens::Type::KEYWORD && currentToken().value == "if") { |
| | | return parseIfStatementNode(); |
| | | } |
| | | // Handle nested for loops in blocks |
| | | if (currentToken().type == Lexer::Tokens::Type::KEYWORD && currentToken().value == "for") { |
| | | return parseForStatementNode(); |
| | | } |
| | | // Return statement |
| | | if (currentToken().type == Lexer::Tokens::Type::KEYWORD_RETURN) { |
| | | auto tok = expect(Lexer::Tokens::Type::KEYWORD_RETURN); |
| | | auto tok = expect(Lexer::Tokens::Type::KEYWORD_RETURN); |
| | | ParsedExpressionPtr expr = nullptr; |
| | | if (!(currentToken().type == Lexer::Tokens::Type::PUNCTUATION && currentToken().value == ";")) { |
| | | expr = parseParsedExpression(Symbols::Variables::Type::NULL_TYPE); |
| | | } |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, ";"); |
| | | auto exprNode = expr ? buildExpressionFromParsed(expr) : nullptr; |
| | | return std::make_unique<Interpreter::ReturnStatementNode>( |
| | | std::move(exprNode), this->current_filename_, tok.line_number, tok.column_number); |
| | | return std::make_unique<Interpreter::ReturnStatementNode>(std::move(exprNode), this->current_filename_, |
| | | tok.line_number, tok.column_number); |
| | | } |
| | | // Assignment statement: variable or object member assignment |
| | | if (currentToken().type == Lexer::Tokens::Type::VARIABLE_IDENTIFIER) { |
| | |
| | | const auto & look = peekToken(offset); |
| | | if (look.type == Lexer::Tokens::Type::OPERATOR_ASSIGNMENT && look.value == "=") { |
| | | // Consume base variable |
| | | auto idTok = expect(Lexer::Tokens::Type::VARIABLE_IDENTIFIER); |
| | | auto idTok = expect(Lexer::Tokens::Type::VARIABLE_IDENTIFIER); |
| | | std::string baseName = idTok.value; |
| | | if (!baseName.empty() && baseName[0] == '$') baseName = baseName.substr(1); |
| | | if (!baseName.empty() && baseName[0] == '$') { |
| | | baseName = baseName.substr(1); |
| | | } |
| | | // Collect member path keys |
| | | std::vector<std::string> propertyPath; |
| | | while (match(Lexer::Tokens::Type::PUNCTUATION, "->")) { |
| | |
| | | reportError("Expected property name after '->'"); |
| | | } |
| | | std::string propName = propTok.value; |
| | | if (!propName.empty() && propName[0] == '$') propName = propName.substr(1); |
| | | if (!propName.empty() && propName[0] == '$') { |
| | | propName = propName.substr(1); |
| | | } |
| | | propertyPath.push_back(propName); |
| | | } |
| | | // Consume '=' |
| | | auto eqTok = expect(Lexer::Tokens::Type::OPERATOR_ASSIGNMENT, "="); |
| | | auto eqTok = expect(Lexer::Tokens::Type::OPERATOR_ASSIGNMENT, "="); |
| | | // Parse RHS expression |
| | | auto rhsExpr = parseParsedExpression(Symbols::Variables::Type::NULL_TYPE); |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, ";"); |
| | | // Build RHS node |
| | | auto rhsNode = buildExpressionFromParsed(rhsExpr); |
| | | return std::make_unique<Interpreter::AssignmentStatementNode>( |
| | | baseName, std::move(propertyPath), std::move(rhsNode), |
| | | this->current_filename_, eqTok.line_number, eqTok.column_number); |
| | | return std::make_unique<Interpreter::AssignmentStatementNode>(baseName, std::move(propertyPath), |
| | | std::move(rhsNode), this->current_filename_, |
| | | eqTok.line_number, eqTok.column_number); |
| | | } |
| | | } |
| | | // Function call statement |
| | | if (currentToken().type == Lexer::Tokens::Type::IDENTIFIER && |
| | | peekToken().type == Lexer::Tokens::Type::PUNCTUATION && peekToken().value == "(") { |
| | | auto idTok = expect(Lexer::Tokens::Type::IDENTIFIER); |
| | | auto idTok = expect(Lexer::Tokens::Type::IDENTIFIER); |
| | | std::string funcName = idTok.value; |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "("); |
| | | std::vector<ParsedExpressionPtr> args; |
| | | if (!(currentToken().type == Lexer::Tokens::Type::PUNCTUATION && currentToken().value == ")")) { |
| | | while (true) { |
| | | args.push_back(parseParsedExpression(Symbols::Variables::Type::NULL_TYPE)); |
| | | if (match(Lexer::Tokens::Type::PUNCTUATION, ",")) continue; |
| | | if (match(Lexer::Tokens::Type::PUNCTUATION, ",")) { |
| | | continue; |
| | | } |
| | | break; |
| | | } |
| | | } |
| | |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, ";"); |
| | | std::vector<std::unique_ptr<Interpreter::ExpressionNode>> exprs; |
| | | exprs.reserve(args.size()); |
| | | for (auto &p : args) { |
| | | for (auto & p : args) { |
| | | exprs.push_back(buildExpressionFromParsed(p)); |
| | | } |
| | | return std::make_unique<Interpreter::CallStatementNode>( |
| | | funcName, std::move(exprs), this->current_filename_, idTok.line_number, idTok.column_number); |
| | | return std::make_unique<Interpreter::CallStatementNode>(funcName, std::move(exprs), this->current_filename_, |
| | | idTok.line_number, idTok.column_number); |
| | | } |
| | | // Variable declaration |
| | | if (Parser::variable_types.find(currentToken().type) != Parser::variable_types.end()) { |
| | | auto type = parseType(); |
| | | auto idTok = expect(Lexer::Tokens::Type::VARIABLE_IDENTIFIER); |
| | | std::string name = idTok.value; |
| | | if (!name.empty() && name[0] == '$') name = name.substr(1); |
| | | auto type = parseType(); |
| | | auto idTok = expect(Lexer::Tokens::Type::VARIABLE_IDENTIFIER); |
| | | std::string name = idTok.value; |
| | | if (!name.empty() && name[0] == '$') { |
| | | name = name.substr(1); |
| | | } |
| | | expect(Lexer::Tokens::Type::OPERATOR_ASSIGNMENT, "="); |
| | | auto valExpr = parseParsedExpression(type); |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, ";"); |
| | | auto exprNode = buildExpressionFromParsed(valExpr); |
| | | return std::make_unique<Interpreter::DeclareVariableStatementNode>( |
| | | name, Symbols::SymbolContainer::instance()->currentScopeName(), type, |
| | | std::move(exprNode), this->current_filename_, idTok.line_number, idTok.column_number); |
| | | name, Symbols::SymbolContainer::instance()->currentScopeName(), type, std::move(exprNode), |
| | | this->current_filename_, idTok.line_number, idTok.column_number); |
| | | } |
| | | reportError("Unexpected token in block"); |
| | | return nullptr; |
| | |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, ")"); |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, ";"); |
| | | // Record the function call operation |
| | | Interpreter::OperationsFactory::callFunction( |
| | | func_name, |
| | | std::move(args), |
| | | Symbols::SymbolContainer::instance()->currentScopeName(), |
| | | this->current_filename_, |
| | | id_token.line_number, |
| | | id_token.column_number); |
| | | Interpreter::OperationsFactory::callFunction(func_name, std::move(args), |
| | | Symbols::SymbolContainer::instance()->currentScopeName(), |
| | | this->current_filename_, id_token.line_number, id_token.column_number); |
| | | } |
| | | |
| | | // Parse a return statement, e.g., return; or return expression; |
| | | void Parser::parseReturnStatement() { |
| | | // Consume 'return' keyword |
| | | auto returnToken = expect(Lexer::Tokens::Type::KEYWORD_RETURN); |
| | | auto returnToken = expect(Lexer::Tokens::Type::KEYWORD_RETURN); |
| | | // Parse optional expression |
| | | ParsedExpressionPtr expr = nullptr; |
| | | ParsedExpressionPtr expr = nullptr; |
| | | if (!(currentToken().type == Lexer::Tokens::Type::PUNCTUATION && currentToken().value == ";")) { |
| | | expr = parseParsedExpression(Symbols::Variables::Type::NULL_TYPE); |
| | | } |
| | | // Record return operation |
| | | Interpreter::OperationsFactory::callReturn( |
| | | expr, |
| | | Symbols::SymbolContainer::instance()->currentScopeName(), |
| | | this->current_filename_, |
| | | returnToken.line_number, |
| | | returnToken.column_number); |
| | | Interpreter::OperationsFactory::callReturn(expr, Symbols::SymbolContainer::instance()->currentScopeName(), |
| | | this->current_filename_, returnToken.line_number, |
| | | returnToken.column_number); |
| | | // Consume terminating semicolon |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, ";"); |
| | | } |
| | | |
| | | // Parse a for-in loop over object members |
| | | void Parser::parseForStatement() { |
| | | // 'for' |
| | | auto forToken = expect(Lexer::Tokens::Type::KEYWORD, "for"); |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "("); |
| | | // Parse key type and name |
| | | Symbols::Variables::Type keyType = parseType(); |
| | | auto keyTok = expect(Lexer::Tokens::Type::VARIABLE_IDENTIFIER); |
| | | std::string keyName = keyTok.value; |
| | | if (!keyName.empty() && keyName[0] == '$') { |
| | | keyName = keyName.substr(1); |
| | | } |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, ","); |
| | | // Parse 'auto' keyword for value |
| | | if (!(currentToken().type == Lexer::Tokens::Type::IDENTIFIER && currentToken().value == "auto")) { |
| | | reportError("Expected 'auto' in for-in loop"); |
| | | } |
| | | consumeToken(); |
| | | auto valTok = expect(Lexer::Tokens::Type::VARIABLE_IDENTIFIER); |
| | | std::string valName = valTok.value; |
| | | if (!valName.empty() && valName[0] == '$') { |
| | | valName = valName.substr(1); |
| | | } |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, ":"); |
| | | // Parse iterable expression |
| | | auto iterableExpr = parseParsedExpression(Symbols::Variables::Type::NULL_TYPE); |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, ")"); |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "{"); |
| | | // Parse loop body |
| | | std::vector<std::unique_ptr<Interpreter::StatementNode>> body; |
| | | while (!(currentToken().type == Lexer::Tokens::Type::PUNCTUATION && currentToken().value == "}")) { |
| | | body.push_back(parseStatementNode()); |
| | | } |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "}"); |
| | | // Build expression and statement node |
| | | auto iterableExprNode = buildExpressionFromParsed(iterableExpr); |
| | | auto stmt = std::make_unique<Interpreter::ForStatementNode>(keyType, keyName, valName, std::move(iterableExprNode), |
| | | std::move(body), this->current_filename_, |
| | | forToken.line_number, forToken.column_number); |
| | | // Record loop operation |
| | | Operations::Container::instance()->add(Symbols::SymbolContainer::instance()->currentScopeName(), |
| | | Operations::Operation{ Operations::Type::Loop, "", std::move(stmt) }); |
| | | } |
| | | |
| | | // Continue with numeric literal parsing |
| | |
| | | // Expect ':' delimiter |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, ":"); |
| | | // Parse value expression (pass tag type if provided) |
| | | Symbols::Variables::Type expectType = (memberType == Symbols::Variables::Type::UNDEFINED_TYPE) |
| | | ? Symbols::Variables::Type::NULL_TYPE |
| | | : memberType; |
| | | auto valueExpr = parseParsedExpression(expectType); |
| | | Symbols::Variables::Type expectType = (memberType == Symbols::Variables::Type::UNDEFINED_TYPE) ? |
| | | Symbols::Variables::Type::NULL_TYPE : |
| | | memberType; |
| | | auto valueExpr = parseParsedExpression(expectType); |
| | | members.emplace_back(key, std::move(valueExpr)); |
| | | if (match(Lexer::Tokens::Type::PUNCTUATION, ",")) { |
| | | continue; |
| | |
| | | if (output_queue.size() < 2) { |
| | | Parser::reportError("Malformed expression", token); |
| | | } |
| | | auto rhs = std::move(output_queue.back()); output_queue.pop_back(); |
| | | auto lhs = std::move(output_queue.back()); output_queue.pop_back(); |
| | | auto rhs = std::move(output_queue.back()); |
| | | output_queue.pop_back(); |
| | | auto lhs = std::move(output_queue.back()); |
| | | output_queue.pop_back(); |
| | | output_queue.push_back(Lexer::applyOperator(op, std::move(rhs), std::move(lhs))); |
| | | } else { |
| | | break; |
| | |
| | | } else if (token.type == Lexer::Tokens::Type::PUNCTUATION && token.lexeme == ")") { |
| | | // Only handle grouping parentheses if a matching "(" exists on the operator stack |
| | | std::stack<std::string> temp_stack = operator_stack; |
| | | bool has_paren = false; |
| | | bool has_paren = false; |
| | | while (!temp_stack.empty()) { |
| | | if (temp_stack.top() == "(") { |
| | | has_paren = true; |
| | |
| | | // Create call expression node |
| | | output_queue.push_back(ParsedExpression::makeCall(func_name, std::move(call_args))); |
| | | expect_unary = false; |
| | | } else if (token.type == Lexer::Tokens::Type::OPERATOR_ARITHMETIC |
| | | || token.type == Lexer::Tokens::Type::OPERATOR_RELATIONAL |
| | | || token.type == Lexer::Tokens::Type::OPERATOR_LOGICAL) { |
| | | } else if (token.type == Lexer::Tokens::Type::OPERATOR_ARITHMETIC || |
| | | token.type == Lexer::Tokens::Type::OPERATOR_RELATIONAL || |
| | | token.type == Lexer::Tokens::Type::OPERATOR_LOGICAL) { |
| | | std::string op = std::string(token.lexeme); |
| | | |
| | | if (expect_unary && Lexer::isUnaryOperator(op)) { |
| | |
| | | operator_stack.push(op); |
| | | consumeToken(); |
| | | expect_unary = true; |
| | | } else if (token.type == Lexer::Tokens::Type::NUMBER |
| | | || token.type == Lexer::Tokens::Type::STRING_LITERAL |
| | | || token.type == Lexer::Tokens::Type::KEYWORD |
| | | || token.type == Lexer::Tokens::Type::VARIABLE_IDENTIFIER |
| | | || token.type == Lexer::Tokens::Type::IDENTIFIER) { |
| | | } else if (token.type == Lexer::Tokens::Type::NUMBER || token.type == Lexer::Tokens::Type::STRING_LITERAL || |
| | | token.type == Lexer::Tokens::Type::KEYWORD || |
| | | token.type == Lexer::Tokens::Type::VARIABLE_IDENTIFIER || |
| | | token.type == Lexer::Tokens::Type::IDENTIFIER) { |
| | | if (token.type == Lexer::Tokens::Type::IDENTIFIER) { |
| | | // Treat bare identifiers as variable references for member access |
| | | output_queue.push_back(ParsedExpression::makeVariable(token.value)); |
| | |
| | | reportError("Unexpected tokens after program end"); |
| | | } |
| | | } |
| | | |
| | | void Parser::parseStatement() { |
| | | const auto & token_type = currentToken().type; |
| | | // if-else conditional |
| | | if (token_type == Lexer::Tokens::Type::KEYWORD && currentToken().value == "if") { |
| | | parseIfStatement(); |
| | | return; |
| | | } |
| | | |
| | | if (token_type == Lexer::Tokens::Type::KEYWORD_FUNCTION_DECLARATION) { |
| | | parseFunctionDefinition(); |
| | | return; |
| | | } |
| | | // Return statement |
| | | if (token_type == Lexer::Tokens::Type::KEYWORD_RETURN) { |
| | | parseReturnStatement(); |
| | | return; |
| | | } |
| | | // For-in loop over object members |
| | | if (token_type == Lexer::Tokens::Type::KEYWORD && currentToken().value == "for") { |
| | | parseForStatement(); |
| | | return; |
| | | } |
| | | |
| | | // Variable definition if leading token matches a type keyword |
| | | if (Parser::variable_types.find(token_type) != Parser::variable_types.end()) { |
| | | parseVariableDefinition(); |
| | | return; |
| | | } |
| | | // Function call if identifier followed by '(' |
| | | if (currentToken().type == Lexer::Tokens::Type::IDENTIFIER && |
| | | peekToken().type == Lexer::Tokens::Type::PUNCTUATION && peekToken().value == "(") { |
| | | parseCallStatement(); |
| | | return; |
| | | } |
| | | // Assignment statement at top-level |
| | | if (currentToken().type == Lexer::Tokens::Type::VARIABLE_IDENTIFIER) { |
| | | size_t offset = 1; |
| | | // Skip member access chain |
| | | while (peekToken(offset).type == Lexer::Tokens::Type::PUNCTUATION && peekToken(offset).value == "->") { |
| | | offset += 2; |
| | | } |
| | | const auto & look = peekToken(offset); |
| | | if (look.type == Lexer::Tokens::Type::OPERATOR_ASSIGNMENT && look.value == "=") { |
| | | parseAssignmentStatement(); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | reportError("Unexpected token at beginning of statement"); |
| | | } |
| | | |
| | | Symbols::Variables::Type Parser::parseType() { |
| | | const auto & token = currentToken(); |
| | | // Direct lookup for type keyword |
| | | auto it = Parser::variable_types.find(token.type); |
| | | if (it != Parser::variable_types.end()) { |
| | | consumeToken(); |
| | | return it->second; |
| | | } |
| | | reportError("Expected type keyword (string, int, double, float)"); |
| | | } |
| | | |
| | | Symbols::Value Parser::parseValue(Symbols::Variables::Type expected_var_type) { |
| | | Lexer::Tokens::Token token = currentToken(); |
| | | bool is_negative = false; |
| | | |
| | | // Handle unary sign |
| | | if (token.type == Lexer::Tokens::Type::OPERATOR_ARITHMETIC && (token.lexeme == "-" || token.lexeme == "+") && |
| | | peekToken().type == Lexer::Tokens::Type::NUMBER) { |
| | | is_negative = (token.lexeme == "-"); |
| | | token = peekToken(); |
| | | consumeToken(); // consumed the sign |
| | | } |
| | | |
| | | // STRING type |
| | | if (expected_var_type == Symbols::Variables::Type::STRING) { |
| | | if (token.type == Lexer::Tokens::Type::STRING_LITERAL) { |
| | | consumeToken(); |
| | | return Symbols::Value(token.value); |
| | | } |
| | | reportError("Expected string literal value"); |
| | | } |
| | | |
| | | // BOOLEAN type |
| | | if (expected_var_type == Symbols::Variables::Type::BOOLEAN) { |
| | | if (token.type == Lexer::Tokens::Type::KEYWORD && (token.value == "true" || token.value == "false")) { |
| | | consumeToken(); |
| | | return Symbols::Value(token.value == "true"); |
| | | } |
| | | reportError("Expected boolean literal value (true or false)"); |
| | | } |
| | | |
| | | // NUMERIC types |
| | | if (expected_var_type == Symbols::Variables::Type::INTEGER || |
| | | expected_var_type == Symbols::Variables::Type::DOUBLE || expected_var_type == Symbols::Variables::Type::FLOAT) { |
| | | if (token.type == Lexer::Tokens::Type::NUMBER) { |
| | | Symbols::Value val = parseNumericLiteral(token.value, is_negative, expected_var_type); |
| | | consumeToken(); |
| | | return val; |
| | | } |
| | | |
| | | reportError("Expected numeric literal value"); |
| | | } |
| | | |
| | | reportError("Unsupported variable type encountered during value parsing"); |
| | | return Symbols::Value(); // compiler happy |
| | | } |
| | | |
| | | bool Parser::isAtEnd() const { |
| | | // We're at the end if the index equals the number of tokens, |
| | | // or if only the EOF token remains (as the last element) |
| | | return current_token_index_ >= tokens_.size() || |
| | | (current_token_index_ == tokens_.size() - 1 && tokens_.back().type == Lexer::Tokens::Type::END_OF_FILE); |
| | | } |
| | | |
| | | Lexer::Tokens::Token Parser::expect(Lexer::Tokens::Type expected_type, const std::string & expected_value) { |
| | | if (isAtEnd()) { |
| | | reportError("Unexpected end of file, expected token: " + Lexer::Tokens::TypeToString(expected_type) + |
| | | " with value '" + expected_value + "'"); |
| | | } |
| | | const auto & token = currentToken(); |
| | | if (token.type == expected_type && token.value == expected_value) { |
| | | return consumeToken(); |
| | | } |
| | | reportError("Expected token " + Lexer::Tokens::TypeToString(expected_type) + " with value '" + expected_value + |
| | | "'"); |
| | | return token; // reportError throws |
| | | } |
| | | |
| | | Lexer::Tokens::Token Parser::expect(Lexer::Tokens::Type expected_type) { |
| | | if (isAtEnd()) { |
| | | reportError("Unexpected end of file, expected token type: " + Lexer::Tokens::TypeToString(expected_type)); |
| | | } |
| | | const auto & token = currentToken(); |
| | | if (token.type == expected_type) { |
| | | return consumeToken(); |
| | | } |
| | | reportError("Expected token type " + Lexer::Tokens::TypeToString(expected_type)); |
| | | // reportError throws; this return is never reached, but may satisfy the compiler |
| | | return token; // or let reportError throw |
| | | } |
| | | |
| | | bool Parser::match(Lexer::Tokens::Type expected_type, const std::string & expected_value) { |
| | | if (isAtEnd()) { |
| | | return false; |
| | | } |
| | | const auto & token = currentToken(); |
| | | if (token.type == expected_type && token.value == expected_value) { |
| | | consumeToken(); |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | bool Parser::match(Lexer::Tokens::Type expected_type) { |
| | | if (isAtEnd()) { |
| | | return false; |
| | | } |
| | | if (currentToken().type == expected_type) { |
| | | consumeToken(); |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | Lexer::Tokens::Token Parser::consumeToken() { |
| | | if (isAtEnd()) { |
| | | throw std::runtime_error("Cannot consume token at end of stream."); |
| | | } |
| | | return tokens_[current_token_index_++]; |
| | | } |
| | | |
| | | const Lexer::Tokens::Token & Parser::peekToken(size_t offset) const { |
| | | if (current_token_index_ + offset >= tokens_.size()) { |
| | | // If at or beyond EOF, return the last token (should be EOF) |
| | | if (!tokens_.empty()) { |
| | | return tokens_.back(); |
| | | } |
| | | throw std::runtime_error("Cannot peek beyond end of token stream."); |
| | | } |
| | | return tokens_[current_token_index_ + offset]; |
| | | } |
| | | |
| | | const Lexer::Tokens::Token & Parser::currentToken() const { |
| | | if (isAtEnd()) { |
| | | // Technically we should never reach this if parseScript's loop is correct |
| | | // But it's useful as a safety check |
| | | if (!tokens_.empty() && tokens_.back().type == Lexer::Tokens::Type::END_OF_FILE) { |
| | | return tokens_.back(); // return the EOF token |
| | | } |
| | | throw std::runtime_error("Unexpected end of token stream reached."); |
| | | } |
| | | return tokens_[current_token_index_]; |
| | | } |
| | | } // namespace Parser |