fix function call in function
5 files modified
3 files added
| | |
| | | string name = "VoidScript"; |
| | | |
| | | // Functions |
| | | function add = (int $a, double $b, string $help) { |
| | | function add (int $a, double $b, string $help) { |
| | | int $result = $a + $b; |
| | | print("The sum is: ", $result, "\n"); |
| | | print("Help: ", $help, "\n"); |
| | |
| | | return mgr.callFunction(functionName_, argValues); |
| | | } |
| | | |
| | | // User-defined function |
| | | SymbolContainer *sc = Symbols::SymbolContainer::instance(); |
| | | const std::string currentNs = sc->currentScopeName(); |
| | | const std::string fnSymNs = currentNs + ".functions"; |
| | | auto sym = sc->get(fnSymNs, functionName_); |
| | | if (!sym || sym->getKind() != Kind::Function) { |
| | | throw std::runtime_error("Function not found: " + functionName_); |
| | | } |
| | | auto funcSym = std::static_pointer_cast<FunctionSymbol>(sym); |
| | | const auto ¶ms = funcSym->parameters(); |
| | | if (params.size() != argValues.size()) { |
| | | throw std::runtime_error( |
| | | "Function '" + functionName_ + "' expects " + std::to_string(params.size()) + |
| | | " args, got " + std::to_string(argValues.size())); |
| | | } |
| | | |
| | | // Enter function scope and bind parameters |
| | | const std::string fnOpNs = currentNs + "." + functionName_; |
| | | sc->enter(fnOpNs); |
| | | for (size_t i = 0; i < params.size(); ++i) { |
| | | const auto &p = params[i]; |
| | | const Value &v = argValues[i]; |
| | | auto varSym = SymbolFactory::createVariable(p.name, v, fnOpNs); |
| | | sc->add(varSym); |
| | | } |
| | | |
| | | // Execute function body operations and capture return |
| | | Symbols::Value returnValue; |
| | | auto ops = Operations::Container::instance()->getAll(fnOpNs); |
| | | for (const auto &op : ops) { |
| | | try { |
| | | interpreter.runOperation(*op); |
| | | } catch (const ReturnException &ret) { |
| | | returnValue = ret.value(); |
| | | break; |
| | | } |
| | | } |
| | | sc->enterPreviousScope(); |
| | | return returnValue; |
| | | // User-defined function: lookup through scope hierarchy |
| | | SymbolContainer *sc = SymbolContainer::instance(); |
| | | std::string lookupNs = sc->currentScopeName(); |
| | | std::shared_ptr<FunctionSymbol> funcSym; |
| | | // Search for function symbol in current and parent scopes |
| | | while (true) { |
| | | std::string fnSymNs = lookupNs + ".functions"; |
| | | auto sym = sc->get(fnSymNs, functionName_); |
| | | if (sym && sym->getKind() == Kind::Function) { |
| | | funcSym = std::static_pointer_cast<FunctionSymbol>(sym); |
| | | break; |
| | | } |
| | | auto pos = lookupNs.find_last_of('.'); |
| | | if (pos == std::string::npos) { |
| | | break; |
| | | } |
| | | lookupNs = lookupNs.substr(0, pos); |
| | | } |
| | | if (!funcSym) { |
| | | throw std::runtime_error("Function not found: " + functionName_); |
| | | } |
| | | const auto ¶ms = funcSym->parameters(); |
| | | if (params.size() != argValues.size()) { |
| | | throw std::runtime_error( |
| | | "Function '" + functionName_ + "' expects " + std::to_string(params.size()) + |
| | | " args, got " + std::to_string(argValues.size())); |
| | | } |
| | | // Enter function scope and bind parameters |
| | | const std::string fnOpNs = funcSym->context() + "." + functionName_; |
| | | sc->enter(fnOpNs); |
| | | for (size_t i = 0; i < params.size(); ++i) { |
| | | const auto &p = params[i]; |
| | | const Value &v = argValues[i]; |
| | | auto varSym = SymbolFactory::createVariable(p.name, v, fnOpNs); |
| | | sc->add(varSym); |
| | | } |
| | | // Execute function body operations and capture return |
| | | Symbols::Value returnValue; |
| | | auto ops = Operations::Container::instance()->getAll(fnOpNs); |
| | | for (const auto &op : ops) { |
| | | try { |
| | | interpreter.runOperation(*op); |
| | | } catch (const ReturnException &ret) { |
| | | returnValue = ret.value(); |
| | | break; |
| | | } |
| | | } |
| | | sc->enterPreviousScope(); |
| | | return returnValue; |
| | | } |
| | | |
| | | std::string toString() const override { |
| | |
| | | return; |
| | | } |
| | | } |
| | | SymbolContainer * sc = SymbolContainer::instance(); |
| | | const std::string currentNs = sc->currentScopeName(); |
| | | const std::string fnSymNs = currentNs + ".functions"; |
| | | auto sym = sc->get(fnSymNs, functionName_); |
| | | if (!sym || sym->getKind() != Kind::Function) { |
| | | // User-defined function: lookup through scope hierarchy |
| | | SymbolContainer *sc = SymbolContainer::instance(); |
| | | std::string lookupNs = sc->currentScopeName(); |
| | | std::shared_ptr<FunctionSymbol> funcSym; |
| | | // Search for function symbol in current and parent scopes |
| | | while (true) { |
| | | std::string fnSymNs = lookupNs + ".functions"; |
| | | auto sym = sc->get(fnSymNs, functionName_); |
| | | if (sym && sym->getKind() == Kind::Function) { |
| | | funcSym = std::static_pointer_cast<FunctionSymbol>(sym); |
| | | break; |
| | | } |
| | | auto pos = lookupNs.find_last_of('.'); |
| | | if (pos == std::string::npos) { |
| | | break; |
| | | } |
| | | lookupNs = lookupNs.substr(0, pos); |
| | | } |
| | | if (!funcSym) { |
| | | throw Exception("Function not found: " + functionName_, filename_, line_, column_); |
| | | } |
| | | auto funcSym = std::static_pointer_cast<FunctionSymbol>(sym); |
| | | const auto & params = funcSym->parameters(); |
| | | if (params.size() != argValues.size()) { |
| | | throw Exception( |
| | |
| | | " args, got " + std::to_string(argValues.size()), |
| | | filename_, line_, column_); |
| | | } |
| | | const std::string fnOpNs = currentNs + "." + functionName_; |
| | | // Enter function scope and bind parameters |
| | | const std::string fnOpNs = funcSym->context() + "." + functionName_; |
| | | sc->enter(fnOpNs); |
| | | for (size_t i = 0; i < params.size(); ++i) { |
| | | const auto & p = params[i]; |
| | | const Value & v = argValues[i]; |
| | | const auto &p = params[i]; |
| | | const Value &v = argValues[i]; |
| | | auto varSym = SymbolFactory::createVariable(p.name, v, fnOpNs); |
| | | sc->add(varSym); |
| | | } |
| | | auto ops = Operations::Container::instance()->getAll(fnOpNs); |
| | | auto it = ops.begin(); |
| | | for (; it != ops.end(); ++it) { |
| | | interpreter.runOperation(*(*it)); |
| | | for (const auto &op : ops) { |
| | | interpreter.runOperation(*op); |
| | | } |
| | | sc->enterPreviousScope(); |
| | | } catch (const Exception &) { |
| | |
| | | Lexer::Tokens::Token id_token = expect(Lexer::Tokens::Type::IDENTIFIER); |
| | | std::string func_name = id_token.value; |
| | | Symbols::Variables::Type func_return_type = Symbols::Variables::Type::NULL_TYPE; |
| | | expect(Lexer::Tokens::Type::OPERATOR_ASSIGNMENT, "="); |
| | | // note: '=' before parameter list is no longer required; function name is followed directly by '(' |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "("); |
| | | |
| | | Symbols::FunctionParameterInfo param_infos; |
| | |
| | | #include <memory> |
| | | #include <stdexcept> |
| | | #include <unordered_map> |
| | | #include <vector> |
| | | |
| | | #include "SymbolTable.hpp" |
| | | |
| | |
| | | |
| | | class SymbolContainer { |
| | | std::unordered_map<std::string, std::shared_ptr<SymbolTable>> scopes_; |
| | | std::string currentScope_ = "global"; |
| | | std::string previousScope_ = "global"; |
| | | // Stack of active scope names (supports nested scope entry) |
| | | std::vector<std::string> scopeStack_; |
| | | |
| | | public: |
| | | static SymbolContainer * instance() { |
| | |
| | | |
| | | // --- Scope management --- |
| | | |
| | | /** |
| | | * @brief Create a new scope and enter it. |
| | | * @param name Name of the new scope. |
| | | */ |
| | | void create(const std::string & name) { |
| | | scopes_[name] = std::make_shared<SymbolTable>(); |
| | | previousScope_ = currentScope_; |
| | | currentScope_ = name; |
| | | scopes_[name] = std::make_shared<SymbolTable>(); |
| | | scopeStack_.push_back(name); |
| | | } |
| | | |
| | | /** |
| | | * @brief Enter an existing scope. |
| | | * @param name Name of the scope to enter. |
| | | */ |
| | | void enter(const std::string & name) { |
| | | auto it = scopes_.find(name); |
| | | if (it != scopes_.end()) { |
| | | previousScope_ = currentScope_; |
| | | currentScope_ = name; |
| | | scopeStack_.push_back(name); |
| | | } else { |
| | | throw std::runtime_error("Scope does not exist: " + name); |
| | | } |
| | | } |
| | | |
| | | void enterPreviousScope() { currentScope_ = previousScope_; } |
| | | /** |
| | | * @brief Exit the current scope, returning to the previous one. |
| | | */ |
| | | void enterPreviousScope() { |
| | | if (scopeStack_.size() > 1) { |
| | | scopeStack_.pop_back(); |
| | | } |
| | | } |
| | | |
| | | [[nodiscard]] std::string currentScopeName() const { return currentScope_; } |
| | | /** |
| | | * @brief Get the name of the current scope. |
| | | * @return Current scope name. |
| | | */ |
| | | [[nodiscard]] std::string currentScopeName() const { return scopeStack_.empty() ? std::string() : scopeStack_.back(); } |
| | | |
| | | std::vector<std::string> getScopeNames() const { |
| | | std::vector<std::string> result; |
| | |
| | | |
| | | // --- Symbol operations --- |
| | | |
| | | /** |
| | | * @brief Add a symbol to the current scope. |
| | | * @param symbol Symbol to add. |
| | | * @return Namespace under which the symbol was defined. |
| | | */ |
| | | std::string add(const SymbolPtr & symbol) { |
| | | const std::string ns = getNamespaceForSymbol(symbol); |
| | | scopes_[currentScope_]->define(ns, symbol); |
| | | scopes_[currentScopeName()]->define(ns, symbol); |
| | | return ns; |
| | | } |
| | | |
| | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * @brief Check if a symbol exists in the given namespace (or current scope if none provided). |
| | | * @param name Symbol name. |
| | | * @param fullNamespace Namespace to search within (defaults to current scope). |
| | | * @return True if the symbol exists, false otherwise. |
| | | */ |
| | | bool exists(const std::string & name, std::string fullNamespace = "") const { |
| | | if (fullNamespace.empty()) { |
| | | fullNamespace = currentScope_; |
| | | fullNamespace = currentScopeName(); |
| | | } |
| | | |
| | | for (const auto & [_, table] : scopes_) { |
| | |
| | | } |
| | | |
| | | private: |
| | | /** |
| | | * @brief Compute the namespace string for a symbol based on its kind and context. |
| | | */ |
| | | std::string getNamespaceForSymbol(const SymbolPtr & symbol) const { |
| | | std::string base = symbol->context().empty() ? currentScope_ : symbol->context(); |
| | | std::string base = symbol->context().empty() ? currentScopeName() : symbol->context(); |
| | | |
| | | switch (symbol->getKind()) { |
| | | case Symbols::Kind::Variable: |
| New file |
| | |
| | | array[string] $array = ["cat", "dog", "girafe"]; |
| | | |
| | | array[int] $intArray = [10,11,0,1,2,44,2]; |
| | | |
| | | array[int] $emptyIntArray = []; |
| | | |
| | | |
| | | |
| New file |
| | |
| | | class test1 { |
| | | |
| | | private: |
| | | string $name; |
| | | int $age; |
| | | |
| | | public: |
| | | // constructor method, the return is always the class, can not use 'return' keyword here |
| | | function construct(string $name, int $age) { |
| | | this->name = $name; |
| | | this->age = $age; |
| | | } |
| | | |
| | | function getAge() int { |
| | | return this->age; |
| | | } |
| | | |
| | | function getName() string { |
| | | return this->name; |
| | | } |
| | | |
| | | function isAdult() bool { |
| | | return this->age >= 18; |
| | | } |
| | | |
| | | function changeName(string $new_name) { |
| | | this->name = $new_name; |
| | | } |
| | | |
| | | function incrementAge(int $incremental) int { |
| | | return this->age + $incremental; |
| | | } |
| | | |
| | | |
| | | } |
| | | |
| | | // |
| | | |
| | | test1 $testclass = new test1("Batman", 17); |
| | | |
| | | if ($testclass->isAdult() == false) { |
| | | printnl($testclass->getName(), " is not adult.. incrementing the age"); |
| | | $testclass->incrementAge(1); |
| | | } |
| | | |
| | | if ($testclass->isAdult()) { |
| | | println($testclass->getName(), " is adult"); |
| | | } |
| New file |
| | |
| | | // Functions Feature Test |
| | | |
| | | # Define a simple greeting function (no return type) |
| | | function greet (string $name) { |
| | | printnl("Hello, ", $name, "!"); |
| | | } |
| | | |
| | | # Define a sum function with explicit return type |
| | | function sum (int $a, int $b) int { |
| | | return $a + $b; |
| | | } |
| | | |
| | | # Define a function that uses other functions and local variables |
| | | function mulSum (int $x, int $y, int $z) int { |
| | | int $product = $x * $y; |
| | | int $result = sum($product, $z); |
| | | return $result; |
| | | } |
| | | |
| | | # Call the functions and print results |
| | | greet("VoidScript"); |
| | | |
| | | int $result1 = sum(7, 5); |
| | | printnl("sum(7, 5) = ", $result1); |
| | | |
| | | int $result2 = mulSum(2, 3, 4); |
| | | printnl("mulSum(2, 3, 4) = ", $result2); |
| | | |
| | | # Demonstrate nested call in expression |
| | | int $combined = mulSum(1, 2, 3) + sum(3, 4); |
| | | printnl("combined (mulSum(1,2,3) + sum(3,4)) = ", $combined); |