constats variable type implementation
6 files modified
2 files added
| | |
| | | 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, |
| | |
| | | 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 { |
| | |
| | | "': 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; |
| | |
| | | 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_; } |
| | |
| | | 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. |
| | |
| | | { 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(); |
| | | |
| | |
| | | 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); |
| | | } |
| | |
| | | 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); |
| | |
| | | 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(); |
| | |
| | | |
| | | // 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);) |
| New file |
| | |
| | | # 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"; |
| New file |
| | |
| | | # 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; |