From 17b3739f756a1e713704b22cce89307308cea2d8 Mon Sep 17 00:00:00 2001
From: Ferenc Szontágh <szf@fsociety.hu>
Date: Fri, 18 Apr 2025 20:33:13 +0000
Subject: [PATCH] add else if to the if
---
src/Parser/Parser.cpp | 459 +++++++++++++++++++++++++++-----
src/Interpreter/ForStatementNode.hpp | 91 ++++++
src/Parser/Parser.hpp | 225 +--------------
test_scripts/object.vs | 9
src/Interpreter/Interpreter.hpp | 10
5 files changed, 526 insertions(+), 268 deletions(-)
diff --git a/src/Interpreter/ForStatementNode.hpp b/src/Interpreter/ForStatementNode.hpp
new file mode 100644
index 0000000..6d33723
--- /dev/null
+++ b/src/Interpreter/ForStatementNode.hpp
@@ -0,0 +1,91 @@
+ #ifndef INTERPRETER_FOR_STATEMENT_NODE_HPP
+ #define INTERPRETER_FOR_STATEMENT_NODE_HPP
+
+ #include <vector>
+ #include <memory>
+ #include <string>
+ #include <stdexcept>
+ #include "Interpreter/StatementNode.hpp"
+ #include "Interpreter/ExpressionNode.hpp"
+ #include "Symbols/Value.hpp"
+ #include "Symbols/SymbolContainer.hpp"
+ #include "Symbols/SymbolFactory.hpp"
+
+ namespace Interpreter {
+
+ /**
+ * @brief Statement node representing a for-in loop over object members.
+ */
+ class ForStatementNode : public StatementNode {
+ private:
+ Symbols::Variables::Type keyType_;
+ std::string keyName_;
+ std::string valueName_;
+ std::unique_ptr<ExpressionNode> iterableExpr_;
+ std::vector<std::unique_ptr<StatementNode>> body_;
+
+ public:
+ ForStatementNode(Symbols::Variables::Type keyType,
+ std::string keyName,
+ std::string valueName,
+ std::unique_ptr<ExpressionNode> iterableExpr,
+ std::vector<std::unique_ptr<StatementNode>> body,
+ const std::string & file_name,
+ int line,
+ size_t column)
+ : StatementNode(file_name, line, column),
+ keyType_(keyType),
+ keyName_(std::move(keyName)),
+ valueName_(std::move(valueName)),
+ iterableExpr_(std::move(iterableExpr)),
+ body_(std::move(body)) {}
+
+ void interpret(Interpreter & interpreter) const override {
+ using namespace Symbols;
+ // Evaluate iterable expression
+ auto iterableVal = iterableExpr_->evaluate(interpreter);
+ if (iterableVal.getType() != Variables::Type::OBJECT) {
+ throw std::runtime_error("For-in loop applied to non-object at " + filename_ + ":" + std::to_string(line_) +
+ "," + std::to_string(column_));
+ }
+ // Access underlying object map
+ const auto & objMap = std::get<Value::ObjectMap>(iterableVal.get());
+ auto * symContainer = SymbolContainer::instance();
+ const std::string base_ns = symContainer->currentScopeName();
+ const std::string var_ns = base_ns + ".variables";
+ // Iterate through object entries
+ for (const auto & entry : objMap) {
+ // Key binding
+ const std::string & key = entry.first;
+ Value keyVal(key);
+ if (!symContainer->exists(keyName_, var_ns)) {
+ auto sym = SymbolFactory::createVariable(keyName_, keyVal, base_ns);
+ symContainer->add(sym);
+ } else {
+ auto sym = symContainer->get(var_ns, keyName_);
+ sym->setValue(keyVal);
+ }
+ // Value binding
+ Value valVal = entry.second;
+ if (!symContainer->exists(valueName_, var_ns)) {
+ auto sym = SymbolFactory::createVariable(valueName_, valVal, base_ns);
+ symContainer->add(sym);
+ } else {
+ auto sym = symContainer->get(var_ns, valueName_);
+ sym->setValue(valVal);
+ }
+ // Execute loop body
+ for (const auto & stmt : body_) {
+ stmt->interpret(interpreter);
+ }
+ }
+ }
+
+ std::string toString() const override {
+ return "ForStatementNode at " + filename_ + ":" + std::to_string(line_);
+ }
+ };
+
+} // namespace Interpreter
+
+#endif // INTERPRETER_FOR_STATEMENT_NODE_HPP
\ No newline at end of file
diff --git a/src/Interpreter/Interpreter.hpp b/src/Interpreter/Interpreter.hpp
index 328d6af..e67443b 100644
--- a/src/Interpreter/Interpreter.hpp
+++ b/src/Interpreter/Interpreter.hpp
@@ -72,7 +72,17 @@
}
break;
case Operations::Type::Return:
+ // return statement
+ if (op.statement) {
+ op.statement->interpret(*this);
+ }
+ break;
case Operations::Type::Loop:
+ // for-in or while loop
+ if (op.statement) {
+ op.statement->interpret(*this);
+ }
+ break;
case Operations::Type::Break:
case Operations::Type::Continue:
case Operations::Type::Block:
diff --git a/src/Parser/Parser.cpp b/src/Parser/Parser.cpp
index b2bebf5..c802a9d 100644
--- a/src/Parser/Parser.cpp
+++ b/src/Parser/Parser.cpp
@@ -5,14 +5,14 @@
#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 {
@@ -72,10 +72,8 @@
// 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
@@ -94,39 +92,119 @@
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) {
@@ -139,9 +217,11 @@
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, "->")) {
@@ -154,32 +234,36 @@
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;
}
}
@@ -187,25 +271,27 @@
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;
@@ -286,33 +372,70 @@
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
@@ -440,10 +563,10 @@
// 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;
@@ -472,8 +595,10 @@
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;
@@ -491,7 +616,7 @@
} 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;
@@ -558,9 +683,9 @@
// 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)) {
@@ -598,11 +723,10 @@
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));
@@ -669,4 +793,199 @@
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
diff --git a/src/Parser/Parser.hpp b/src/Parser/Parser.hpp
index b8656e5..ed08bde 100644
--- a/src/Parser/Parser.hpp
+++ b/src/Parser/Parser.hpp
@@ -47,7 +47,6 @@
void parseScript(const std::vector<Lexer::Tokens::Token> & tokens, std::string_view input_string,
const std::string & filename);
-
static const std::unordered_map<std::string, Lexer::Tokens::Type> keywords;
static const std::unordered_map<Lexer::Tokens::Type, Symbols::Variables::Type> variable_types;
@@ -58,100 +57,29 @@
std::string current_filename_;
// Token stream handling and error-reporting helper functions (unchanged)
- const Lexer::Tokens::Token & 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_];
- }
+ const Lexer::Tokens::Token & currentToken() const;
// Look ahead in the token stream
- const Lexer::Tokens::Token & peekToken(size_t offset = 1) 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 & peekToken(size_t offset = 1) const;
// Consume (advance past) the current token and return it
- Lexer::Tokens::Token consumeToken() {
- if (isAtEnd()) {
- throw std::runtime_error("Cannot consume token at end of stream.");
- }
- return tokens_[current_token_index_++];
- }
+ Lexer::Tokens::Token consumeToken();
// Check if current token type matches the expected type
// If so, consume it and return true; otherwise return false
- bool match(Lexer::Tokens::Type expected_type) {
- if (isAtEnd()) {
- return false;
- }
- if (currentToken().type == expected_type) {
- consumeToken();
- return true;
- }
- return false;
- }
+ bool match(Lexer::Tokens::Type expected_type);
// Check if current token type and value match the expected ones
// Only use value checking for operators and punctuation
- bool 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 match(Lexer::Tokens::Type expected_type, const std::string & expected_value);
- Lexer::Tokens::Token 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
- }
+ Lexer::Tokens::Token expect(Lexer::Tokens::Type expected_type);
// Like expect, but also checks the token's value
- Lexer::Tokens::Token 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 expect(Lexer::Tokens::Type expected_type, const std::string & expected_value);
// Check if we've reached the end of relevant tokens (just before EOF)
- bool 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);
- }
+ bool isAtEnd() const;
[[noreturn]] void reportError(const std::string & message, const std::string & expected = "") {
if (current_token_index_ < tokens_.size()) {
@@ -168,64 +96,23 @@
}
// parseStatement (updated to handle return)
- void 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;
- }
-
- // 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");
- }
-
- void parseVariableDefinition();
- void parseFunctionDefinition();
+ void parseStatement();
+ void parseVariableDefinition();
+ void parseFunctionDefinition();
// Parse a top-level function call statement (e.g., foo(arg1, arg2);)
- void parseCallStatement();
+ void parseCallStatement();
// Parse a top-level assignment statement (variable or object member)
- void parseAssignmentStatement();
+ void parseAssignmentStatement();
// Parse a return statement (e.g., return; or return expr;)
- void parseReturnStatement();
- // Parse an if-else conditional statement
- void parseIfStatement();
- // Parse a for-in loop over object members
- void parseForStatement();
+ void parseReturnStatement();
+ // Parse an if-else conditional statement (at top-level)
+ void parseIfStatement();
+ // Parse a for-in loop over object members (at top-level)
+ void parseForStatement();
+ // Parse an if-else conditional block and return a StatementNode (for nested blocks)
+ std::unique_ptr<Interpreter::StatementNode> parseIfStatementNode();
+ // Parse a for-in loop over object members and return a StatementNode (for nested blocks)
+ std::unique_ptr<Interpreter::StatementNode> parseForStatementNode();
// Parse a statement node for use inside blocks (not added to operation container)
std::unique_ptr<Interpreter::StatementNode> parseStatementNode();
@@ -233,69 +120,11 @@
// type : KEYWORD_STRING | KEYWORD_INT | KEYWORD_DOUBLE
// Returns the corresponding Symbols::Variables::Type enum and consumes the token
- Symbols::Variables::Type 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 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
- }
-
- Symbols::Value parseNumericLiteral(const std::string & value, bool is_negative, Symbols::Variables::Type type);
-
- void parseFunctionBody(const Lexer::Tokens::Token & opening_brace, const std::string & function_name,
- Symbols::Variables::Type return_type, const Symbols::FunctionParameterInfo & params);
-
+ Symbols::Variables::Type parseType();
+ Symbols::Value parseValue(Symbols::Variables::Type expected_var_type);
+ Symbols::Value parseNumericLiteral(const std::string & value, bool is_negative, Symbols::Variables::Type type);
+ void parseFunctionBody(const Lexer::Tokens::Token & opening_brace, const std::string & function_name,
+ Symbols::Variables::Type return_type, const Symbols::FunctionParameterInfo & params);
ParsedExpressionPtr parseParsedExpression(const Symbols::Variables::Type & expected_var_type);
}; // class Parser
diff --git a/test_scripts/object.vs b/test_scripts/object.vs
index cd88c71..753bc57 100644
--- a/test_scripts/object.vs
+++ b/test_scripts/object.vs
@@ -33,4 +33,13 @@
printnl("Child1 is old enough to go to school.");
} else {
printnl("Child1 is too young to go to school.");
+}
+
+for (string $key, auto $value : $person2) {
+
+ if (typeof($value,"object") == false) {
+ printnl("Key: ", $key, " Value: ", $value);
+ }else {
+ printnl("Key: ", $key, " is an object");
+ }
}
\ No newline at end of file
--
Gitblit v1.9.3