From 3940a2dd82f380d33bd9444d32dbb9dae9e313c8 Mon Sep 17 00:00:00 2001 From: Ange1PLSGreet <663267554@qq.com> Date: Tue, 29 Jul 2025 21:11:58 +0800 Subject: [PATCH 01/11] =?UTF-8?q?=E5=AE=9E=E7=8E=B0Linux=E5=B9=B3=E5=8F=B0?= =?UTF-8?q?=E4=B8=8B=E5=8A=A8=E6=80=81=E5=BA=93=E5=8A=A0=E8=BD=BD=E7=BC=96?= =?UTF-8?q?=E8=AF=91=EF=BC=8C=E4=BD=86=E6=98=AF=E7=9B=AE=E5=89=8D=E4=BB=8D?= =?UTF-8?q?=E6=9C=89=E8=B7=AF=E5=BE=84=E6=90=9C=E7=B4=A2=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 5 +- interpreter/interpreter.cpp | 130 +++++++----------------------------- interpreter/module.cpp | 90 +++++++++++++++++++++++++ interpreter/module.hpp | 26 ++++++++ interpreter/value.hpp | 12 ++-- 5 files changed, 151 insertions(+), 112 deletions(-) create mode 100644 interpreter/module.cpp create mode 100644 interpreter/module.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index db01964e..7799ba5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,12 +50,15 @@ add_executable(Lamina interpreter/trackback.hpp interpreter/value.hpp interpreter/lamina.hpp + interpreter/module.cpp + interpreter/module.hpp extensions/standard/math.cpp extensions/standard/stdio.cpp extensions/standard/random.cpp extensions/standard/random.hpp extensions/standard/times.cpp - extensions/standard/times.hpp) + extensions/standard/times.hpp +) target_include_directories(lamina_core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interpreter diff --git a/interpreter/interpreter.cpp b/interpreter/interpreter.cpp index 8fc8d89f..c487d17c 100644 --- a/interpreter/interpreter.cpp +++ b/interpreter/interpreter.cpp @@ -1,5 +1,6 @@ #include "interpreter.hpp" #include "lexer.hpp" +#include "module.hpp" #include "lamina.hpp" #include "parser.hpp" #include "bigint.hpp" @@ -16,9 +17,7 @@ #include #include #include -#ifndef PATH_MAX -#define PATH_MAX MAX_PATH -#endif + #else #include #include @@ -27,6 +26,7 @@ #include // For isatty #include // For getenv #include +#include #endif @@ -757,17 +757,31 @@ bool Interpreter::load_module(const std::string& module_name) { full_path = path + filename; // std::cout << "Current Full Path: " << full_path << std::endl; file.open(full_path); + if (file) { + file.close(); +#ifdef __linux__ + ModuleLoader loader(full_path); + if (loader.isLoaded()) { + std::vector entryFunctions = loader.findEntryFunctions(); + for (auto& entryFunc : entryFunctions) { + entryFunc(*this); + } + loaded_modules.insert(module_name); + return true; + } else { + std::cerr << "Failed to load shared library: " << full_path << std::endl; + } +#endif break; - } else { - std::cerr << "Error: Cannot load module '" << module_name << "'" << std::endl; + } + if (!file) { + std::cerr << "Error: Cannot load module '" << module_name << "'\n"; std::cerr << " Searched in: "; - for (size_t i = 0; i < search_paths.size(); ++i) { - std::cerr << search_paths[i] + filename; - if (i < search_paths.size() - 1) std::cerr << ", "; + for (const auto& path : search_paths) { + std::cerr << path + filename << " "; } - - std::cerr << std::endl; + std::cerr << "\n"; return false; } } @@ -779,102 +793,6 @@ bool Interpreter::load_module(const std::string& module_name) { std::string source = buffer.str(); file.close(); - // Record module as loaded - if (is_shared_lib && file) { - char abs_path[PATH_MAX]; -#ifdef _WIN32 - if (_fullpath(abs_path, full_path.c_str(), PATH_MAX) == nullptr) { - std::cerr << "Error resolving path: " << full_path << std::endl; - return false; - } -#else - if (realpath(full_path.c_str(), abs_path) == nullptr) { - std::cerr << "Error resolving path: " << full_path << std::endl; - return false; - } -#endif - const char* func_symbol; -#ifdef _WIN32 - HMODULE handle = LoadLibraryA(full_path.c_str()); - if (!handle) { - DWORD err_code = GetLastError(); - char err_msg[256] = {0}; - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err_code, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - err_msg, sizeof(err_msg)-1, NULL); - std::cerr << "Failed to load DLL (" << full_path << "): " - << err_msg << " (Code: " << err_code << ")" << std::endl; - return false; - } - func_symbol = ""; // I Don't Know Windows Platform's symbol -#else - void* handle = dlopen(full_path.c_str(), RTLD_LAZY | RTLD_LOCAL); - if (!handle) { - std::cerr << "Failed to load SO (" << full_path << "): " << dlerror() << std::endl; - return false; - } - func_symbol = "_ZN11Interpreter14register_entryEPFvRS_E"; -#endif - -#ifdef _WIN32 -#else - dlerror(); -#endif - - using RegisterFunc = Interpreter::EntryFunction; - auto register_entry = reinterpret_cast( -#ifdef _WIN32 - GetProcAddress(handle, func_symbol) -#else - dlsym(handle, func_symbol) -#endif - ); - - if (!register_entry) { -#ifdef _WIN32 - DWORD err_code = GetLastError(); - std::cerr << "Failed to find function " << func_symbol << " in " - << full_path << " (Code: " << err_code << ")" << std::endl; -#else - std::cerr << "Failed to find function " << func_symbol << " in " - << full_path << ": " << dlerror() << std::endl; -#endif -#ifdef _WIN32 - FreeLibrary(handle); -#else - dlclose(handle); -#endif - return false; - } - - try { - register_entry(*this); - - for (const auto& entry_func : Interpreter::entry_functions) { - if (entry_func) { - try { - entry_func(*this); - } catch (const std::exception& e) { - std::cerr << "Failed to register an entry function: " << e.what() << std::endl; - } - } else { - std::cerr << "Skipped a null entry function" << std::endl; - } - } - - loaded_modules.insert(module_name); - return true; - } catch (const std::exception& e) { - std::cerr << "Error when calling register_entry for " - << module_name << ": " << e.what() << std::endl; -#ifdef _WIN32 - FreeLibrary(handle); -#else - dlclose(handle); -#endif - return false; - } - } // Lexical and syntax analysis auto tokens = Lexer::tokenize(source); diff --git a/interpreter/module.cpp b/interpreter/module.cpp new file mode 100644 index 00000000..b25ca591 --- /dev/null +++ b/interpreter/module.cpp @@ -0,0 +1,90 @@ +/* + @Dev Ange1PlsGreet +*/ +#include "module.hpp" + +ModuleLoader::ModuleLoader(const std::string& soPath) { +#ifdef __linux__ + m_handle = dlopen(soPath.c_str(), RTLD_LAZY); + if (!m_handle) { + std::cerr << "Failed to load library: " << dlerror() << std::endl; + } +#endif +} + +ModuleLoader::~ModuleLoader() { +#ifdef __linux__ + if (m_handle) { + dlclose(m_handle); + m_handle = nullptr; + } +#endif +} + +void* ModuleLoader::findSymbol(const std::string& symbolName) { +#ifdef __linux__ + if (!m_handle) { + return nullptr; + } + dlerror(); + void* symbol = dlsym(m_handle, symbolName.c_str()); + const char* dlsym_error = dlerror(); + if (dlsym_error) { + std::cerr << "Error looking up symbol '" << symbolName << "': " << dlsym_error << std::endl; + return nullptr; + } + return symbol; +#endif +} + +bool ModuleLoader::isLoaded() const { + return m_handle != nullptr; +} + +std::vector ModuleLoader::findEntryFunctions() { +#ifdef __linux__ + std::vector entryFunctions; + if (!m_handle) { + return entryFunctions; + } + + struct link_map* lmap; + dlinfo(m_handle, RTLD_DI_LINKMAP, &lmap); + + const ElfW(Sym)* symtab = nullptr; + const char* strtab = nullptr; + size_t symtabsz = 0; + + for (ElfW(Dyn)* d = lmap->l_ld; d->d_tag != DT_NULL; ++d) { + if (d->d_tag == DT_SYMTAB) { + symtab = reinterpret_cast(d->d_un.d_ptr); + } else if (d->d_tag == DT_STRTAB) { + strtab = reinterpret_cast(d->d_un.d_ptr); + } else if (d->d_tag == DT_SYMENT) { + symtabsz = d->d_un.d_val; + } + } + + if (symtab && strtab && symtabsz > 0) { + size_t symcount = (reinterpret_cast(strtab) - reinterpret_cast(symtab)) / symtabsz; + + for (size_t j = 0; j < symcount; ++j) { + const ElfW(Sym)* sym = &symtab[j]; + if (sym->st_name && strtab + sym->st_name) { + std::string symbolName = strtab + sym->st_name; + if (symbolName.find("_entry") != std::string::npos) { + void* symbolAddr = findSymbol(symbolName.c_str()); + if (symbolAddr) { + auto entryFunc = reinterpret_cast(symbolAddr); + entryFunctions.emplace_back([entryFunc](Interpreter& interpreter) { + entryFunc(interpreter); + }); + } + } + } + } + } + + return entryFunctions; +#endif +} \ No newline at end of file diff --git a/interpreter/module.hpp b/interpreter/module.hpp new file mode 100644 index 00000000..29046767 --- /dev/null +++ b/interpreter/module.hpp @@ -0,0 +1,26 @@ +/* + 专门用于动态库加载的模块类 +*/ +#pragma once +#include +#ifdef __linux__ +#include +#include +#endif +#include +#include +#include "interpreter.hpp" + +class ModuleLoader { +public: + using EntryFunction = std::function; + ModuleLoader(const std::string& soPath); + ~ModuleLoader(); + void* findSymbol(const std::string& symbolName); + bool isLoaded() const; + void* m_handle; + std::vector findEntryFunctions(); +}; + + + diff --git a/interpreter/value.hpp b/interpreter/value.hpp index e5295fa9..c27d393d 100644 --- a/interpreter/value.hpp +++ b/interpreter/value.hpp @@ -16,12 +16,14 @@ public: enum class Type { Null, Bool, Int, Float, String, Array, Matrix, BigI virtual ~Value() = default; // Constructors - Value() : type(Type::Null), data(nullptr) {} + Value() : type(Type::Null), data(std::in_place_index<0>, nullptr) {} // 0对应std::nullptr_t类型 - Value(std::nullptr_t) : type(Type::Null), data(nullptr) {} - Value(bool b) : type(Type::Bool), data(b) {} - Value(int i) : type(Type::Int), data(i) {} - Value(double f) : type(Type::Float), data(f) {} Value(const std::string& s) : type(Type::String), data(s) {} + // 其他构造函数需要同步修改初始化方式 + Value(std::nullptr_t) : type(Type::Null), data(std::in_place_index<0>, nullptr) {} + Value(bool b) : type(Type::Bool), data(std::in_place_index<1>, b) {} + Value(int i) : type(Type::Int), data(std::in_place_index<2>, i) {} + Value(double f) : type(Type::Float), data(std::in_place_index<3>, f) {} + Value(const std::string& s) : type(Type::String), data(s) {} Value(const char* s) : type(Type::String), data(std::string(s)) {} Value(const ::BigInt& bi) : type(Type::BigInt), data(bi) {} Value(const ::Rational& r) : type(Type::Rational), data(r) {} From 3812ba285ba4360b7737776c0de530e40f935e53 Mon Sep 17 00:00:00 2001 From: Ange1PLSGreet <663267554@qq.com> Date: Tue, 29 Jul 2025 23:13:44 +0800 Subject: [PATCH 02/11] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E5=8A=A0=E8=BD=BD=E7=9A=84BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- interpreter/interpreter.cpp | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/interpreter/interpreter.cpp b/interpreter/interpreter.cpp index c487d17c..151340cd 100644 --- a/interpreter/interpreter.cpp +++ b/interpreter/interpreter.cpp @@ -749,13 +749,12 @@ bool Interpreter::load_module(const std::string& module_name) { } if (!file) { - std::string filename = "lib" + clean_name + ".so"; + std::string lib_filename = "lib" + clean_name + ".so"; is_shared_lib = true; + bool found = false; - // 重新搜索文件路径 for (const auto& path : search_paths) { - full_path = path + filename; - // std::cout << "Current Full Path: " << full_path << std::endl; + full_path = path + lib_filename; file.open(full_path); if (file) { @@ -768,25 +767,35 @@ bool Interpreter::load_module(const std::string& module_name) { entryFunc(*this); } loaded_modules.insert(module_name); - return true; + found = true; + break; } else { std::cerr << "Failed to load shared library: " << full_path << std::endl; + file.clear(); } -#endif +#else + // Windows TODO! + found = true; break; +#endif + } else { + file.clear(); } - if (!file) { - std::cerr << "Error: Cannot load module '" << module_name << "'\n"; - std::cerr << " Searched in: "; - for (const auto& path : search_paths) { - std::cerr << path + filename << " "; - } - std::cerr << "\n"; - return false; + } + + if (!found) { + std::cerr << "Error: Cannot load module '" << module_name << "'\n"; + std::cerr << " Searched in: "; + for (const auto& path : search_paths) { + std::cerr << path + lib_filename << " "; } + std::cerr << "\n"; + return false; } + return true; } + // Read file into string std::stringstream buffer; buffer << file.rdbuf(); From eaf598fa3e06e245d4ed64b7f6a0482a63337ddc Mon Sep 17 00:00:00 2001 From: Ange1PLSGreet <663267554@qq.com> Date: Tue, 29 Jul 2025 23:14:12 +0800 Subject: [PATCH 03/11] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=BA=86LINUX=E5=B9=B3?= =?UTF-8?q?=E5=8F=B0=E4=B8=8B=E9=83=A8=E5=88=86sockets=E5=BA=93=E7=9A=84?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extensions/standard/sockets.cpp | 72 +++++++++++++++++++++++++++++++++ extensions/standard/sockets.hpp | 22 ++++++++++ 2 files changed, 94 insertions(+) create mode 100644 extensions/standard/sockets.cpp create mode 100644 extensions/standard/sockets.hpp diff --git a/extensions/standard/sockets.cpp b/extensions/standard/sockets.cpp new file mode 100644 index 00000000..d3bca6ef --- /dev/null +++ b/extensions/standard/sockets.cpp @@ -0,0 +1,72 @@ +/* + CopyRight Shizuku Technologies + 2025.07.24 22:39 + @Dev Ange1PlsGreet +*/ +#include "sockets.hpp" + +SOCKETS_HPP int create_socket(uv_stream_t *server, int status) { +#ifdef __linux__ + if (status < 0) { + return -1; + } + + uv_tcp_t *client = new uv_tcp_t; + uv_tcp_init(loop, client); + if (uv_accept(server, (uv_stream_t*) client) == 0) { + return 1; + } else { + uv_close((uv_handle_t*) client, [](uv_handle_t* handle) { + delete (uv_tcp_t*) handle; + }); + } +#endif +} + +SOCKETS_HPP Value runnable(const std::vector &args) { + std::string address = args[0].to_string(); + if (!args[1].is_numeric()) { + L_ERR("Second Arg Must Be Number!"); + } + int port = args[1].as_number(); + // protocol(IPV4/IPV6) + if (!args[2].is_numeric()) { + L_ERR("Third Arg Must Be Number!"); + } + int protocol = args[2].as_number(); + if (protocol != 4 && protocol != 6) { + L_ERR("Third Arg Must Be 4 Or 6!"); + } + // TCP/UDP + if (!args[3].is_numeric()) { + L_ERR("Fourth Arg Must Be Number!"); + } + int type = args[3].as_number(); + if (type != 0 && type != 1) { + L_ERR("Fourth Arg Must Be 0 Or 1!"); + } +#ifdef __linux__ + if (protocol == 4 && type == 0) { + loop = uv_default_loop(); + uv_tcp_init(loop, &server); + + uv_tcp_nodelay(&server, 1); + + struct sockaddr_in addr; + uv_ip4_addr(address.c_str(), port, &addr); + uv_tcp_bind(&server, (const sockaddr*)&addr, 0); + + int r = uv_listen((uv_stream_t*) &server, DEFAULT_BACKLOG, + [](uv_stream_t* server, int status) { + create_socket(server, status); + }); + + if (r) { + L_ERR("Listen error: " + std::string(uv_strerror(r))); + } + + uv_run(loop, UV_RUN_DEFAULT); + return Value(0); + } +#endif +} diff --git a/extensions/standard/sockets.hpp b/extensions/standard/sockets.hpp new file mode 100644 index 00000000..b71e2676 --- /dev/null +++ b/extensions/standard/sockets.hpp @@ -0,0 +1,22 @@ +/* + TODO: WINDOWS SOCKET +*/ +#ifndef SOCKETS_HPP +#define SOCKETS_HPP + +#include +#include "lamina.hpp" +#ifdef __linux__ +#include +uv_loop_t *loop; +uv_tcp_t server; +#endif +#define DEFAULT_BACKLOG 128 + +static int create_socket(uv_stream_t *server, int status); +Value runnable(const std::vector& args); + +namespace lamina{ + LAMINA_FUNC("socket_create", runnable, 4); +} +#endif //SOCKETS_HPP From 652f13b8e8701ff68bb0ac9880474760889e0129 Mon Sep 17 00:00:00 2001 From: Ange1PLSGreet <663267554@qq.com> Date: Wed, 30 Jul 2025 01:58:06 +0800 Subject: [PATCH 04/11] =?UTF-8?q?=E5=A4=A7=E6=9B=B4=E6=96=B0=EF=BC=81?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=B4=A1=E7=8C=AE=E6=96=87=E6=A1=A3=EF=BC=8C?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E6=96=87=E6=A1=A3=EF=BC=8C=E9=87=8D=E6=96=B0?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=EF=BC=8C=E5=8E=BB?= =?UTF-8?q?=E9=99=A4lamina.hpp=E7=9A=84=E6=97=A0=E7=94=A8=E5=AE=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CONTRIBUTING-CN.md | 743 +++++++++++++++++++++++++++++ CONTRIBUTING-EN.md | 807 ++++++++++++++++++++++++++++++++ compile-cn.md | 17 + compile-en.md | 17 + extensions/standard/random.cpp | 1 + extensions/standard/sockets.cpp | 9 +- interpreter/lamina.hpp | 18 - 7 files changed, 1587 insertions(+), 25 deletions(-) create mode 100644 CONTRIBUTING-CN.md create mode 100644 CONTRIBUTING-EN.md create mode 100644 compile-cn.md create mode 100644 compile-en.md diff --git a/CONTRIBUTING-CN.md b/CONTRIBUTING-CN.md new file mode 100644 index 00000000..724aecb4 --- /dev/null +++ b/CONTRIBUTING-CN.md @@ -0,0 +1,743 @@ +## 代码提交规范 -- 中文 + +- 请先阅读 了解项目的基本情况,在继续进行开发! +- 本项目使用GPL-3.0协议,请严格遵守本协议! + +## 项目结构: +```angular2html +Lamina +├── assets +│   ├── logo-icon.svg +│   └── logo.svg +├── extensions +│   └── standard +│   ├── math.cpp +│   ├── random.cpp +│   ├── random.hpp +│   ├── sockets.cpp +│   ├── sockets.hpp +│   ├── stdio.cpp +│   ├── times.cpp +│   └── times.hpp +├── interpreter +│   ├── ast.hpp +│   ├── bigint.hpp +│   ├── examples +│   │   ├── calculator.lm +│   │   ├── defin.lm +│   │   ├── hello.lm +│   │   ├── onestop.lm +│   │   └── quadratic.lm +│   ├── interpreter.cpp +│   ├── interpreter.hpp +│   ├── interpreter.md +│   ├── irrational.hpp +│   ├── lamina.hpp +│   ├── lexer.cpp +│   ├── lexer.hpp +│   ├── main.cpp +│   ├── module.cpp +│   ├── module.hpp +│   ├── parser.cpp +│   ├── parser.hpp +│   ├── rational.hpp +│   ├── trackback.hpp +│   └── value.hpp +├── LICENSE +└── README.md +├── CMakeLists.txt +├── compile-cn.md +├── compile-en.md +├── CONTRIUTING-CN.md +``` +总计: 5 文件夹, 38 文件。 + +## 创建拉取请求: + +```angular2html +1. Fork代码到你的仓库。 +2. 克隆代码到本地。 +3. 提交代码到你的仓库。 +4. 创建拉取请求。 +``` + +创建拉取请求时,你必须要确保你的PR内容符合以下要求: + +- 目的明确 +- 语言流畅 +- 代码规范 + +在编写拉取请求标题时,必须携带如以下标识, 主要分为这几种: +```angular2html +1. [Feature] +2. [Bug Fix] +``` +如果是针对某一个模块,请在创建拉取请求时,在标题中携带类型以及你的模块名称,例如: +```angular2html +[Feature][stdio] 新增print函数对于文件流操作的支持 +``` + +## 有关库/标准库: + +标准库的代码存放在`extensions/standard`目录下,每个文件对应一个模块,模块的名称就是文件的名称,且每个模块都要有对应的头文件,头文件内注册Lamina函数。 + +在拓展层注册的Lamina变量均为全局变量。 + +### 注册Lamina函数的方式: + +调用LAMINA_FUNC宏,例如: +```c++ +namespace lamina{ + LAMINA_FUNC("lamina_func_name", cpp_func_name, arg_counts); +} +``` +其实,您也可以不用将函数注册到lamina命名空间下,而是直接注册,例如: +```c++ +LAMINA_FUNC("lamina_func_name", cpp_func_name, arg_counts); +``` + +但是,这是一种更为规范的方式,我们更推荐这样做! + +若你需要声明Lamina函数,它在C++层的返回值必须声明为```Value```,并携带```const std::vector &args```参数,并且我们更推荐使用lamina.hpp中对于数据类型操作的宏,如```LAMINA_BOOL```,这对于你的Lamina库项目会更直观! + +但是,由于部分历史遗留问题,一些Lamina标准库的内容没有使用这些宏作为返回值。 + +在编写标准库的代码时,必须要遵循以下规范: + +- 代码必须要确保一定的安全性。 +- 代码必须要符合Lamina拓展的风格。 + +## !! 当你为Lamina编写其他库的时候,也亦是如此。 + +## 模块解析: + +Lamina主要的核心模块有 + +- bigint.hpp 大整数模块 +- interpreter.cpp 解释器模块 +- irrational.hpp 无理数模块 +- lamina.hpp 访问Lamina部分核心资源的模块 +- module.cpp 加载Lamina动态库的模块 +- rational.hpp 有理数模块 +- value.hpp 数值模块 +- parser.cpp 解析器模块 +- lexer.cpp 词法分析器模块 + +让我们从0开始,讲解这些模块内的函数,以便于让你进入Lamina的库开发! + +在编写Lamina库中,最重要的便是```lamina.hpp```模块,此模块提供了Lamina库开发的一些基本的宏。 +```c++ +// Source Code: +#pragma once +/* + 对LAMINA核心资源操作的头文件 + */ + +template constexpr bool always_false = false; + + +#define LAMINA_BOOL(value) Value((bool) value) +#define LAMINA_INT(value) Value((int) value) +#define LAMINA_DOUBLE(value) Value((double) value) +#define LAMINA_STRING(value) Value((const char*) value) +#define LAMINA_BIGINT(value) Value((const ::BigInt&)value) +#define LAMINA_RATIONAL(value) Value((const ::Rational&)value) +#define LAMINA_IRRATIONAL(value) Value((const ::Irrational&)value) +#define LAMINA_ARR(value) Value(value) +#define LAMINA_MATRIX(value) Value(value) +#define LAMINA_NULL Value() + +#define LAMINA_FUNC_WIT_ANY_ARGS(func_name, func) \ +void func##_any_args_entry(Interpreter& interpreter); \ +namespace { \ +struct func##_any_args_registrar { \ + func##_any_args_registrar() { \ + Interpreter::register_entry(&func##_any_args_entry); \ + } \ +} func##_any_args_instance; \ +} \ +void func##_any_args_entry(Interpreter& interpreter) { \ + interpreter.builtin_functions[func_name] = [](const std::vector& args) -> Value { \ + return func(args); \ + }; \ +} + +#define LAMINA_FUNC(func_name, func, arg_count) \ +void func##_entry(Interpreter& interpreter) LAMINA_EXPORT; \ +namespace { \ +struct func##_registrar { \ + func##_registrar() { \ + Interpreter::register_entry(&func##_entry); \ + } \ +} func##_instance; \ +} \ +void func##_entry(Interpreter& interpreter) { \ + interpreter.builtin_functions[func_name] = [](const std::vector& args) -> Value { \ + if (args.size() != arg_count) { \ + std::cerr << "Error: " << func_name << "() requires " << arg_count <<" arguments\n"; \ + return Value(); \ + } \ + return func(args); \ + }; \ +} + +#define LAMINA_FUNC_MULTI_ARGS(func_name, func, arg_count) \ +void func##_entry(Interpreter& interpreter); \ +namespace { \ +struct func##_registrar { \ + func##_registrar() { \ + Interpreter::register_entry(&func##_entry); \ + } \ +} func##_instance; \ +} \ +void func##_entry(Interpreter& interpreter) { \ + interpreter.builtin_functions[func_name] = [](const std::vector& args) -> Value { \ + if (args.size() > arg_count) { \ + std::cerr << "Error: " << func_name << "() takes 0 to " << arg_count << " arguments\n"; \ + return Value(); \ + } \ + return func(args); \ + }; \ +} + +#define LAMINA_GET_VAR(interpreter, var) \ + interpreter.get_variable(#var) + +#define L_ERR(msg)\ + error_and_exit(msg); \ + +#define LAMINA_GLOBAL_VAR(name, value) \ +void global_var_##name##_entry(Interpreter& interpreter) { \ + interpreter.set_global_variable(#name, Value(value)); \ +} \ +namespace { \ +struct global_var_##name##_registrar { \ + global_var_##name##_registrar() { \ + Interpreter::register_entry(&global_var_##name##_entry); \ + } \ +} global_var_##name##_instance; \ +} + +``` + +- ``` LAMINA_FUNC_WIT_ANY_ARGS ```宏用于注册一个可以接受任意参数数量的Lamina函数。 +- ``` LAMINA_FUNC ```宏用于注册一个可以接受固定参数数量的Lamina函数。 +- ``` LAMINA_FUNC_MULTI_ARGS ```宏用于注册一个可以接受0到固定参数数量的Lamina函数。 + +他们的宏的内部实现其实都大同小异,只不过对于参数数量的判断略有不同 + +最终编译成动态库之后,他们的符号表像是这样: +``` +0000000000020edc T _Z10test_entryR11Interpreter +``` + +原函数将会带有```_entry```后缀,后续将注册到```builtin_functions```这个vector容器中。 + +```LAMINA_BOOL```宏用于操作Lamina中的布尔数据类型 +```LAMINA_INT```宏用于操作Lamina中的整数数据类型 +```LAMINA_STRING```宏用于操作Lamina中的字符串数据类型 +```LAMINA_BIGINT```宏用于操作Lamina中的大整数数据类型 +```LAMINA_RATIONAL```宏用于操作Lamina中的有理数数据类型 +```LAMINA_IRRATIONAL```宏用于操作Lamina中的无理数数据类型 +```LAMINA_ARR```宏用于操作Lamina中的数组数据类型 +```LAMINA_MATRIX```宏用于操作Lamina中的矩阵数据类型 +```LAMINA_NULL```宏用于操作Lamina中的空值数据类型 + +可以使用```LAMINA_BOOL```等宏来直观操作Lamina函数的返回值,例如随机库当中的: +```c++ +Value randstr(const std::vector &args) { + if (args.size() != 1 || !args[0].is_numeric()) { + L_ERR("randstr() requires exactly one numeric argument"); + return LAMINA_NULL; + } + + int length = std::stoi(args[0].to_string()); + if (length < 0) { + L_ERR("randstr() length argument must be non-negative"); + return LAMINA_NULL; + } + + static const std::string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + static std::random_device rd; + static std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, charset.size() - 1); + + std::string result; + result.reserve(length); + for (int i = 0; i < length; ++i) { + result += charset[dis(gen)]; + } + + return LAMINA_STRING(result.c_str()); +} + +``` + +这段Lamina库代码展示了根据用户输入的字符来随机生成一串字符串,并通过```LAMINA_STRING```宏来返回值,若失败,则使用```LAMINA_NULL```宏来返回空值。 + +- ```LAMINA_GET_VAR```宏用于在解释器运行时,在已经注册的函数内部,获取Lamina中的变量值。 +- ```LAMINA_GLOBAL_VAR```宏用于定义Lamina中的全局变量。 + +这两个目前在标准库中还未使用,但是我们仍旧给出一个例子,便于开发: +```c++ +#include "lamina.hpp" + +LAMINA_GLOBAL_VAR(a, 1); +Interpreter interpreter; + +Value func_a(const std::vector &args){ + Value a = LAMINA_GET_VAR(interpreter, a); +} + +namespace lamina{ + LAMINA_FUNC(func_a, func_a, 0); +} +``` +该例子主要展示了Lamina全局变量的注册方法和使用方法,在获取变量时需要传入一个解释器实例,和一个变量名,在注册全局变量的时候,需要传入一个变量名参数和值。 + +```L_ERR```宏用于在Lamina内部执行过程中抛出一个错误,此函数不做过多讲解,仅给使用示例: +```c++ +#include "lamina.hpp" + +Value a(const std::vector &args){ + L_ERR("a is not defined"); + return LAMINA_NULL; +} +``` + +关于库的部分,我们已经讲解完毕,跳出Lamina扩展层,我们来到更为底层解释器模块! + +Lamina的解释器主要有这几个模块构成,他们共同支撑了Lamina在数学计算方面的优秀。 + +- 大整数模块 +- 无理数模块 +- 有理数模块 + +其次,还有更为底层的解析器模块和词法分析器模块,下文统称为语法处理模块。 + +让我们先从```interpreter.cpp```的源文件进行分析。 + +用于源码过长,这里只展示函数原型和头文件内容。 +```c++ +#pragma once +#include "lamina.hpp" + + +#include "ast.hpp" +#include "value.hpp" +#include +#include +#include +#include +#include +#include +#include + +// Forward declaration for error handling +void error_and_exit(const std::string& msg); + +// Stack frame for function call tracking +struct StackFrame { + std::string function_name; + std::string file_name; + int line_number; + + StackFrame(const std::string& func, const std::string& file, int line) + : function_name(func), file_name(file), line_number(line) {} +}; + +// Enhanced runtime error class with stack trace support +class RuntimeError : public std::exception { +public: + std::string message; + std::vector stack_trace; + + RuntimeError(const std::string& msg) : message(msg) {} + RuntimeError(const std::string& msg, const std::vector& trace) + : message(msg), stack_trace(trace) {} + + const char* what() const noexcept override { + return message.c_str(); + } +}; + +// Exception for return statements +class ReturnException : public std::exception { +public: + Value value; + explicit ReturnException(const Value& v) : value(v) {} +}; + + +// Exception for break statements +class BreakException : public std::exception { +public: + BreakException() = default; +}; + + +// Exception for continue statements +class ContinueException : public std::exception { +public: + ContinueException() = default; +}; + +class LAMINA_EXPORT Interpreter { + // 禁止拷贝,允许移动 + Interpreter(const Interpreter&) = delete; + Interpreter& operator=(const Interpreter&) = delete; + Interpreter(Interpreter&&) = default; + Interpreter& operator=(Interpreter&&) = default; +public: + Interpreter() { + register_builtin_functions(); + } + void execute(const std::unique_ptr& node); + Value eval(const ASTNode* node); + // Print all variables in current scope + void printVariables() const; + void add_function(const std::string& name, FuncDefStmt* func); + // Stack trace management + void push_frame(const std::string& function_name, const std::string& file_name = "