7 files modified
4 files added
| | |
| | | args_(std::move(args)) {} |
| | | |
| | | void interpret(Interpreter & interpreter) const override { |
| | | try { |
| | | using namespace Symbols; |
| | | // Evaluate argument expressions |
| | | std::vector<Value> argValues; |
| | | argValues.reserve(args_.size()); |
| | | for (const auto & expr : args_) { |
| | | argValues.push_back(expr->evaluate(interpreter)); |
| | | } |
| | | |
| | | // Handle built-in function callbacks |
| | | { |
| | | auto &mgr = Modules::ModuleManager::instance(); |
| | | if (mgr.hasFunction(functionName_)) { |
| | |
| | | return; |
| | | } |
| | | } |
| | | // Lookup function symbol in functions namespace |
| | | SymbolContainer * sc = SymbolContainer::instance(); |
| | | const std::string currentNs = sc->currentScopeName(); |
| | | const std::string fnSymNs = currentNs + ".functions"; |
| | |
| | | throw Exception("Function not found: " + functionName_, filename_, line_, column_); |
| | | } |
| | | auto funcSym = std::static_pointer_cast<FunctionSymbol>(sym); |
| | | |
| | | // Check parameter count |
| | | const auto & params = funcSym->parameters(); |
| | | if (params.size() != argValues.size()) { |
| | | throw Exception( |
| | |
| | | " args, got " + std::to_string(argValues.size()), |
| | | filename_, line_, column_); |
| | | } |
| | | |
| | | // Enter function scope to bind parameters and execute body |
| | | const std::string fnOpNs = currentNs + "." + functionName_; |
| | | sc->enter(fnOpNs); |
| | | // Bind parameters as local variables |
| | | 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 |
| | | auto ops = Operations::Container::instance()->getAll(fnOpNs); |
| | | auto it = ops.begin(); |
| | | for (; it != ops.end(); ++it) { |
| | | interpreter.runOperation(*(*it)); |
| | | } |
| | | // Exit function scope |
| | | sc->enterPreviousScope(); |
| | | } catch (const Exception &) { |
| | | throw; |
| | | } catch (const std::exception &e) { |
| | | throw Exception(e.what(), filename_, line_, column_); |
| | | } |
| | | } |
| | | |
| | | std::string toString() const override { |
| | |
| | | elseBranch_(std::move(elseBranch)) {} |
| | | |
| | | void interpret(class Interpreter & interpreter) const override { |
| | | // Evaluate condition |
| | | try { |
| | | auto val = condition_->evaluate(interpreter); |
| | | bool cond = false; |
| | | if (val.getType() == Symbols::Variables::Type::BOOLEAN) { |
| | |
| | | } else { |
| | | throw Exception("Condition did not evaluate to boolean", filename_, line_, column_); |
| | | } |
| | | // Execute appropriate branch |
| | | const auto & branch = cond ? thenBranch_ : elseBranch_; |
| | | for (const auto & stmt : branch) { |
| | | stmt->interpret(interpreter); |
| | | } |
| | | } catch (const Exception &) { |
| | | throw; |
| | | } catch (const std::exception &e) { |
| | | throw Exception(e.what(), filename_, line_, column_); |
| | | } |
| | | } |
| | | |
| | | std::string toString() const override { |
| | |
| | | ns(ns) {} |
| | | |
| | | void interpret(Interpreter & /*interpreter*/) const override { |
| | | //Symbols::Value value = expression_->evaluate(interpreter); |
| | | try { |
| | | if (Symbols::SymbolContainer::instance()->exists(functionName_)) { |
| | | throw Exception("Function already declared: " + functionName_, filename_, line_, column_); |
| | | } |
| | | const auto func = Symbols::SymbolFactory::createFunction(functionName_, ns, params_, "", returnType_); |
| | | Symbols::SymbolContainer::instance()->add(func); |
| | | } catch (const Exception &) { |
| | | throw; |
| | | } catch (const std::exception &e) { |
| | | throw Exception(e.what(), filename_, line_, column_); |
| | | } |
| | | } |
| | | |
| | | std::string toString() const override { |
| | |
| | | ns(ns) {} |
| | | |
| | | void interpret(Interpreter & interpreter) const override { |
| | | // Evaluate the expression and enforce declared type matches actual value type |
| | | try { |
| | | Symbols::Value value = expression_->evaluate(interpreter); |
| | | // Check for duplicate declaration |
| | | if (Symbols::SymbolContainer::instance()->exists(variableName_)) { |
| | | throw Exception("Variable already declared: " + variableName_, filename_, line_, column_); |
| | | } |
| | | // Enforce type correctness: the evaluated value must match the declared type |
| | | if (value.getType() != variableType_) { |
| | | using namespace Symbols::Variables; |
| | | std::string expected = TypeToString(variableType_); |
| | |
| | | "': expected '" + expected + "' but got '" + actual + "'", |
| | | filename_, line_, column_); |
| | | } |
| | | // Create and add the variable symbol |
| | | const auto variable = Symbols::SymbolFactory::createVariable(variableName_, value, ns, variableType_); |
| | | Symbols::SymbolContainer::instance()->add(variable); |
| | | } catch (const Exception &) { |
| | | throw; |
| | | } catch (const std::exception &e) { |
| | | throw Exception(e.what(), filename_, line_, column_); |
| | | } |
| | | } |
| | | |
| | | std::string toString() const override { |
| | |
| | | body_(std::move(body)) {} |
| | | |
| | | void interpret(Interpreter & interpreter) const override { |
| | | try { |
| | | using namespace Symbols; |
| | | // Evaluate iterable expression |
| | | auto iterableVal = iterableExpr_->evaluate(interpreter); |
| | | if (iterableVal.getType() != Variables::Type::OBJECT) { |
| | | throw Exception("For-in loop applied to non-object", filename_, line_, column_); |
| | | } |
| | | // Access underlying object map |
| | | const auto & objMap = std::get<Value::ObjectMap>(iterableVal.get()); |
| | | auto * symContainer = SymbolContainer::instance(); |
| | | const std::string base_ns = symContainer->currentScopeName(); |
| | | const std::string var_ns = base_ns + ".variables"; |
| | | // Iterate through object entries |
| | | for (const auto & entry : objMap) { |
| | | // Key binding |
| | | const std::string & key = entry.first; |
| | | Value keyVal(key); |
| | | if (!symContainer->exists(keyName_, var_ns)) { |
| | |
| | | auto sym = symContainer->get(var_ns, keyName_); |
| | | sym->setValue(keyVal); |
| | | } |
| | | // Value binding |
| | | Value valVal = entry.second; |
| | | if (!symContainer->exists(valueName_, var_ns)) { |
| | | auto sym = SymbolFactory::createVariable(valueName_, valVal, base_ns); |
| | |
| | | auto sym = symContainer->get(var_ns, valueName_); |
| | | sym->setValue(valVal); |
| | | } |
| | | // Execute loop body |
| | | for (const auto & stmt : body_) { |
| | | stmt->interpret(interpreter); |
| | | } |
| | | } |
| | | } catch (const Exception &) { |
| | | throw; |
| | | } catch (const std::exception &e) { |
| | | throw Exception(e.what(), filename_, line_, column_); |
| | | } |
| | | } |
| | | |
| | | std::string toString() const override { |
| | |
| | | #ifndef MODULES_BASEMODULE_HPP |
| | | #define MODULES_BASEMODULE_HPP |
| | | |
| | | #include <string> |
| | | #include "Symbols/SymbolContainer.hpp" |
| | | #include "Symbols/SymbolFactory.hpp" |
| | | |
| | | namespace Modules { |
| | | |
| New file |
| | |
| | | // FileModule.hpp |
| | | #ifndef MODULES_FILEMODULE_HPP |
| | | #define MODULES_FILEMODULE_HPP |
| | | |
| | | #include <filesystem> |
| | | #include <fstream> |
| | | #include <iterator> |
| | | #include <stdexcept> |
| | | #include <vector> |
| | | #include <string> |
| | | #include "BaseModule.hpp" |
| | | #include "ModuleManager.hpp" |
| | | #include "Symbols/Value.hpp" |
| | | |
| | | namespace Modules { |
| | | |
| | | /** |
| | | * @brief Module providing simple file I/O functions: |
| | | * file_get_contents(filename) -> string content |
| | | * file_put_contents(filename, content, overwrite) -> undefined, throws on error |
| | | * file_exists(filename) -> bool |
| | | */ |
| | | class FileModule : public BaseModule { |
| | | public: |
| | | void registerModule() override { |
| | | auto &mgr = ModuleManager::instance(); |
| | | // Read entire file content |
| | | mgr.registerFunction("file_get_contents", [](const std::vector<Symbols::Value> &args) { |
| | | using namespace Symbols; |
| | | if (args.size() != 1) { |
| | | throw std::runtime_error("file_get_contents expects 1 argument"); |
| | | } |
| | | if (args[0].getType() != Variables::Type::STRING) { |
| | | throw std::runtime_error("file_get_contents expects string filename"); |
| | | } |
| | | const std::string filename = args[0].get<std::string>(); |
| | | if (!std::filesystem::exists(filename)) { |
| | | throw std::runtime_error("File does not exist: " + filename); |
| | | } |
| | | std::ifstream input(filename, std::ios::in | std::ios::binary); |
| | | if (!input.is_open()) { |
| | | throw std::runtime_error("Could not open file: " + filename); |
| | | } |
| | | std::string content((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>()); |
| | | input.close(); |
| | | return Value(content); |
| | | }); |
| | | // Write content to file, with optional overwrite |
| | | mgr.registerFunction("file_put_contents", [](const std::vector<Symbols::Value> &args) { |
| | | using namespace Symbols; |
| | | if (args.size() != 3) { |
| | | throw std::runtime_error("file_put_contents expects 3 arguments"); |
| | | } |
| | | if (args[0].getType() != Variables::Type::STRING || |
| | | args[1].getType() != Variables::Type::STRING || |
| | | args[2].getType() != Variables::Type::BOOLEAN) { |
| | | throw std::runtime_error("file_put_contents expects (string, string, bool)"); |
| | | } |
| | | const std::string filename = args[0].get<std::string>(); |
| | | const std::string content = args[1].get<std::string>(); |
| | | const bool overwrite = args[2].get<bool>(); |
| | | if (!overwrite && std::filesystem::exists(filename)) { |
| | | throw std::runtime_error("File already exists: " + filename); |
| | | } |
| | | std::ofstream output(filename, std::ios::out | std::ios::binary | std::ios::trunc); |
| | | if (!output.is_open()) { |
| | | throw std::runtime_error("Could not open file for writing: " + filename); |
| | | } |
| | | output << content; |
| | | if (!output) { |
| | | throw std::runtime_error("Failed to write to file: " + filename); |
| | | } |
| | | output.close(); |
| | | return Value(); |
| | | }); |
| | | // Check if file exists |
| | | mgr.registerFunction("file_exists", [](const std::vector<Symbols::Value> &args) { |
| | | using namespace Symbols; |
| | | if (args.size() != 1) { |
| | | throw std::runtime_error("file_exists expects 1 argument"); |
| | | } |
| | | if (args[0].getType() != Variables::Type::STRING) { |
| | | throw std::runtime_error("file_exists expects string filename"); |
| | | } |
| | | const std::string filename = args[0].get<std::string>(); |
| | | bool exists = std::filesystem::exists(filename); |
| | | return Value(exists); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | } // namespace Modules |
| | | #endif // MODULES_FILEMODULE_HPP |
| | |
| | | #include "Modules/PrintNlModule.hpp" |
| | | #include "Modules/PrintModule.hpp" |
| | | #include "Modules/TypeofModule.hpp" |
| | | #include "Modules/FileModule.hpp" |
| | | #include "Parser/Parser.hpp" |
| | | |
| | | class VoidScript { |
| | |
| | | Modules::ModuleManager::instance().addModule(std::make_unique<Modules::PrintNlModule>()); |
| | | // typeof() builtin |
| | | Modules::ModuleManager::instance().addModule(std::make_unique<Modules::TypeofModule>()); |
| | | // file I/O builtin |
| | | Modules::ModuleManager::instance().addModule(std::make_unique<Modules::FileModule>()); |
| | | this->files.emplace(this->files.begin(), file); |
| | | |
| | | lexer->setKeyWords(Parser::Parser::keywords); |
| New file |
| | |
| | | # File I/O Feature Test |
| | | string $f = "test_file_io.txt"; |
| | | print("file_exists before create: ", file_exists($f), "\n"); |
| | | file_put_contents($f, "Hello from VoidScript!", false); |
| | | print("file_exists after create: ", file_exists($f), "\n"); |
| | | print("file_get_contents: ", file_get_contents($f), "\n"); |
| | | # Overwrite with permission |
| | | file_put_contents($f, "Overwritten content", true); |
| | | print("file_get_contents after overwrite: ", file_get_contents($f), "\n"); |