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