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