diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 710ff7ce..fc9abff5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,120 +24,120 @@ jobs: compiler: msvc steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up build environment (Ubuntu) - if: matrix.os == 'ubuntu-latest' - run: | - sudo apt-get update - sudo apt-get install -y build-essential - if [ "${{ matrix.compiler }}" = "clang++" ]; then - sudo apt-get install -y clang - fi - - - name: Set up build environment (macOS) - if: matrix.os == 'macos-latest' - run: | - if [ "${{ matrix.compiler }}" = "clang++" ]; then - # clang++ is already available on macOS - echo "Using system clang++" - else - # Install GCC if needed - brew install gcc - fi - - - name: Set up MSVC (Windows) - if: matrix.os == 'windows-latest' && matrix.compiler == 'msvc' - uses: ilammy/msvc-dev-cmd@v1 - - - name: Set up MinGW (Windows) - if: matrix.os == 'windows-latest' && matrix.compiler == 'g++' - uses: msys2/setup-msys2@v2 - with: - msystem: MINGW64 - update: true - install: mingw-w64-x86_64-gcc - - - name: Build with GCC/Clang (Unix) - if: matrix.os != 'windows-latest' || matrix.compiler == 'g++' - working-directory: interpreter - shell: bash - run: | - if [ "${{ matrix.os }}" = "windows-latest" ]; then - # Use MSYS2 environment for MinGW - export PATH="/c/msys64/mingw64/bin:$PATH" - fi - - echo "Building with ${{ matrix.compiler }}..." - ${{ matrix.compiler }} --version - - # Compile the interpreter - ${{ matrix.compiler }} -std=c++17 -O2 -Wall -Wextra \ - main.cpp lexer.cpp parser.cpp interpreter.cpp \ - -o lamina${{ matrix.os == 'windows-latest' && '.exe' || '' }} - - echo "Build completed successfully!" - - - name: Build with MSVC (Windows) - if: matrix.os == 'windows-latest' && matrix.compiler == 'msvc' - working-directory: interpreter - run: | - echo "Building with MSVC..." - cl /EHsc /std:c++17 /O2 main.cpp lexer.cpp parser.cpp interpreter.cpp /Fe:lamina.exe - echo "Build completed successfully!" - - - name: Test basic functionality - working-directory: interpreter - shell: bash - run: | - echo "Testing basic functionality..." - - # Create a simple test file - if [ "${{ matrix.os }}" = "windows-latest" ]; then - echo 'print("Hello, Lamina!");' > test.lm - echo 'var x = 2 + 3;' >> test.lm - echo 'print("2 + 3 =", x);' >> test.lm - ./lamina.exe test.lm - else - echo 'print("Hello, Lamina!");' > test.lm - echo 'var x = 2 + 3;' >> test.lm - echo 'print("2 + 3 =", x);' >> test.lm - ./lamina test.lm - fi - - echo "Basic test passed!" - - - name: Run example programs - working-directory: interpreter - shell: bash - run: | - echo "Running example programs..." - - # Test all example files if they exist - if [ -d "examples" ]; then - for example in examples/*.lm; do - if [ -f "$example" ]; then - echo "Testing $example..." - if [ "${{ matrix.os }}" = "windows-latest" ]; then - ./lamina.exe "$example" || echo "Warning: $example failed" - else - ./lamina "$example" || echo "Warning: $example failed" + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up build environment (Ubuntu) + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt-get update + sudo apt-get install -y build-essential + if [ "${{ matrix.compiler }}" = "clang++" ]; then + sudo apt-get install -y clang + fi + + - name: Set up build environment (macOS) + if: matrix.os == 'macos-latest' + run: | + if [ "${{ matrix.compiler }}" = "clang++" ]; then + # clang++ is already available on macOS + echo "Using system clang++" + else + # Install GCC if needed + brew install gcc + fi + + - name: Set up MSVC (Windows) + if: matrix.os == 'windows-latest' && matrix.compiler == 'msvc' + uses: ilammy/msvc-dev-cmd@v1 + + - name: Set up MinGW (Windows) + if: matrix.os == 'windows-latest' && matrix.compiler == 'g++' + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: true + install: mingw-w64-x86_64-gcc + + - name: Build with GCC/Clang (Unix) + if: matrix.os != 'windows-latest' || matrix.compiler == 'g++' + working-directory: interpreter + shell: bash + run: | + if [ "${{ matrix.os }}" = "windows-latest" ]; then + # Use MSYS2 environment for MinGW + export PATH="/c/msys64/mingw64/bin:$PATH" + fi + + echo "Building with ${{ matrix.compiler }}..." + ${{ matrix.compiler }} --version + + ${{ matrix.compiler }} -std=c++17 -O2 -Wall -Wextra \ + main.cpp lexer.cpp parser.cpp interpreter.cpp module.cpp \ + -o lamina${{ matrix.os == 'windows-latest' && '.exe' || '' }} + + echo "Build completed successfully!" + + - name: Build with MSVC (Windows) + if: matrix.os == 'windows-latest' && matrix.compiler == 'msvc' + working-directory: interpreter + run: | + echo "Building with MSVC..." + # 编译命令中添加了module.cpp + cl /EHsc /std:c++17 /O2 main.cpp lexer.cpp parser.cpp interpreter.cpp module.cpp /Fe:lamina.exe + echo "Build completed successfully!" + + - name: Test basic functionality + working-directory: interpreter + shell: bash + run: | + echo "Testing basic functionality..." + + # Create a simple test file + if [ "${{ matrix.os }}" = "windows-latest" ]; then + echo 'print("Hello, Lamina!");' > test.lm + echo 'var x = 2 + 3;' >> test.lm + echo 'print("2 + 3 =", x);' >> test.lm + ./lamina.exe test.lm + else + echo 'print("Hello, Lamina!");' > test.lm + echo 'var x = 2 + 3;' >> test.lm + echo 'print("2 + 3 =", x);' >> test.lm + ./lamina test.lm + fi + + echo "Basic test passed!" + + - name: Run example programs + working-directory: interpreter + shell: bash + run: | + echo "Running example programs..." + + # Test all example files if they exist + if [ -d "examples" ]; then + for example in examples/*.lm; do + if [ -f "$example" ]; then + echo "Testing $example..." + if [ "${{ matrix.os }}" = "windows-latest" ]; then + ./lamina.exe "$example" || echo "Warning: $example failed" + else + ./lamina "$example" || echo "Warning: $example failed" + fi fi - fi - done - else - echo "No examples directory found, skipping example tests" - fi - - - name: Upload build artifacts - uses: actions/upload-artifact@v4 - with: - name: lamina-${{ matrix.os }}-${{ matrix.compiler }} - path: | - interpreter/lamina${{ matrix.os == 'windows-latest' && '.exe' || '' }} - interpreter/examples/ - retention-days: 30 + done + else + echo "No examples directory found, skipping example tests" + fi + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: lamina-${{ matrix.os }}-${{ matrix.compiler }} + path: | + interpreter/lamina${{ matrix.os == 'windows-latest' && '.exe' || '' }} + interpreter/examples/ + retention-days: 30 release: needs: build @@ -145,44 +145,44 @@ jobs: if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - path: artifacts - - - name: Create release assets - run: | - mkdir -p release - - # Package Linux build - if [ -f "artifacts/lamina-ubuntu-latest-g++/lamina" ]; then - cd artifacts/lamina-ubuntu-latest-g++ - tar -czf ../../release/lamina-linux-x64.tar.gz lamina examples/ - cd ../.. - fi - - # Package Windows build - if [ -f "artifacts/lamina-windows-latest-g++/lamina.exe" ]; then - cd artifacts/lamina-windows-latest-g++ - zip -r ../../release/lamina-windows-x64.zip lamina.exe examples/ - cd ../.. - fi - - # Package macOS build - if [ -f "artifacts/lamina-macos-latest-g++/lamina" ]; then - cd artifacts/lamina-macos-latest-g++ - tar -czf ../../release/lamina-macos-x64.tar.gz lamina examples/ - cd ../.. - fi - - ls -la release/ - - - name: Upload release assets - uses: actions/upload-artifact@v4 - with: - name: lamina-release-packages - path: release/ - retention-days: 90 + - name: Checkout code + uses: actions/checkout@v4 + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Create release assets + run: | + mkdir -p release + + # Package Linux build + if [ -f "artifacts/lamina-ubuntu-latest-g++/lamina" ]; then + cd artifacts/lamina-ubuntu-latest-g++ + tar -czf ../../release/lamina-linux-x64.tar.gz lamina examples/ + cd ../.. + fi + + # Package Windows build + if [ -f "artifacts/lamina-windows-latest-g++/lamina.exe" ]; then + cd artifacts/lamina-windows-latest-g++ + zip -r ../../release/lamina-windows-x64.zip lamina.exe examples/ + cd ../.. + fi + + # Package macOS build + if [ -f "artifacts/lamina-macos-latest-g++/lamina" ]; then + cd artifacts/lamina-macos-latest-g++ + tar -czf ../../release/lamina-macos-x64.tar.gz lamina examples/ + cd ../.. + fi + + ls -la release/ + + - name: Upload release assets + uses: actions/upload-artifact@v4 + with: + name: lamina-release-packages + path: release/ + retention-days: 90 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92977811..ec1326fe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,97 +9,97 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y build-essential g++ - - - name: Compile Lamina - working-directory: interpreter - run: | - echo "Compiling Lamina interpreter..." - g++ -std=c++17 -Wall -Wextra -O2 \ - main.cpp lexer.cpp parser.cpp interpreter.cpp \ - -o lamina - echo " Compilation successful!" - - - name: Basic functionality test - working-directory: interpreter - run: | - echo "Running basic tests..." - - # Test 1: Hello World - echo 'print("Hello, Lamina!");' > test1.lm - ./lamina test1.lm - - # Test 2: Basic arithmetic - echo 'var x = 2 + 3 * 4;' > test2.lm - echo 'print("2 + 3 * 4 =", x);' >> test2.lm - ./lamina test2.lm - - # Test 3: Precise fractions - echo 'var fraction = 16 / 9;' > test3.lm - echo 'print("16/9 =", fraction);' >> test3.lm - ./lamina test3.lm - - # Test 4: Functions - echo 'func add(a, b) { return a + b; }' > test4.lm - echo 'var result = add(10, 20);' >> test4.lm - echo 'print("add(10, 20) =", result);' >> test4.lm - ./lamina test4.lm - - # Test 5: Include statement syntax (should show error for old syntax) - echo 'include test_module;' > test5.lm - echo 'print("This should not execute");' >> test5.lm - if ./lamina test5.lm 2>&1 | grep -q "Include statement requires a quoted string"; then - echo " Include syntax enforcement working correctly" - else - echo "❌ Include syntax enforcement failed" - exit 1 - fi - - # Test 6: Correct include syntax (should parse correctly) - echo 'print("Testing correct include syntax");' > test6.lm - echo 'include "nonexistent";' >> test6.lm - if ./lamina test6.lm 2>&1 | grep -q "Cannot load module"; then - echo " Correct include syntax parsing working" - else - echo "❌ Correct include syntax parsing failed" - exit 1 - fi - - echo " All basic tests passed!" - - - name: Test example programs - working-directory: interpreter - run: | - echo "Testing example programs..." - - if [ -d "examples" ]; then - for example in examples/*.lm; do - if [ -f "$example" ]; then - echo "Testing $(basename "$example")..." - ./lamina "$example" || echo "⚠️ Warning: $example execution failed" - fi - done - else - echo "ℹ️ No examples directory found" - fi - - - name: Check for memory leaks (with valgrind) - working-directory: interpreter - run: | - echo "Installing valgrind for memory leak detection..." - sudo apt-get install -y valgrind - - echo "Running memory leak test..." - echo 'var x = 42; print("x =", x);' > memtest.lm - - if valgrind --leak-check=full --error-exitcode=1 ./lamina memtest.lm 2>&1; then - echo " No memory leaks detected" - else - echo "⚠️ Memory leaks detected, but continuing..." - fi + - name: Checkout + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y build-essential g++ + + - name: Compile Lamina + working-directory: interpreter + run: | + echo "Compiling Lamina interpreter..." + g++ -std=c++17 -Wall -Wextra -O2 \ + main.cpp lexer.cpp parser.cpp interpreter.cpp module.cpp \ + -o lamina + echo " Compilation successful!" + + - name: Basic functionality test + working-directory: interpreter + run: | + echo "Running basic tests..." + + # Test 1: Hello World + echo 'print("Hello, Lamina!");' > test1.lm + ./lamina test1.lm + + # Test 2: Basic arithmetic + echo 'var x = 2 + 3 * 4;' > test2.lm + echo 'print("2 + 3 * 4 =", x);' >> test2.lm + ./lamina test2.lm + + # Test 3: Precise fractions + echo 'var fraction = 16 / 9;' > test3.lm + echo 'print("16/9 =", fraction);' >> test3.lm + ./lamina test3.lm + + # Test 4: Functions + echo 'func add(a, b) { return a + b; }' > test4.lm + echo 'var result = add(10, 20);' >> test4.lm + echo 'print("add(10, 20) =", result);' >> test4.lm + ./lamina test4.lm + + # Test 5: Include statement syntax (should show error for old syntax) + echo 'include test_module;' > test5.lm + echo 'print("This should not execute");' >> test5.lm + if ./lamina test5.lm 2>&1 | grep -q "Include statement requires a quoted string"; then + echo " Include syntax enforcement working correctly" + else + echo "❌ Include syntax enforcement failed" + exit 1 + fi + + # Test 6: Correct include syntax (should parse correctly) + echo 'print("Testing correct include syntax");' > test6.lm + echo 'include "nonexistent";' >> test6.lm + if ./lamina test6.lm 2>&1 | grep -q "Cannot load module"; then + echo " Correct include syntax parsing working" + else + echo "❌ Correct include syntax parsing failed" + exit 1 + fi + + echo " All basic tests passed!" + + - name: Test example programs + working-directory: interpreter + run: | + echo "Testing example programs..." + + if [ -d "examples" ]; then + for example in examples/*.lm; do + if [ -f "$example" ]; then + echo "Testing $(basename "$example")..." + ./lamina "$example" || echo "⚠️ Warning: $example execution failed" + fi + done + else + echo "ℹ️ No examples directory found" + fi + + - name: Check for memory leaks (with valgrind) + working-directory: interpreter + run: | + echo "Installing valgrind for memory leak detection..." + sudo apt-get install -y valgrind + + echo "Running memory leak test..." + echo 'var x = 42; print("x =", x);' > memtest.lm + + if valgrind --leak-check=full --error-exitcode=1 ./lamina memtest.lm 2>&1; then + echo " No memory leaks detected" + else + echo "⚠️ Memory leaks detected, but continuing..." + fi 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/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 = "