From 558e0191ba5a5b0ab99825de7d7d2219387e559e Mon Sep 17 00:00:00 2001
From: Ferenc Szontágh <szf@fsociety.hu>
Date: Sat, 19 Apr 2025 18:36:42 +0000
Subject: [PATCH] constats variable type implementation

---
 src/Parser/Parser.cpp                            |   63 ++++++++++++++++++++-
 test_scripts/constants_object.vs                 |   17 +++++
 test_scripts/constants.vs                        |   19 ++++++
 src/Parser/Parser.hpp                            |    3 +
 src/Interpreter/OperationsFactory.hpp            |   14 ++++
 src/Interpreter/AssignmentStatementNode.hpp      |   10 ++
 src/Interpreter/IdentifierExpressionNode.hpp     |   14 +++-
 src/Interpreter/DeclareVariableStatementNode.hpp |   17 ++++-
 8 files changed, 144 insertions(+), 13 deletions(-)

diff --git a/src/Interpreter/AssignmentStatementNode.hpp b/src/Interpreter/AssignmentStatementNode.hpp
index c5eeabc..bfa3b3e 100644
--- a/src/Interpreter/AssignmentStatementNode.hpp
+++ b/src/Interpreter/AssignmentStatementNode.hpp
@@ -35,8 +35,14 @@
         using namespace Symbols;
         auto * symContainer = SymbolContainer::instance();
         // Variables are stored under <scope>.variables
-        const std::string base_ns = symContainer->currentScopeName();
-        const std::string var_ns = base_ns + ".variables";
+        const std::string base_ns   = symContainer->currentScopeName();
+        const std::string var_ns    = base_ns + ".variables";
+        const std::string const_ns  = base_ns + ".constants";
+        // Prevent assignment to constants
+        if (symContainer->exists(targetName_, const_ns)) {
+            throw Exception(
+                "Cannot assign to constant '" + targetName_ + "'", filename_, line_, column_);
+        }
         if (!symContainer->exists(targetName_, var_ns)) {
             throw Exception(
                 "Variable '" + targetName_ + "' does not exist in namespace: " + var_ns,
diff --git a/src/Interpreter/DeclareVariableStatementNode.hpp b/src/Interpreter/DeclareVariableStatementNode.hpp
index 4b4fe5d..e2fbe54 100644
--- a/src/Interpreter/DeclareVariableStatementNode.hpp
+++ b/src/Interpreter/DeclareVariableStatementNode.hpp
@@ -20,17 +20,20 @@
     Symbols::Variables::Type        variableType_;
     std::unique_ptr<ExpressionNode> expression_;
     std::string                     ns;
+    bool                            isConst_;
 
 
   public:
+    // isConst: if true, declares a constant; otherwise a mutable variable
     DeclareVariableStatementNode(std::string name, const std::string & ns, Symbols::Variables::Type type,
-                                 std::unique_ptr<ExpressionNode> expr, const std::string & file_name, int file_line,
-                                 size_t line_column) :
+                                 std::unique_ptr<ExpressionNode> expr, const std::string & file_name,
+                                 int file_line, size_t line_column, bool isConst = false) :
         StatementNode(file_name, file_line, line_column),
         variableName_(std::move(name)),
         variableType_(type),
         expression_(std::move(expr)),
-        ns(ns) {}
+        ns(ns),
+        isConst_(isConst) {}
 
     void interpret(Interpreter & interpreter) const override {
         try {
@@ -47,7 +50,13 @@
                     "': expected '" + expected + "' but got '" + actual + "'",
                     filename_, line_, column_);
             }
-            const auto variable = Symbols::SymbolFactory::createVariable(variableName_, value, ns, variableType_);
+            // Create a constant or variable symbol
+            std::shared_ptr<Symbols::Symbol> variable;
+            if (isConst_) {
+                variable = Symbols::SymbolFactory::createConstant(variableName_, value, ns);
+            } else {
+                variable = Symbols::SymbolFactory::createVariable(variableName_, value, ns, variableType_);
+            }
             Symbols::SymbolContainer::instance()->add(variable);
         } catch (const Exception &) {
             throw;
diff --git a/src/Interpreter/IdentifierExpressionNode.hpp b/src/Interpreter/IdentifierExpressionNode.hpp
index c610f77..1846a9d 100644
--- a/src/Interpreter/IdentifierExpressionNode.hpp
+++ b/src/Interpreter/IdentifierExpressionNode.hpp
@@ -14,11 +14,17 @@
     explicit IdentifierExpressionNode(std::string name) : name_(std::move(name)) {}
 
     Symbols::Value evaluate(Interpreter & /*interpreter*/) const override {
-        const auto ns = Symbols::SymbolContainer::instance()->currentScopeName() + ".variables";
-        if (Symbols::SymbolContainer::instance()->exists(name_, ns)) {
-            return Symbols::SymbolContainer::instance()->get(ns, name_)->getValue();
+        auto * sc = Symbols::SymbolContainer::instance();
+        const std::string base_ns  = sc->currentScopeName();
+        const std::string var_ns   = base_ns + ".variables";
+        if (sc->exists(name_, var_ns)) {
+            return sc->get(var_ns, name_)->getValue();
         }
-        throw std::runtime_error("Variable " + name_ + " does not exist in ns: " + ns);
+        const std::string const_ns = base_ns + ".constants";
+        if (sc->exists(name_, const_ns)) {
+            return sc->get(const_ns, name_)->getValue();
+        }
+        throw std::runtime_error("Identifier '" + name_ + "' not found in namespace: " + base_ns);
     }
 
     std::string toString() const override { return name_; }
diff --git a/src/Interpreter/OperationsFactory.hpp b/src/Interpreter/OperationsFactory.hpp
index f69bb14..8965f29 100644
--- a/src/Interpreter/OperationsFactory.hpp
+++ b/src/Interpreter/OperationsFactory.hpp
@@ -54,6 +54,20 @@
                                               Operations::Container::instance()->add(
                                                   ns, Operations::Operation{Operations::Type::Declaration, varName, std::move(stmt)});
                                           }
+    /**
+     * @brief Record a constant declaration operation with an initializer expression.
+     */
+    static void defineConstantWithExpression(const std::string & varName, Symbols::Variables::Type type,
+                                             const Parser::ParsedExpressionPtr pexpr, const std::string & ns,
+                                             const std::string & filename, int line, size_t column) {
+        // Build initializer expression
+        std::unique_ptr<ExpressionNode> expr = buildExpressionFromParsed(pexpr);
+        // Create declaration node with const flag
+        std::unique_ptr<DeclareVariableStatementNode> stmt = std::make_unique<DeclareVariableStatementNode>(
+            varName, ns, type, std::move(expr), filename, line, column, /* isConst */ true);
+        Operations::Container::instance()->add(
+            ns, Operations::Operation{Operations::Type::Declaration, varName, std::move(stmt)});
+    }
     
     /**
      * @brief Record a function call operation with argument expressions.
diff --git a/src/Parser/Parser.cpp b/src/Parser/Parser.cpp
index 7eabca3..d475d46 100644
--- a/src/Parser/Parser.cpp
+++ b/src/Parser/Parser.cpp
@@ -53,6 +53,35 @@
     { Lexer::Tokens::Type::KEYWORD_OBJECT,  Symbols::Variables::Type::OBJECT    },
 };
 
+// Parse a top-level constant variable definition: const <type> $name = expr;
+void Parser::parseConstVariableDefinition() {
+    // 'const'
+    auto constTok = expect(Lexer::Tokens::Type::KEYWORD, "const");
+    // Parse type
+    Symbols::Variables::Type var_type = parseType();
+    // Variable name
+    Lexer::Tokens::Token id_token;
+    if (currentToken().type == Lexer::Tokens::Type::VARIABLE_IDENTIFIER ||
+        currentToken().type == Lexer::Tokens::Type::IDENTIFIER) {
+        id_token = consumeToken();
+    } else {
+        reportError("Expected variable name after 'const'", currentToken());
+    }
+    std::string var_name = id_token.value;
+    if (!var_name.empty() && var_name[0] == '$') {
+        var_name = var_name.substr(1);
+    }
+    const auto ns = Symbols::SymbolContainer::instance()->currentScopeName();
+    // Expect assignment
+    expect(Lexer::Tokens::Type::OPERATOR_ASSIGNMENT, "=");
+    // Parse initializer expression
+    auto expr = parseParsedExpression(var_type);
+    // Record constant definition
+    Interpreter::OperationsFactory::defineConstantWithExpression(
+        var_name, var_type, std::move(expr), ns, current_filename_, id_token.line_number, id_token.column_number);
+    expect(Lexer::Tokens::Type::PUNCTUATION, ";");
+}
+
 void Parser::parseVariableDefinition() {
     Symbols::Variables::Type var_type = parseType();
 
@@ -365,11 +394,30 @@
         return std::make_unique<Interpreter::CallStatementNode>(funcName, std::move(exprs), this->current_filename_,
                                                                 idTok.line_number, idTok.column_number);
     }
+    // Constant variable declaration in blocks
+    if (currentToken().type == Lexer::Tokens::Type::KEYWORD && currentToken().value == "const") {
+        // 'const'
+        auto constTok = expect(Lexer::Tokens::Type::KEYWORD, "const");
+        // Parse type
+        Symbols::Variables::Type type = parseType();
+        // Variable name
+        auto idTok = expect(Lexer::Tokens::Type::VARIABLE_IDENTIFIER);
+        std::string name = idTok.value;
+        if (!name.empty() && name[0] == '$') name = name.substr(1);
+        // Assignment
+        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, /* isConst */ true);
+    }
     // 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;
+        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);
         }
@@ -381,9 +429,13 @@
             name, Symbols::SymbolContainer::instance()->currentScopeName(), type, std::move(exprNode),
             this->current_filename_, idTok.line_number, idTok.column_number);
     }
+    // Unexpected token in block
     reportError("Unexpected token in block");
     return nullptr;
 }
+// End of parseStatementNode
+// Parse next definition or statement
+// (Subsequent methods follow)
 
 void Parser::parseFunctionDefinition() {
     expect(Lexer::Tokens::Type::KEYWORD_FUNCTION_DECLARATION);
@@ -1015,6 +1067,11 @@
         return;
     }
 
+    // Constant variable definition
+    if (token_type == Lexer::Tokens::Type::KEYWORD && currentToken().value == "const") {
+        parseConstVariableDefinition();
+        return;
+    }
     // Variable definition if leading token matches a type keyword
     if (Parser::variable_types.find(token_type) != Parser::variable_types.end()) {
         parseVariableDefinition();
diff --git a/src/Parser/Parser.hpp b/src/Parser/Parser.hpp
index 0acedf7..14d07a3 100644
--- a/src/Parser/Parser.hpp
+++ b/src/Parser/Parser.hpp
@@ -100,6 +100,9 @@
 
     // parseStatement (updated to handle return)
     void                                        parseStatement();
+    // Parse a top-level constant variable definition (e.g., const <type> $name = expr;)
+    void                                        parseConstVariableDefinition();
+    // Parse a top-level variable definition (e.g., <type> $name = expr;)
     void                                        parseVariableDefinition();
     void                                        parseFunctionDefinition();
     // Parse a top-level function call statement (e.g., foo(arg1, arg2);)
diff --git a/test_scripts/constants.vs b/test_scripts/constants.vs
new file mode 100644
index 0000000..3c55460
--- /dev/null
+++ b/test_scripts/constants.vs
@@ -0,0 +1,19 @@
+# Constants Feature Test
+# Test declaration of immutable constants and verify re-assignment errors
+
+# Declare constant string and print
+const string $name = "Alice";
+printnl($name);
+
+# Declare constant integer and print
+const int $x = 100;
+printnl($x);
+
+# Declare mutable variable and modify
+string $y = "mutable";
+printnl($y);
+$y = "changed";
+printnl($y);
+
+# Attempt to modify constant (should produce runtime error)
+$name = "Bob";
\ No newline at end of file
diff --git a/test_scripts/constants_object.vs b/test_scripts/constants_object.vs
new file mode 100644
index 0000000..6859c91
--- /dev/null
+++ b/test_scripts/constants_object.vs
@@ -0,0 +1,17 @@
+# Constants Object Feature Test
+# Test declaration of immutable object constants and verify property modification errors
+
+# Declare constant object and print its properties
+const object $person = {
+    string name: "Bruce Wayne",
+    int age: 42,
+    object address: {
+        string city: "Gotham",
+        int zip: 12345
+    }
+};
+printnl($person->name, " is ", $person->age, " years old.");
+printnl("City: ", $person->address->city, ", ZIP: ", $person->address->zip);
+
+# Attempt to modify a property of the constant object (should produce runtime error)
+$person->age = 43;
\ No newline at end of file

--
Gitblit v1.9.3