From e2e9e07a9f50dc1f7a967280a3d1d8ef7fcaa153 Mon Sep 17 00:00:00 2001
From: Ferenc Szontágh <szf@fsociety.hu>
Date: Fri, 18 Apr 2025 09:12:33 +0000
Subject: [PATCH] add built-in modules
---
/dev/null | 43 ----------
src/VoidScript.hpp | 6 +
src/Interpreter/CallStatementNode.hpp | 9 ++
src/Modules/PrintModule.hpp | 30 +++++++
test_scripts/test2.vs | 6 +
src/Modules/ModuleManager.hpp | 82 ++++++++++++++++++++
src/Modules/BaseModule.hpp | 27 ++++++
7 files changed, 159 insertions(+), 44 deletions(-)
diff --git a/src/Builtins/BaseFunction.hpp b/src/Builtins/BaseFunction.hpp
deleted file mode 100644
index 3cedf31..0000000
--- a/src/Builtins/BaseFunction.hpp
+++ /dev/null
@@ -1,77 +0,0 @@
-#ifndef SCRIPT_FUNCTION_HPP
-#define SCRIPT_FUNCTION_HPP
-
-#include <functional>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "ScriptExceptionMacros.h"
-#include "ScriptInterpreterHelpers.hpp"
-#include "Token.hpp"
-#include "Value.hpp"
-
-class ScriptInterpreter;
-
-using CallbackFunction = std::function<Value(const std::vector<Value> &)>;
-using CallBackStorage = std::unordered_map<std::string, CallbackFunction>;
-
-class BaseFunction {
- protected:
- std::string name;
- CallBackStorage functionMap;
-
-
- public:
- BaseFunction(const std::string & functionName) : name(functionName) {}
-
- virtual void addFunction(const std::string & name, std::function<Value(const std::vector<Value> &)> callback) {
- functionMap[name] = std::move(callback);
- }
-
- virtual void validate(const std::vector<Token> & tokens, size_t & i,
- const std::unordered_map<std::string, Value> & variables) {
- size_t index = i;
-
- if (tokens[index].type != TokenType::Identifier) {
- THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], "identifier");
- }
- index++; // skip function name
-
- if (tokens[index].type != TokenType::LeftParenthesis) {
- THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], "(");
- }
- index++; // skip '('
-
- std::vector<Token> args;
- while (tokens[index].type != TokenType::RightParenthesis) {
- if (tokens[index].type == TokenType::Comma) {
- index++;
- continue;
- }
- if (tokens[index].type == TokenType::Variable && !variables.contains(tokens[index].lexeme)) {
- THROW_UNDEFINED_VARIABLE_ERROR(tokens[index].lexeme, tokens[index]);
- }
- args.push_back(tokens[index]);
- index++;
- }
-
- index++; // skip ')'
-
- if (tokens[index].type != TokenType::Semicolon) {
- THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], ";");
- }
-
- this->validateArgs(args, variables);
- ScriptInterpreterHelpers::expectSemicolon(tokens, index, "function call");
- }
-
- virtual void validateArgs(const std::vector<Token> & args,
- const std::unordered_map<std::string, Value> & variables) = 0;
-
- virtual Value call(const std::vector<Value> & args, bool debug = false) const = 0;
-
- template <typename FuncClass> void registerFunctionTo(ScriptInterpreter & interp) { FuncClass::registerTo(interp); }
-};
-
-#endif // SCRIPT_FUNCTION_HPP
diff --git a/src/Builtins/MathUtilsModule.hpp b/src/Builtins/MathUtilsModule.hpp
deleted file mode 100644
index e85f4d8..0000000
--- a/src/Builtins/MathUtilsModule.hpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef MATH_UTILS_MODULE_HPP
-#define MATH_UTILS_MODULE_HPP
-
-#include <stdexcept>
-#include <vector>
-
-#include "ScriptExceptionMacros.h"
-#include "Value.hpp"
-
-class MathUtils {
- public:
- static Value multiply(const std::vector<Value> & args) {
- if (args.size() != 2) {
- throw std::runtime_error("multiply expects two arguments.");
- }
- if (args[0].type == Variables::Type::VT_INT && args[1].type == Variables::Type::VT_INT) {
- int left = args[0].ToInt();
- int right = args[1].ToInt();
- auto result = Value();
- result.data = left * right;
- result.type = Variables::Type::VT_INT;
- return result;
- //return Value::fromInt(left * right);
- }
- THROW_INVALID_FUNCTION_ARGUMENT_ERROR("multiply", args[0].TypeToString(), args[0].GetToken());
- };
-};
-#endif
diff --git a/src/Builtins/PrintModule.hpp b/src/Builtins/PrintModule.hpp
deleted file mode 100644
index a7bc07d..0000000
--- a/src/Builtins/PrintModule.hpp
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef PRINTFUNCTION_HPP
-#define PRINTFUNCTION_HPP
-
-#include <iostream>
-
-#include "BaseFunction.hpp"
-#include "ScriptExceptionMacros.h"
-#include "Token.hpp"
-#include "Value.hpp"
-
-class PrintFunction : public BaseFunction {
- private:
- const std::string name = "print";
- bool addNewLine = false;
- public:
- PrintFunction() : BaseFunction(name) {}
-
- void validateArgs(const std::vector<Token> & args,
- const std::unordered_map<std::string, Value> & variables) override {
- if (args.size() == 0) {
- THROW_UNEXPECTED_TOKEN_ERROR(args[0], "at least one argument");
- }
-
- for (const auto & arg : args) {
- if (arg.type == TokenType::Variable) {
- if (!variables.contains(arg.lexeme)) {
- THROW_UNDEFINED_VARIABLE_ERROR(arg.lexeme, arg);
- }
- }
- }
- if (args.end()->variableType == Variables::Type::VT_INT || args.end()->type == TokenType::IntLiteral) {
- this->addNewLine = true;
- }
- }
-
- Value call(const std::vector<Value> & args, bool debug = false) const override {
- for (const auto & arg : args) {
- std::cout << arg.ToString(); // todo: add endline if the last parameter is bool
- }
- return Value();
- }
-};
-
-#endif // PRINTFUNCTION_HPP
diff --git a/src/Builtins/SleepModule.hpp b/src/Builtins/SleepModule.hpp
deleted file mode 100644
index 17585b2..0000000
--- a/src/Builtins/SleepModule.hpp
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef SLEEPFUNCTION_HPP
-#define SLEEPFUNCTION_HPP
-
-#include <thread>
-
-#include "BaseFunction.hpp"
-
-class SleepFunction : public BaseFunction {
- public:
- SleepFunction() : BaseFunction("sleep") {}
-
- void validateArgs(const std::vector<Token> & args,
- const std::unordered_map<std::string, Value> & variables) override {
- if (args.size() != 1) {
- throw std::runtime_error("sleep() requires exactly one argument");
- }
-
- const Token & arg = args[0];
-
- if (arg.type == TokenType::IntLiteral) {
- return;
- }
-
- if (arg.type == TokenType::Variable) {
- const auto & value = variables.at(arg.lexeme);
- if (value.type != Variables::Type::VT_INT) {
- THROW_VARIABLE_TYPE_MISSMATCH_ERROR(arg.lexeme, Variables::TypeToString(Variables::Type::VT_INT), "",
- Variables::TypeToString(value.type), arg);
- }
- return;
- }
-
- THROW_UNEXPECTED_TOKEN_ERROR(arg, "int literal or variable");
- }
-
-
- Value call(const std::vector<Value> & args, bool debug = false) const override {
- std::this_thread::sleep_for(std::chrono::seconds(args[0].ToInt()));
- return Value();
- }
-};
-
-#endif // SLEEPFUNCTION_HPP
diff --git a/src/Interpreter/CallStatementNode.hpp b/src/Interpreter/CallStatementNode.hpp
index ccd85a1..224ac16 100644
--- a/src/Interpreter/CallStatementNode.hpp
+++ b/src/Interpreter/CallStatementNode.hpp
@@ -13,6 +13,7 @@
#include "Symbols/SymbolContainer.hpp"
#include "Symbols/SymbolFactory.hpp"
#include "Symbols/Value.hpp"
+#include "Modules/ModuleManager.hpp"
namespace Interpreter {
@@ -39,6 +40,14 @@
argValues.push_back(expr->evaluate(interpreter));
}
+ // Handle built-in function callbacks
+ {
+ auto &mgr = Modules::ModuleManager::instance();
+ if (mgr.hasFunction(functionName_)) {
+ mgr.callFunction(functionName_, argValues);
+ return;
+ }
+ }
// Lookup function symbol in functions namespace
SymbolContainer * sc = SymbolContainer::instance();
const std::string currentNs = sc->currentScopeName();
diff --git a/src/Modules/BaseModule.hpp b/src/Modules/BaseModule.hpp
new file mode 100644
index 0000000..27475bb
--- /dev/null
+++ b/src/Modules/BaseModule.hpp
@@ -0,0 +1,27 @@
+// BaseModule.hpp
+#ifndef MODULES_BASEMODULE_HPP
+#define MODULES_BASEMODULE_HPP
+
+#include <string>
+#include "Symbols/SymbolContainer.hpp"
+#include "Symbols/SymbolFactory.hpp"
+
+namespace Modules {
+
+/**
+ * @brief Base class for modules that can register functions and variables into the symbol table.
+ */
+class BaseModule {
+ public:
+ BaseModule() = default;
+ virtual ~BaseModule() = default;
+
+ /**
+ * @brief Register this module's symbols (functions, variables) into the global symbol container.
+ * Modules should use Symbols::SymbolContainer::instance() and SymbolFactory to add symbols.
+ */
+ virtual void registerModule() = 0;
+};
+
+} // namespace Modules
+#endif // MODULES_BASEMODULE_HPP
\ No newline at end of file
diff --git a/src/Modules/ModuleManager.hpp b/src/Modules/ModuleManager.hpp
new file mode 100644
index 0000000..c694493
--- /dev/null
+++ b/src/Modules/ModuleManager.hpp
@@ -0,0 +1,82 @@
+// ModuleManager.hpp
+#ifndef MODULES_MODULEMANAGER_HPP
+#define MODULES_MODULEMANAGER_HPP
+
+#include <memory>
+#include <vector>
+#include "BaseModule.hpp"
+#include <functional>
+#include <unordered_map>
+#include "Symbols/Value.hpp"
+
+namespace Modules {
+
+/**
+ * @brief Manager for registering and invoking modules.
+ */
+class ModuleManager {
+ public:
+ /**
+ * @brief Get singleton instance of ModuleManager.
+ */
+ static ModuleManager &instance() {
+ static ModuleManager mgr;
+ return mgr;
+ }
+
+ /**
+ * @brief Add a module to the manager.
+ * @param module Unique pointer to a BaseModule.
+ */
+ void addModule(std::unique_ptr<BaseModule> module) {
+ modules_.push_back(std::move(module));
+ }
+
+ /**
+ * @brief Invoke all registered modules to register their symbols.
+ */
+ void registerAll() {
+ for (const auto &module : modules_) {
+ module->registerModule();
+ }
+ }
+
+ private:
+ ModuleManager() = default;
+ std::vector<std::unique_ptr<BaseModule>> modules_;
+ // Built-in function callbacks: name -> function
+ std::unordered_map<std::string,
+ std::function<Symbols::Value(const std::vector<Symbols::Value>&)>> callbacks_;
+ public:
+ /**
+ * @brief Register a built-in function callback.
+ * @param name Name of the function.
+ * @param cb Callable taking argument values and returning a Value.
+ */
+ void registerFunction(const std::string &name,
+ std::function<Symbols::Value(const std::vector<Symbols::Value>&)> cb) {
+ callbacks_[name] = std::move(cb);
+ }
+
+ /**
+ * @brief Check if a built-in function is registered.
+ */
+ bool hasFunction(const std::string &name) const {
+ return callbacks_.find(name) != callbacks_.end();
+ }
+
+ /**
+ * @brief Call a built-in function callback.
+ */
+ Symbols::Value callFunction(const std::string &name,
+ const std::vector<Symbols::Value> &args) const {
+ auto it = callbacks_.find(name);
+ if (it == callbacks_.end()) {
+ throw std::runtime_error("Built-in function callback not found: " + name);
+ }
+ return it->second(args);
+ }
+};
+
+} // namespace Modules
+#endif // MODULES_MODULEMANAGER_HPP
\ No newline at end of file
diff --git a/src/Modules/PrintModule.hpp b/src/Modules/PrintModule.hpp
new file mode 100644
index 0000000..e4deb6a
--- /dev/null
+++ b/src/Modules/PrintModule.hpp
@@ -0,0 +1,30 @@
+// PrintModule.hpp
+#ifndef MODULES_PRINTMODULE_HPP
+#define MODULES_PRINTMODULE_HPP
+
+#include <iostream>
+#include "BaseModule.hpp"
+#include "ModuleManager.hpp"
+#include "Symbols/Value.hpp"
+
+namespace Modules {
+
+/**
+ * @brief Module that provides a built-in print function.
+ */
+class PrintModule : public BaseModule {
+ public:
+ void registerModule() override {
+ auto &mgr = ModuleManager::instance();
+ mgr.registerFunction("print", [](const std::vector<Symbols::Value> &args) {
+ for (const auto &v : args) {
+ std::cout << Symbols::Value::to_string(v);
+ }
+ std::cout << std::endl;
+ return Symbols::Value();
+ });
+ }
+};
+
+} // namespace Modules
+#endif // MODULES_PRINTMODULE_HPP
\ No newline at end of file
diff --git a/src/VoidScript.hpp b/src/VoidScript.hpp
index 879afeb..5fa3ef0 100644
--- a/src/VoidScript.hpp
+++ b/src/VoidScript.hpp
@@ -7,6 +7,8 @@
#include "Interpreter/Interpreter.hpp"
#include "Lexer/Lexer.hpp"
#include "Parser/Parser.hpp"
+#include "Modules/ModuleManager.hpp"
+#include "Modules/PrintModule.hpp"
class VoidScript {
private:
@@ -33,6 +35,8 @@
lexer(std::make_shared<Lexer::Lexer>()),
parser(std::make_shared<Parser::Parser>()) {
+ // Register built-in modules (print, etc.)
+ Modules::ModuleManager::instance().addModule(std::make_unique<Modules::PrintModule>());
this->files.emplace(this->files.begin(), file);
lexer->setKeyWords(Parser::Parser::keywords);
@@ -40,6 +44,8 @@
int run() {
try {
+ // Register all built-in modules before execution
+ Modules::ModuleManager::instance().registerAll();
while (!files.empty()) {
std::string file = files.back();
const std::string file_content = readFile(file);
diff --git a/test_scripts/test2.vs b/test_scripts/test2.vs
index bb9d822..15fc79e 100644
--- a/test_scripts/test2.vs
+++ b/test_scripts/test2.vs
@@ -8,5 +8,9 @@
int $result = $i + 1;
}
+function increment = (int $i) int {
+ return $i + 1;
+}
-test(5);
\ No newline at end of file
+
+test(1);
\ No newline at end of file
--
Gitblit v1.9.3