From fb8d8f9f5bb4a1f7736d927a346d4bf834a28ffa Mon Sep 17 00:00:00 2001
From: Ferenc Szontágh <szf@fsociety.hu>
Date: Fri, 18 Apr 2025 17:25:48 +0000
Subject: [PATCH] add if else statements

---
 src/Parser/Parser.cpp                        |   97 +++++++++++++++++++
 src/VoidScript.hpp                           |    3 
 tmp.vs                                       |    4 
 test_scripts/if_statements.vs                |   21 ++++
 src/Interpreter/BinaryExpressionNode.hpp     |    7 +
 src/Parser/Parser.hpp                        |   10 ++
 src/Interpreter/ConditionalStatementNode.hpp |   57 +++++++++++
 test_scripts/object.vs                       |    4 
 src/Modules/TypeofModule.hpp                 |   45 +++++++++
 src/Interpreter/Interpreter.hpp              |    7 +
 test_scripts/arguments.vs                    |    4 
 test_scripts/typeof.vs                       |    7 +
 12 files changed, 264 insertions(+), 2 deletions(-)

diff --git a/src/Interpreter/BinaryExpressionNode.hpp b/src/Interpreter/BinaryExpressionNode.hpp
index 3281aab..fdf8d9c 100644
--- a/src/Interpreter/BinaryExpressionNode.hpp
+++ b/src/Interpreter/BinaryExpressionNode.hpp
@@ -94,7 +94,12 @@
             if (op_ == "+") {
                 return Symbols::Value(l + r);
             }
-
+            if (op_ == "==") {
+                return Symbols::Value(l == r);
+            }
+            if (op_ == "!=") {
+                return Symbols::Value(l != r);
+            }
             throw std::runtime_error("Unknown operator: " + op_);
         }
 
diff --git a/src/Interpreter/ConditionalStatementNode.hpp b/src/Interpreter/ConditionalStatementNode.hpp
new file mode 100644
index 0000000..254f4a9
--- /dev/null
+++ b/src/Interpreter/ConditionalStatementNode.hpp
@@ -0,0 +1,57 @@
+ #ifndef INTERPRETER_CONDITIONAL_STATEMENT_NODE_HPP
+ #define INTERPRETER_CONDITIONAL_STATEMENT_NODE_HPP
+
+ #include <vector>
+ #include <memory>
+ #include <string>
+ #include "Interpreter/StatementNode.hpp"
+ #include "Interpreter/ExpressionNode.hpp"
+
+ namespace Interpreter {
+
+ /**
+  * @brief Statement node representing an if-else conditional block.
+  */
+ class ConditionalStatementNode : public StatementNode {
+     std::unique_ptr<ExpressionNode> condition_;
+     std::vector<std::unique_ptr<StatementNode>> thenBranch_;
+     std::vector<std::unique_ptr<StatementNode>> elseBranch_;
+
+   public:
+     ConditionalStatementNode(
+         std::unique_ptr<ExpressionNode> condition,
+         std::vector<std::unique_ptr<StatementNode>> thenBranch,
+         std::vector<std::unique_ptr<StatementNode>> elseBranch,
+         const std::string & file_name,
+         int line,
+         size_t column
+     ) : StatementNode(file_name, line, column),
+         condition_(std::move(condition)),
+         thenBranch_(std::move(thenBranch)),
+         elseBranch_(std::move(elseBranch)) {}
+
+     void interpret(class Interpreter & interpreter) const override {
+         // Evaluate condition
+         auto val = condition_->evaluate(interpreter);
+         bool cond = false;
+         if (val.getType() == Symbols::Variables::Type::BOOLEAN) {
+             cond = val.get<bool>();
+         } else {
+             throw std::runtime_error("Condition did not evaluate to boolean at " + filename_ +
+                                      ":" + std::to_string(line_) + "," + std::to_string(column_));
+         }
+         // Execute appropriate branch
+         const auto & branch = cond ? thenBranch_ : elseBranch_;
+         for (const auto & stmt : branch) {
+             stmt->interpret(interpreter);
+         }
+     }
+
+     std::string toString() const override {
+         return "ConditionalStatementNode at " + filename_ + ":" + std::to_string(line_);
+     }
+ };
+
+ } // namespace Interpreter
+
+ #endif // INTERPRETER_CONDITIONAL_STATEMENT_NODE_HPP
\ No newline at end of file
diff --git a/src/Interpreter/Interpreter.hpp b/src/Interpreter/Interpreter.hpp
index fe18030..328d6af 100644
--- a/src/Interpreter/Interpreter.hpp
+++ b/src/Interpreter/Interpreter.hpp
@@ -65,6 +65,12 @@
                     op.statement->interpret(*this);
                 }
                 break;
+            case Operations::Type::Conditional:
+                // if-else conditional block
+                if (op.statement) {
+                    op.statement->interpret(*this);
+                }
+                break;
             case Operations::Type::Return:
             case Operations::Type::Loop:
             case Operations::Type::Break:
@@ -72,7 +78,6 @@
             case Operations::Type::Block:
             case Operations::Type::Import:
             case Operations::Type::Error:
-            case Operations::Type::Conditional:
                 // TODO: implement these operations later
                 break;
             default:
diff --git a/src/Modules/TypeofModule.hpp b/src/Modules/TypeofModule.hpp
new file mode 100644
index 0000000..f38cd54
--- /dev/null
+++ b/src/Modules/TypeofModule.hpp
@@ -0,0 +1,45 @@
+// TypeofModule.hpp
+#ifndef MODULES_TYPEOFMODULE_HPP
+#define MODULES_TYPEOFMODULE_HPP
+
+#include <string>
+#include <vector>
+#include "BaseModule.hpp"
+#include "ModuleManager.hpp"
+#include "Symbols/Value.hpp"
+#include "Symbols/VariableTypes.hpp"
+
+namespace Modules {
+
+/**
+ * @brief Module providing a typeof() function.
+ * Usage:
+ *   typeof($var)            -> returns string name of type ("int", "string", etc.)
+ *   typeof($var, "int")   -> returns bool indicating if type matches
+ */
+class TypeofModule : public BaseModule {
+  public:
+    void registerModule() override {
+        auto &mgr = ModuleManager::instance();
+        mgr.registerFunction("typeof", [](const std::vector<Symbols::Value> &args) {
+            using namespace Symbols;
+            if (args.size() == 1) {
+                auto t = args[0].getType();
+                return Value(Variables::TypeToString(t));
+            } else if (args.size() == 2) {
+                auto t = args[0].getType();
+                std::string name = Variables::TypeToString(t);
+                if (args[1].getType() != Variables::Type::STRING) {
+                    throw std::runtime_error("Second argument to typeof must be string");
+                }
+                bool match = (name == args[1].get<std::string>());
+                return Value(match);
+            }
+            throw std::runtime_error("typeof expects 1 or 2 arguments");
+        });
+    }
+};
+
+} // namespace Modules
+
+#endif // MODULES_TYPEOFMODULE_HPP
\ No newline at end of file
diff --git a/src/Parser/Parser.cpp b/src/Parser/Parser.cpp
index 7e99d37..e1e9776 100644
--- a/src/Parser/Parser.cpp
+++ b/src/Parser/Parser.cpp
@@ -4,6 +4,13 @@
 
 #include "Interpreter/OperationsFactory.hpp"
 #include "Lexer/Operators.hpp"
+// Statements and expression building for conditional and block parsing
+#include "Interpreter/ConditionalStatementNode.hpp"
+#include "Interpreter/CallStatementNode.hpp"
+#include "Interpreter/DeclareVariableStatementNode.hpp"
+#include "Interpreter/ReturnStatementNode.hpp"
+#include "Symbols/SymbolContainer.hpp"
+#include "Interpreter/ExpressionBuilder.hpp"
 
 // Additional necessary includes, if needed
 namespace Parser {
@@ -58,6 +65,96 @@
     expect(Lexer::Tokens::Type::PUNCTUATION, ";");
 }
 
+// Parse an if-else conditional statement
+void Parser::parseIfStatement() {
+    // 'if'
+    auto ifToken = expect(Lexer::Tokens::Type::KEYWORD, "if");
+    expect(Lexer::Tokens::Type::PUNCTUATION, "(");
+    auto condExpr = parseParsedExpression(Symbols::Variables::Type::BOOLEAN);
+    expect(Lexer::Tokens::Type::PUNCTUATION, ")");
+    expect(Lexer::Tokens::Type::PUNCTUATION, "{");
+    // then branch
+    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, "}");
+    // else 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());
+        }
+        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);
+    // add conditional operation
+    Operations::Container::instance()->add(
+        Symbols::SymbolContainer::instance()->currentScopeName(),
+        Operations::Operation{Operations::Type::Conditional, "", std::move(stmt)});
+}
+
+// Parse a single statement and return its StatementNode (for use in blocks)
+std::unique_ptr<Interpreter::StatementNode> Parser::parseStatementNode() {
+    // Return statement
+    if (currentToken().type == 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);
+    }
+    // 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);
+        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;
+                break;
+            }
+        }
+        expect(Lexer::Tokens::Type::PUNCTUATION, ")");
+        expect(Lexer::Tokens::Type::PUNCTUATION, ";");
+        std::vector<std::unique_ptr<Interpreter::ExpressionNode>> exprs;
+        exprs.reserve(args.size());
+        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);
+    }
+    // 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);
+        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);
+    }
+    reportError("Unexpected token in block");
+    return nullptr;
+}
+
 void Parser::parseFunctionDefinition() {
     expect(Lexer::Tokens::Type::KEYWORD_FUNCTION_DECLARATION);
     Lexer::Tokens::Token     id_token         = expect(Lexer::Tokens::Type::IDENTIFIER);
diff --git a/src/Parser/Parser.hpp b/src/Parser/Parser.hpp
index 0eb1180..d2c826c 100644
--- a/src/Parser/Parser.hpp
+++ b/src/Parser/Parser.hpp
@@ -6,6 +6,7 @@
 #include <vector>
 
 #include "BaseException.hpp"
+#include "Interpreter/StatementNode.hpp"
 #include "Lexer/Token.hpp"
 #include "Lexer/TokenType.hpp"
 #include "Parser/ParsedExpression.hpp"
@@ -169,6 +170,11 @@
     // 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();
@@ -201,6 +207,10 @@
     void parseCallStatement();
     // Parse a return statement (e.g., return; or return expr;)
     void parseReturnStatement();
+    // Parse an if-else conditional statement
+    void parseIfStatement();
+    // Parse a statement node for use inside blocks (not added to operation container)
+    std::unique_ptr<Interpreter::StatementNode> parseStatementNode();
 
     // --- Parsing helper functions ---
 
diff --git a/src/VoidScript.hpp b/src/VoidScript.hpp
index 6ed0e02..8bbf24d 100644
--- a/src/VoidScript.hpp
+++ b/src/VoidScript.hpp
@@ -9,6 +9,7 @@
 #include "Modules/ModuleManager.hpp"
 #include "Modules/PrintNlModule.hpp"
 #include "Modules/PrintModule.hpp"
+#include "Modules/TypeofModule.hpp"
 #include "Parser/Parser.hpp"
 
 class VoidScript {
@@ -54,6 +55,8 @@
         // Register built-in modules (print, etc.)
         Modules::ModuleManager::instance().addModule(std::make_unique<Modules::PrintModule>());
         Modules::ModuleManager::instance().addModule(std::make_unique<Modules::PrintNlModule>());
+        // typeof() builtin
+        Modules::ModuleManager::instance().addModule(std::make_unique<Modules::TypeofModule>());
         this->files.emplace(this->files.begin(), file);
 
         lexer->setKeyWords(Parser::Parser::keywords);
diff --git a/test_scripts/arguments.vs b/test_scripts/arguments.vs
new file mode 100644
index 0000000..235482d
--- /dev/null
+++ b/test_scripts/arguments.vs
@@ -0,0 +1,4 @@
+if ($argc > 1) {
+    printnl("Hello,", $argv[1]);
+
+}
\ No newline at end of file
diff --git a/test_scripts/if_statements.vs b/test_scripts/if_statements.vs
new file mode 100644
index 0000000..42ddec5
--- /dev/null
+++ b/test_scripts/if_statements.vs
@@ -0,0 +1,21 @@
+string $test = "Test string";
+
+
+if ($test == "Test string") {
+    printnl("Test passed");
+} else {
+    printnl("Test failed");
+}
+
+
+bool $this_is_okay = true;
+bool $this_is_not_okay = false;
+
+
+if ($this_is_okay && $this_is_not_okay == false && $test == "Test string") {
+    printnl("This is okay");
+}
+
+if ($this_is_okay == false) {
+    printnl("This is not okay");
+}
\ No newline at end of file
diff --git a/test_scripts/object.vs b/test_scripts/object.vs
new file mode 100644
index 0000000..fa6d218
--- /dev/null
+++ b/test_scripts/object.vs
@@ -0,0 +1,4 @@
+object $person = {
+    name: "Szoni",
+    age: 37
+};
\ No newline at end of file
diff --git a/test_scripts/typeof.vs b/test_scripts/typeof.vs
new file mode 100644
index 0000000..4eccd9c
--- /dev/null
+++ b/test_scripts/typeof.vs
@@ -0,0 +1,7 @@
+string $var1 = "Hello";
+printnl(typeof($var1));
+printnl("This is string: ", typeof($var1,"string"));
+
+printnl("This is string: ", typeof($var1,"bool"));
+
+printnl("This is a \"something\": ", typeof($var1,"something"));
\ No newline at end of file
diff --git a/tmp.vs b/tmp.vs
new file mode 100644
index 0000000..b29f906
--- /dev/null
+++ b/tmp.vs
@@ -0,0 +1,4 @@
+int $x = 5;
+printnl(typeof($x));
+printnl(typeof($x, "int"));
+printnl(typeof($x, "string"));

--
Gitblit v1.9.3