From 7ddf03d4c9a32239e3bc95c51703848ccbc5f1bd Mon Sep 17 00:00:00 2001 From: PuqiAR Date: Wed, 28 Jan 2026 18:30:31 +0800 Subject: [PATCH] =?UTF-8?q?v0.4.3-alpha=20[Fix]=20=E4=BF=AE=E5=A4=8Dbuilti?= =?UTF-8?q?n=20type=E7=9A=84=E6=96=B9=E6=B3=95=E5=8F=AF=E8=83=BD=E4=BC=9A?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=20bad=5Fvariant=5Faccess=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E3=80=82=E7=8E=B0=E5=9C=A8Function=E4=B8=A5=E6=A0=BC?= =?UTF-8?q?=E5=8C=BA=E5=88=863=E4=B8=AD=E5=87=BD=E6=95=B0=EF=BC=9A=20=20?= =?UTF-8?q?=20=20=20Normal,=20Builtin,=20MemberType=20[Feat]=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E4=BA=86=20Repl!=20=E9=80=9A=E8=BF=87=20Fig=20-r/--re?= =?UTF-8?q?pl=E8=BF=9B=E5=85=A5=E3=80=82=E7=9B=AE=E5=89=8D=E5=BF=85?= =?UTF-8?q?=E9=A1=BB=E5=86=99=E5=88=86=E5=8F=B7=EF=BC=8C=E4=B8=94=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E4=BA=A7=E7=94=9F=E6=97=B6=E5=9B=A0=E4=B8=BA=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E4=B8=8D=E5=88=B0=E6=BA=90=E6=96=87=E4=BB=B6=E4=BC=9A?= =?UTF-8?q?=E6=9C=89=E6=84=8F=E6=83=B3=E4=B8=8D=E5=88=B0=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20:(=20[Impl]=20=E7=B2=BE=E7=AE=80=E4=BA=86Context?= =?UTF-8?q?=E7=B1=BB=EF=BC=8C=E5=87=8F=E5=B0=91=E5=86=85=E5=AD=98=E5=8D=A0?= =?UTF-8?q?=E7=94=A8=E3=80=82=E5=BD=93=E7=84=B6=EF=BC=8C=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=86=85=E7=BD=AE=E5=87=BD=E6=95=B0=E5=90=8D=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E4=B8=8D=E4=BA=86=E4=BA=86=20=20=20=20=20=20=20=20=20=E5=90=8E?= =?UTF-8?q?=E7=BB=AD=E7=9A=84=20Fig=20lang=20standard=20(=E7=9B=AE?= =?UTF-8?q?=E5=89=8D=E8=BF=98=E6=B2=A1=E6=9C=89)=E4=B8=AD=E5=86=85?= =?UTF-8?q?=E7=BD=AE=E5=87=BD=E6=95=B0(MemberType/Builtin)=E4=B8=A5?= =?UTF-8?q?=E6=A0=BC=E6=97=A0=E5=BC=82=E5=B8=B8=E6=8A=9B=E5=87=BA=20[Feat]?= =?UTF-8?q?=20=E5=86=99=E4=BA=86=E4=B8=AASimple=20Compiler=20=E5=92=8C=20B?= =?UTF-8?q?ytecode=20VM=E4=BD=9C=E4=B8=BA=E6=B5=8B=E8=AF=95=E3=80=82?= =?UTF-8?q?=E6=80=A7=E8=83=BD=E6=8B=89=E8=B7=A8=E3=80=82=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=9B=AE=E5=89=8D=E5=81=9C=E6=AD=A2=20[...]=20=E5=89=A9?= =?UTF-8?q?=E4=B8=8B=E7=9A=84=E5=BF=98=E4=BA=86~?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Ast/astBase.hpp | 4 +- src/Bytecode/Bytecode.hpp | 125 ----- src/Bytecode/CallFrame.hpp | 17 + src/Bytecode/Chunk.hpp | 25 + src/Bytecode/CompileError.hpp | 22 + src/Bytecode/CompiledFunction.hpp | 21 + src/Bytecode/Instruction.hpp | 69 +++ src/Bytecode/vm_test_main.cpp | 107 +++++ src/Compiler/Compiler.cpp | 21 - src/Compiler/Compiler.hpp | 32 -- src/Core/core.hpp | 2 +- src/Core/fig_string.hpp | 2 +- src/Evaluator/Context/context.hpp | 51 +- src/Evaluator/Core/Eval.cpp | 27 +- src/Evaluator/Core/EvalBinary.cpp | 9 + src/Evaluator/Core/EvalFunctionCall.cpp | 18 +- src/Evaluator/Core/EvalInitExpr.cpp | 12 +- src/Evaluator/Core/EvalLvObject.cpp | 32 +- src/Evaluator/Core/EvalStatement.cpp | 21 +- src/Evaluator/Value/IntPool.hpp | 5 + src/Evaluator/Value/function.hpp | 120 ++++- src/Evaluator/Value/value.hpp | 591 ++++++++++++------------ src/Evaluator/evaluator.hpp | 1 + src/IR/IR.hpp | 62 +++ src/IR/IRInterpreter.hpp | 74 +++ src/IR/ir_test_main.cpp | 14 + src/Lexer/lexer.cpp | 6 +- src/Repl/Repl.cpp | 65 +++ src/Repl/Repl.hpp | 54 +++ src/VMValue/VMValue.cpp | 29 -- src/VMValue/VMValue.hpp | 91 ---- src/VirtualMachine/VirtualMachine.cpp | 225 +++++++++ src/VirtualMachine/VirtualMachine.hpp | 75 +++ src/VirtualMachine/main.cpp | 43 -- src/{Evaluator => }/main.cpp | 13 + xmake.lua | 116 ++--- 36 files changed, 1393 insertions(+), 808 deletions(-) delete mode 100644 src/Bytecode/Bytecode.hpp create mode 100644 src/Bytecode/CallFrame.hpp create mode 100644 src/Bytecode/Chunk.hpp create mode 100644 src/Bytecode/CompileError.hpp create mode 100644 src/Bytecode/CompiledFunction.hpp create mode 100644 src/Bytecode/Instruction.hpp create mode 100644 src/Bytecode/vm_test_main.cpp delete mode 100644 src/Compiler/Compiler.cpp delete mode 100644 src/Compiler/Compiler.hpp create mode 100644 src/IR/IR.hpp create mode 100644 src/IR/IRInterpreter.hpp create mode 100644 src/IR/ir_test_main.cpp create mode 100644 src/Repl/Repl.cpp create mode 100644 src/Repl/Repl.hpp delete mode 100644 src/VMValue/VMValue.cpp delete mode 100644 src/VMValue/VMValue.hpp create mode 100644 src/VirtualMachine/VirtualMachine.cpp create mode 100644 src/VirtualMachine/VirtualMachine.hpp delete mode 100644 src/VirtualMachine/main.cpp rename src/{Evaluator => }/main.cpp (97%) diff --git a/src/Ast/astBase.hpp b/src/Ast/astBase.hpp index a406b44..8853685 100644 --- a/src/Ast/astBase.hpp +++ b/src/Ast/astBase.hpp @@ -131,9 +131,9 @@ namespace Fig::Ast return FString(std::format("", typeName().toBasicString(), aai.line, aai.column)); } - AstAddressInfo getAAI() { return aai; } + AstAddressInfo getAAI() const { return aai; } - AstType getType() { return type; } + AstType getType() const { return type; } }; class StatementAst : public _AstBase diff --git a/src/Bytecode/Bytecode.hpp b/src/Bytecode/Bytecode.hpp deleted file mode 100644 index 4f3c01d..0000000 --- a/src/Bytecode/Bytecode.hpp +++ /dev/null @@ -1,125 +0,0 @@ -#pragma once - -/* - -从树遍历到虚拟机! - -*/ - -#include "Core/fig_string.hpp" -#include "Utils/magic_enum/magic_enum.hpp" -#include -#include -#include - -namespace Fig -{ - using OpCodeType = uint8_t; - enum class Bytecode : OpCodeType - { - HALT = 0x01, // 程序终止,后跟 8 位退出码 - - POP = 0x10, - - LOAD_NULL = 0x20, - LOAD_TRUE = 0x21, - LOAD_FALSE = 0x22, - LOAD_CON8 = 0x23, // 跟 8 位索引 (0 - 255) - LOAD_CON16 = 0x24, // 跟 16 位索引 (255 - 65535) - LOAD_CON32 = 0x25, // 跟 32 位索引 (65536 - 2^32-1) - - ADD = 0x30, // + - SUB = 0x31, // - - MUL = 0x32, // * - DIV = 0x33, // / - MOD = 0x34, // % - - AND = 0x40, // & - OR = 0x41, // | - XOR = 0x42, // ~ - NOT = 0x43, // ! (not) - - EQ = 0x50, // == - GT = 0x51, // > - GTEQ = 0x52, // >= - LT = 0x53, // < - LTEQ = 0x54, // <= - - JUMP16 = 0x60, // 跟 16 位索引 无条件 - JUMP32 = 0x61, // 跟 32 位索引 无条件 - - JUMP16_IF_TRUE = 0x62, // 跟 16 位索引 栈顶为真跳转 - JUMP32_IF_TRUE = 0x63, // 跟 32 位索引 栈顶为真跳转 - - JUMP16_IF_FALSE = 0x64, // 跟 16 位索引 栈顶为假跳转 - JUMP32_IF_FALSE = 0x65, // 跟 32 位索引 栈顶为假跳转 - - LOAD_LOCAL16 = 0x70, // 后跟 16 位索引 - LOAD_LOCAL32 = 0x71, // 后跟 32 位索引 - }; - - inline FString bytecode2string(Bytecode code) - { - const std::string_view &name = magic_enum::enum_name(code); - return FString(FStringView(name)); - } - - inline FString reverseCompile(const std::vector &src) - { - assert(src.size() >= 1); - - FString result; - - using enum Bytecode; - for (size_t i = 0; i < src.size();) - { - Bytecode code = Bytecode(src[i]); - switch (code) - { - case HALT: { - uint8_t quitCode = src[++i]; - result += FString(std::format("HALT {}", static_cast(quitCode))) + u8"\n"; - break; - } - case LOAD_CON8: { - uint8_t id = src[++i]; - result += FString(std::format("LOAD_CON8 {}", static_cast(id))) + u8"\n"; - break; - } - case LOAD_CON16: - case JUMP16: - case JUMP16_IF_TRUE: - case JUMP16_IF_FALSE: - case LOAD_LOCAL16: { - uint8_t high = src[++i]; - uint8_t low = src[++i]; - int32_t id = (high << 8) | low; - result += FString(std::format("{} {}", bytecode2string(code).toBasicString(), id)) - + u8"\n"; - break; - } - - case LOAD_CON32: - case JUMP32: - case JUMP32_IF_TRUE: - case JUMP32_IF_FALSE: - case LOAD_LOCAL32: { - uint32_t b0 = static_cast(src[++i]); - uint32_t b1 = static_cast(src[++i]); - uint32_t b2 = static_cast(src[++i]); - uint32_t b3 = static_cast(src[++i]); - - uint32_t id = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; - result += FString(std::format("{} {}", bytecode2string(code).toBasicString(), id)) - + u8"\n"; - break; - } - default: { - result += bytecode2string(code) + u8"\n"; - } - } - ++i; - } - return result; - } -}; // namespace Fig \ No newline at end of file diff --git a/src/Bytecode/CallFrame.hpp b/src/Bytecode/CallFrame.hpp new file mode 100644 index 0000000..d0be5ed --- /dev/null +++ b/src/Bytecode/CallFrame.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +namespace Fig +{ + struct CallFrame + { + uint64_t ip; // 函数第一个指令 index + uint64_t base; // 第一个参数在栈中位置偏移量 + + CompiledFunction fn; // 编译过的函数体 + }; + +}; \ No newline at end of file diff --git a/src/Bytecode/Chunk.hpp b/src/Bytecode/Chunk.hpp new file mode 100644 index 0000000..b85b317 --- /dev/null +++ b/src/Bytecode/Chunk.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +#include + +namespace Fig +{ + struct ChunkAddressInfo + { + FString sourcePath; + std::vector sourceLines; + }; + + struct Chunk + { + Instructions ins; // vector + std::vector constants; // 常量池 + + std::vector instructions_addr; // 下标和ins对齐,表示每个Instruction对应的地址 + ChunkAddressInfo addr; // 代码块独立Addr + }; +}; \ No newline at end of file diff --git a/src/Bytecode/CompileError.hpp b/src/Bytecode/CompileError.hpp new file mode 100644 index 0000000..42dd218 --- /dev/null +++ b/src/Bytecode/CompileError.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace Fig +{ + class CompileError : public AddressableError + { + using AddressableError::AddressableError; + + virtual FString toString() const override + { + std::string msg = std::format("[CompileError] {} in [{}] {}", + this->message.toBasicString(), + this->src_loc.file_name(), + this->src_loc.function_name()); + return FString(msg); + } + + virtual FString getErrorType() const override { return FString(u8"CompileError"); } + }; +}; \ No newline at end of file diff --git a/src/Bytecode/CompiledFunction.hpp b/src/Bytecode/CompiledFunction.hpp new file mode 100644 index 0000000..1ec9125 --- /dev/null +++ b/src/Bytecode/CompiledFunction.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +namespace Fig +{ + struct CompiledFunction + { + + Chunk chunk; // 函数代码块 + + FString name; // 函数名 + uint64_t posArgCount; // 位置参数数量 + uint64_t defArgCount; // 默认参数数量 + bool variadicPara; // 可变参数(是:最后为可变,否:不可变) + + uint64_t localCount; // 局部变量数量(不包括参数) + uint64_t slotCount; // = 总参数数量 + 局部变量数量 + }; +}; \ No newline at end of file diff --git a/src/Bytecode/Instruction.hpp b/src/Bytecode/Instruction.hpp new file mode 100644 index 0000000..d042ef8 --- /dev/null +++ b/src/Bytecode/Instruction.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include + +namespace Fig +{ + using u8 = uint8_t; + enum class OpCode : u8 + { + HALT = 0, // 程序结束 + RETURN, + + LOAD_LOCAL, + LOAD_CONST, + + STORE_LOCAL, + + LT, + LTET, + GT, + GTET, + ADD, + SUB, + MUL, + DIV, + + JUMP, // + 64 offset (int64_t) + JUMP_IF_FALSE, // + 64 offset (int64_t) + + CALL, + }; + + static constexpr int MAX_LOCAL_COUNT = UINT64_MAX; + static constexpr int MAX_CONSTANT_COUNT = UINT64_MAX; + static constexpr int MAX_FUNCTION_ARG_COUNT = UINT64_MAX; + + inline OpCode getLastOpCode() + { + return OpCode::RETURN; + } + + struct InstructionAddressInfo + { + size_t line, column; + }; + + using ProgramCounter = uint64_t; + using InstructionPoint = uint64_t; + + class Instruction + { + public: + OpCode code; + + int64_t operand; + + Instruction(OpCode _code) : code(_code) {} + + Instruction(OpCode _code, int64_t _operand) + { + code = _code; + operand = _operand; + } + }; + + using Instructions = std::vector; +}; // namespace Fig \ No newline at end of file diff --git a/src/Bytecode/vm_test_main.cpp b/src/Bytecode/vm_test_main.cpp new file mode 100644 index 0000000..7fff430 --- /dev/null +++ b/src/Bytecode/vm_test_main.cpp @@ -0,0 +1,107 @@ +#include +#include +#include +#include + +#include +#include + +using namespace Fig; + +int main() +{ + /* + func fib(x) + { + if x <= 1 + { + return x; + } + return fib(x - 1) + fib(x - 2); + } + */ + + // ---------------- fib ---------------- + + Instructions fib_ins{ + /* 0 */ {OpCode::LOAD_LOCAL, 0}, // x + /* 1 */ {OpCode::LOAD_CONST, 0}, // 1 + /* 2 */ {OpCode::LTET}, // x <= 1 + /* 3 */ {OpCode::JUMP_IF_FALSE, 2}, // false -> jump to 6 + + /* 4 */ {OpCode::LOAD_LOCAL, 0}, // return x + /* 5 */ {OpCode::RETURN}, + + /* 6 */ {OpCode::LOAD_LOCAL, 0}, // x + /* 7 */ {OpCode::LOAD_CONST, 0}, // 1 + /* 8 */ {OpCode::SUB}, // x - 1 + /* 9 */ {OpCode::LOAD_CONST, 2}, // fib + /* 10 */ {OpCode::CALL, 1}, // fib(x-1) + + /* 11 */ {OpCode::LOAD_LOCAL, 0}, // x + /* 12 */ {OpCode::LOAD_CONST, 1}, // 2 + /* 13 */ {OpCode::SUB}, // x - 2 + /* 14 */ {OpCode::LOAD_CONST, 2}, // fib + /* 15 */ {OpCode::CALL, 1}, // fib(x-2) + + /* 16 */ {OpCode::ADD}, + /* 17 */ {OpCode::RETURN}, + }; + + std::vector fib_consts{ + Object((int64_t) 1), // 0 + Object((int64_t) 2), // 1 + Object(), // 2 fib (回填) + }; + + CompiledFunction fib_fn{ + {}, + u8"fib", + 1, // posArgCount + 0, + false, + 0, // localCount + 1 // slotCount = 参数 x + }; + + // fib 自引用 + fib_consts[2] = Object(Function(&fib_fn)); + + Chunk fib_chunk{fib_ins, fib_consts, {}, ChunkAddressInfo{}}; + + fib_fn.chunk = fib_chunk; + + // ---------------- main ---------------- + + Instructions main_ins{ + {OpCode::LOAD_CONST, 0}, // 30 + {OpCode::LOAD_CONST, 1}, // fib + {OpCode::CALL, 1}, + {OpCode::RETURN}, + }; + + std::vector main_consts{ + Object((int64_t)251), // 0 + Object(Function(&fib_fn)), // 1 + }; + + Chunk main_chunk{main_ins, main_consts, {}, ChunkAddressInfo{}}; + + CompiledFunction main_fn{main_chunk, u8"main", 0, 0, false, 0, 0}; + + CallFrame entry{.ip = 0, .base = 0, .fn = main_fn}; + + VirtualMachine vm(entry); + + using Clock = std::chrono::high_resolution_clock; + + auto start = Clock::now(); + Object result = vm.Execute(); + auto end = Clock::now(); + + auto duration_secs = std::chrono::duration_cast(end - start).count(); + auto duration_ms = std::chrono::duration_cast(end - start).count(); + + std::cout << result.toString().toBasicString() << "\n"; + std::cout << "cost: " << duration_secs << "s. " << duration_ms << "ms" << "\n"; +} diff --git a/src/Compiler/Compiler.cpp b/src/Compiler/Compiler.cpp deleted file mode 100644 index c8d8d8d..0000000 --- a/src/Compiler/Compiler.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include -#include - -namespace Fig -{ - void Compiler::compile(Ast::Statement stmt) - { - using enum Ast::AstType; - using namespace Ast; - Ast::AstType type = stmt->getType(); - - switch (type) - { - case VarDefSt: { - auto vd = std::static_pointer_cast(stmt); - const FString name = vd->name; - } - } - } -}; // namespace Fig \ No newline at end of file diff --git a/src/Compiler/Compiler.hpp b/src/Compiler/Compiler.hpp deleted file mode 100644 index 3612283..0000000 --- a/src/Compiler/Compiler.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "Ast/astBase.hpp" -#include -#include -#include - -#include - -namespace Fig -{ - class Compiler - { - private: - std::vector source; - std::vector output; // std::vector - - std::vector constants; - public: - std::vector getOutput() const { return output; } - std::vector getConstantPool() const { return constants; } - - Compiler() {} - Compiler(std::vector _source) : source(std::move(_source)) {} - - void compile_expr(Ast::Expression); - - void compile(Ast::Statement); - - void CompileAll(); - }; -}; // namespace Fig \ No newline at end of file diff --git a/src/Core/core.hpp b/src/Core/core.hpp index a49dec2..2e07843 100644 --- a/src/Core/core.hpp +++ b/src/Core/core.hpp @@ -4,7 +4,7 @@ #include #include -#define __FCORE_VERSION "0.4.2-alpha" +#define __FCORE_VERSION "0.4.3-alpha" #if defined(_WIN32) #define __FCORE_PLATFORM "Windows" diff --git a/src/Core/fig_string.hpp b/src/Core/fig_string.hpp index a676436..853e4a4 100644 --- a/src/Core/fig_string.hpp +++ b/src/Core/fig_string.hpp @@ -41,7 +41,7 @@ namespace Fig FString operator+(const FString &x) { - return FString(toBasicString() + x.toBasicString()); + return FString(static_cast(*this) + static_cast(x)); } FString operator+(const char8_t *c) { diff --git a/src/Evaluator/Context/context.hpp b/src/Evaluator/Context/context.hpp index 3b850fc..ef202e4 100644 --- a/src/Evaluator/Context/context.hpp +++ b/src/Evaluator/Context/context.hpp @@ -1,9 +1,11 @@ #pragma once +#include "Evaluator/Value/function.hpp" #include #include #include #include +#include #include #include #include @@ -30,8 +32,8 @@ namespace Fig FString scopeName; std::unordered_map> variables; - std::unordered_map functions; - std::unordered_map functionNames; + // std::unordered_map functions; + // std::unordered_map functionNames; // implRegistry std::unordered_map, TypeInfoHash> implRegistry; @@ -51,14 +53,24 @@ namespace Fig void merge(const Context &c) { variables.insert(c.variables.begin(), c.variables.end()); - functions.insert(c.functions.begin(), c.functions.end()); - functionNames.insert(c.functionNames.begin(), c.functionNames.end()); implRegistry.insert(c.implRegistry.begin(), c.implRegistry.end()); // structTypeNames.insert(c.structTypeNames.begin(), // c.structTypeNames.end()); } - std::unordered_map getFunctions() const { return functions; } + std::unordered_map getFunctions() const + { + std::unordered_map result; + for (auto &[name, slot] : variables) + { + if (slot->declaredType == ValueType::Function) + { + const Function &fn = slot->value->as(); + result[fn.id] = fn; + } + } + return result; + } std::shared_ptr get(const FString &name) { @@ -122,12 +134,6 @@ namespace Fig FString(std::format("Variable '{}' already defined in this scope", name.toBasicString()))); } variables[name] = std::make_shared(name, value, ti, am); - if (ti == ValueType::Function and value->getTypeInfo() == ValueType::Function) - { - auto &fn = value->as(); - functions[fn.id] = fn; - functionNames[fn.id] = name; - } // if (ti == ValueType::StructType) // { // auto &st = value->as(); @@ -144,25 +150,18 @@ namespace Fig } variables[name] = std::make_shared(name, target->value, ti, am, true, target); } - std::optional getFunction(std::size_t id) - { - auto it = functions.find(id); - if (it != functions.end()) { return it->second; } - else if (parent) { return parent->getFunction(id); } - else - { - return std::nullopt; - } - } + std::optional getFunctionName(std::size_t id) { - auto it = functionNames.find(id); - if (it != functionNames.end()) { return it->second; } - else if (parent) { return parent->getFunctionName(id); } - else + for (auto &[name, slot] : variables) { - return std::nullopt; + if (slot->declaredType == ValueType::Function) + { + const Function &fn = slot->value->as(); + if (fn.id == id) { return name; } + } } + return std::nullopt; } // std::optional getStructName(std::size_t id) // { diff --git a/src/Evaluator/Core/Eval.cpp b/src/Evaluator/Core/Eval.cpp index 089da45..a36c1d0 100644 --- a/src/Evaluator/Core/Eval.cpp +++ b/src/Evaluator/Core/Eval.cpp @@ -11,27 +11,27 @@ namespace Fig { case AstType::ValueExpr: { auto val = std::static_pointer_cast(exp); - assert(val != nullptr); + return val->val; } case AstType::VarExpr: { auto varExpr = std::static_pointer_cast(exp); - assert(varExpr != nullptr); + return evalVarExpr(varExpr, ctx).get(); // LvObject -> RvObject } case AstType::BinaryExpr: { auto bin = std::static_pointer_cast(exp); - assert(bin != nullptr); + return evalBinary(bin, ctx); } case AstType::UnaryExpr: { auto un = std::static_pointer_cast(exp); - assert(un != nullptr); + return evalUnary(un, ctx); } case AstType::TernaryExpr: { auto te = std::static_pointer_cast(exp); - assert(te != nullptr); + return evalTernary(te, ctx); } case AstType::MemberExpr: @@ -39,8 +39,7 @@ namespace Fig case AstType::FunctionCall: { auto fnCall = std::static_pointer_cast(exp); - assert(fnCall != nullptr); - + Ast::Expression callee = fnCall->callee; ObjectPtr fnObj = eval(callee, ctx); if (fnObj->getTypeInfo() != ValueType::Function) @@ -58,19 +57,19 @@ namespace Fig auto fnNameOpt = ctx->getFunctionName(fnId); if (!fnNameOpt && fn.closureContext) fnNameOpt = fn.closureContext->getFunctionName(fnId); - const FString &fnName = (fnNameOpt ? *fnNameOpt : u8""); + const FString &fnName = (fnNameOpt ? *fnNameOpt : u8" or builtin-type member function"); return evalFunctionCall(fn, fnCall->arg, fnName, ctx); } case AstType::FunctionLiteralExpr: { auto fnLiteral = std::static_pointer_cast(exp); - assert(fnLiteral != nullptr); + Ast::BlockStatement body = nullptr; if (fnLiteral->isExprMode()) { Ast::Expression exprBody = fnLiteral->getExprBody(); - assert(exprBody != nullptr); + const Ast::AstAddressInfo &aai = exprBody->getAAI(); Ast::Return st = std::make_shared(exprBody); @@ -83,7 +82,7 @@ namespace Fig else { body = fnLiteral->getBlockBody(); - assert(body != nullptr); + } Function fn(fnLiteral->paras, ValueType::Any, body, ctx /* @@ -94,13 +93,13 @@ namespace Fig } case AstType::InitExpr: { auto initExpr = std::static_pointer_cast(exp); - assert(initExpr != nullptr); + return evalInitExpr(initExpr, ctx); } case AstType::ListExpr: { auto lstExpr = std::static_pointer_cast(exp); - assert(lstExpr != nullptr); + List list; for (auto &exp : lstExpr->val) { list.push_back(eval(exp, ctx)); } @@ -109,7 +108,7 @@ namespace Fig case AstType::MapExpr: { auto mapExpr = std::static_pointer_cast(exp); - assert(mapExpr != nullptr); + Map map; for (auto &[key, value] : mapExpr->val) { map[eval(key, ctx)] = eval(value, ctx); } diff --git a/src/Evaluator/Core/EvalBinary.cpp b/src/Evaluator/Core/EvalBinary.cpp index 5e239de..4313bc9 100644 --- a/src/Evaluator/Core/EvalBinary.cpp +++ b/src/Evaluator/Core/EvalBinary.cpp @@ -1,3 +1,4 @@ +#include "Evaluator/Value/value.hpp" #include #include #include @@ -84,11 +85,19 @@ namespace Fig } case Operator::And: { ObjectPtr lhs = eval(lexp, ctx); + if (lhs->is() && !isBoolObjectTruthy(lhs)) + { + return Object::getFalseInstance(); // short-circuit + } ObjectPtr rhs = eval(rexp, ctx); return std::make_shared(*lhs && *rhs); }; case Operator::Or: { ObjectPtr lhs = eval(lexp, ctx); + if (lhs->is() && isBoolObjectTruthy(lhs)) + { + return Object::getTrueInstance(); + } ObjectPtr rhs = eval(rexp, ctx); return std::make_shared(*lhs || *rhs); }; diff --git a/src/Evaluator/Core/EvalFunctionCall.cpp b/src/Evaluator/Core/EvalFunctionCall.cpp index 6e554a3..2de0594 100644 --- a/src/Evaluator/Core/EvalFunctionCall.cpp +++ b/src/Evaluator/Core/EvalFunctionCall.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -11,7 +12,7 @@ namespace Fig { const Function &fnStruct = fn; Ast::FunctionCallArgs evaluatedArgs; - if (fnStruct.isBuiltin) + if (fnStruct.type == Function::Builtin || fnStruct.type == Function::MemberType) { for (const auto &argExpr : fnArgs.argv) { evaluatedArgs.argv.push_back(eval(argExpr, ctx)); } if (fnStruct.builtinParamCount != -1 && fnStruct.builtinParamCount != evaluatedArgs.getLength()) @@ -23,7 +24,14 @@ namespace Fig evaluatedArgs.getLength()), fnArgs.argv.back()); } - return fnStruct.builtin(evaluatedArgs.argv); + if (fnStruct.type == Function::Builtin) + { + return fnStruct.builtin(evaluatedArgs.argv); + } + else + { + return fnStruct.mtFn(nullptr, evaluatedArgs.argv); // wrapped member type function (`this` provided by evalMemberExpr) + } } // check argument, all types of parameters @@ -126,7 +134,7 @@ namespace Fig paramName = fnParas.defParas[defParamIndex].first; paramType = TypeInfo(fnParas.defParas[defParamIndex].second.first); } - AccessModifier argAm = AccessModifier::Const; + AccessModifier argAm = AccessModifier::Normal; newContext->def(paramName, paramType, argAm, evaluatedArgs.argv[j]); } goto ExecuteBody; @@ -138,7 +146,7 @@ namespace Fig { list.push_back(eval(exp, ctx)); // eval arguments in current scope } - newContext->def(fnParas.variadicPara, ValueType::List, AccessModifier::Const, std::make_shared(list)); + newContext->def(fnParas.variadicPara, ValueType::List, AccessModifier::Normal, std::make_shared(list)); goto ExecuteBody; } @@ -172,4 +180,4 @@ namespace Fig return retVal; } } -}; \ No newline at end of file +}; // namespace Fig \ No newline at end of file diff --git a/src/Evaluator/Core/EvalInitExpr.cpp b/src/Evaluator/Core/EvalInitExpr.cpp index 3669203..37e748b 100644 --- a/src/Evaluator/Core/EvalInitExpr.cpp +++ b/src/Evaluator/Core/EvalInitExpr.cpp @@ -288,11 +288,13 @@ namespace Fig [&argName](const Field &f) { return f.name == argName; }); if (fieldIt == structT.fields.end()) { - throw EvaluatorError(u8"StructFieldNotFoundError", - std::format("Field '{}' not found in structure '{}'", - argName.toBasicString(), - structName.toBasicString()), - initExpr); + // throw EvaluatorError(u8"StructFieldNotFoundError", + // std::format("Field '{}' not found in structure '{}'", + // argName.toBasicString(), + // structName.toBasicString()), + // initExpr); + initExpr->initMode = Positional; + return evalInitExpr(initExpr, ctx); } const Field &field = *fieldIt; if (!isTypeMatch(field.type, argVal, ctx)) diff --git a/src/Evaluator/Core/EvalLvObject.cpp b/src/Evaluator/Core/EvalLvObject.cpp index 6cd90b7..fd93001 100644 --- a/src/Evaluator/Core/EvalLvObject.cpp +++ b/src/Evaluator/Core/EvalLvObject.cpp @@ -35,8 +35,12 @@ namespace Fig { return LvObject(std::make_shared( member, - std::make_shared(Function(baseVal->getMemberFunction(member), - baseVal->getMemberFunctionParaCount(member))), + std::make_shared(Function( + [baseVal, member](ObjectPtr self, std::vector args) -> ObjectPtr { + if (self) { return baseVal->getMemberFunction(member)(self, args); } + return baseVal->getMemberFunction(member)(baseVal, args); + }, + baseVal->getMemberFunctionParaCount(member))), ValueType::Function, AccessModifier::PublicConst), ctx); // fake l-value @@ -105,7 +109,7 @@ namespace Fig } LvObject Evaluator::evalIndexExpr(Ast::IndexExpr ie, ContextPtr ctx) { - LvObject base = evalLv(ie->base, ctx); + RvObject base = eval(ie->base, ctx); RvObject index = eval(ie->index, ctx); const TypeInfo &type = base.get()->getTypeInfo(); @@ -125,12 +129,12 @@ namespace Fig { throw EvaluatorError( u8"IndexOutOfRangeError", - std::format("Index {} out of list `{}` range", indexVal, base.get()->toString().toBasicString()), + std::format("Index {} out of list `{}` range", indexVal, base->toString().toBasicString()), ie->index); } - return LvObject(base.get(), indexVal, LvObject::Kind::ListElement, ctx); + return LvObject(base, indexVal, LvObject::Kind::ListElement, ctx); } - else if (type == ValueType::Map) { return LvObject(base.get(), index, LvObject::Kind::MapElement, ctx); } + else if (type == ValueType::Map) { return LvObject(base, index, LvObject::Kind::MapElement, ctx); } else if (type == ValueType::String) { if (index->getTypeInfo() != ValueType::Int) @@ -140,22 +144,22 @@ namespace Fig std::format("Type `String` indices must be `Int`, got '{}'", prettyType(index).toBasicString()), ie->index); } - FString &string = base.get()->as(); + FString &string = base->as(); ValueType::IntClass indexVal = index->as(); if (indexVal >= string.length()) { throw EvaluatorError( u8"IndexOutOfRangeError", - std::format("Index {} out of string `{}` range", indexVal, base.get()->toString().toBasicString()), + std::format("Index {} out of string `{}` range", indexVal, base->toString().toBasicString()), ie->index); } - return LvObject(base.get(), indexVal, LvObject::Kind::StringElement, ctx); + return LvObject(base, indexVal, LvObject::Kind::StringElement, ctx); } else { throw EvaluatorError( u8"NoSubscriptableError", - std::format("`{}` object is not subscriptable", base.declaredType().toString().toBasicString()), + std::format("`{}` object is not subscriptable", base->getTypeInfo().toString().toBasicString()), ie->base); } } @@ -167,17 +171,17 @@ namespace Fig { case AstType::VarExpr: { Ast::VarExpr var = std::static_pointer_cast(exp); - assert(var != nullptr); + return evalVarExpr(var, ctx); } case AstType::MemberExpr: { Ast::MemberExpr me = std::static_pointer_cast(exp); - assert(me != nullptr); + return evalMemberExpr(me, ctx); } case AstType::IndexExpr: { Ast::IndexExpr ie = std::static_pointer_cast(exp); - assert(ie != nullptr); + return evalIndexExpr(ie, ctx); } default: { @@ -188,4 +192,4 @@ namespace Fig } } } -}; \ No newline at end of file +}; // namespace Fig \ No newline at end of file diff --git a/src/Evaluator/Core/EvalStatement.cpp b/src/Evaluator/Core/EvalStatement.cpp index e440410..1eb1db6 100644 --- a/src/Evaluator/Core/EvalStatement.cpp +++ b/src/Evaluator/Core/EvalStatement.cpp @@ -13,12 +13,11 @@ namespace Fig { case ImportSt: { auto i = std::static_pointer_cast(stmt); - assert(i != nullptr); return evalImportSt(i, ctx); } case VarDefSt: { auto varDef = std::static_pointer_cast(stmt); - assert(varDef != nullptr); + if (ctx->containsInThisScope(varDef->name)) { @@ -63,7 +62,7 @@ namespace Fig case FunctionDefSt: { auto fnDef = std::static_pointer_cast(stmt); - assert(fnDef != nullptr); + const FString &fnName = fnDef->name; if (ctx->containsInThisScope(fnName)) @@ -90,7 +89,7 @@ namespace Fig case StructSt: { auto stDef = std::static_pointer_cast(stmt); - assert(stDef != nullptr); + if (ctx->containsInThisScope(stDef->name)) { @@ -149,7 +148,7 @@ namespace Fig case InterfaceDefSt: { auto ifd = std::static_pointer_cast(stmt); - assert(ifd != nullptr); + const FString &interfaceName = ifd->name; @@ -170,7 +169,7 @@ namespace Fig case ImplementSt: { auto ip = std::static_pointer_cast(stmt); - assert(ip != nullptr); + TypeInfo structType(ip->structName); TypeInfo interfaceType(ip->interfaceName); @@ -407,7 +406,7 @@ namespace Fig case TrySt: { auto tryst = std::static_pointer_cast(stmt); - assert(tryst != nullptr); + ContextPtr tryCtx = std::make_shared( FString(std::format("", tryst->getAAI().line, tryst->getAAI().column)), ctx); @@ -446,7 +445,7 @@ namespace Fig case ThrowSt: { auto ts = std::static_pointer_cast(stmt); - assert(ts != nullptr); + ObjectPtr value = eval(ts->value, ctx); if (value->is()) @@ -458,7 +457,7 @@ namespace Fig case ReturnSt: { auto returnSt = std::static_pointer_cast(stmt); - assert(returnSt != nullptr); + ObjectPtr returnValue = Object::getNullInstance(); // default is null if (returnSt->retValue) returnValue = eval(returnSt->retValue, ctx); @@ -491,14 +490,12 @@ namespace Fig case ExpressionStmt: { auto exprStmt = std::static_pointer_cast(stmt); - assert(exprStmt != nullptr); - return StatementResult::normal(eval(exprStmt->exp, ctx)); } case BlockStatement: { auto block = std::static_pointer_cast(stmt); - assert(block != nullptr); + ContextPtr blockCtx = std::make_shared( FString(std::format("", block->getAAI().line, block->getAAI().column)), ctx); diff --git a/src/Evaluator/Value/IntPool.hpp b/src/Evaluator/Value/IntPool.hpp index 3038b74..55f5fe4 100644 --- a/src/Evaluator/Value/IntPool.hpp +++ b/src/Evaluator/Value/IntPool.hpp @@ -30,6 +30,11 @@ namespace Fig if (val >= CACHE_MIN && val <= CACHE_MAX) { return cache[val - CACHE_MIN]; } return std::make_shared(val); } + Object createIntCopy(ValueType::IntClass val) const + { + if (val >= CACHE_MIN && val <= CACHE_MAX) { return *cache[val - CACHE_MIN]; } + return Object(val); + } static const IntPool &getInstance() { diff --git a/src/Evaluator/Value/function.hpp b/src/Evaluator/Value/function.hpp index 27bed89..9896aab 100644 --- a/src/Evaluator/Value/function.hpp +++ b/src/Evaluator/Value/function.hpp @@ -11,51 +11,91 @@ namespace Fig { class Object; + class Function { public: std::size_t id; - Ast::FunctionParameters paras; - TypeInfo retType; - Ast::BlockStatement body; - bool isBuiltin = false; - std::function(const std::vector> &)> builtin; + enum FnType + { + Normal, + Builtin, + MemberType + } type; + + union + { + struct + { + Ast::FunctionParameters paras; + TypeInfo retType; + + Ast::BlockStatement body; + }; + std::function(const std::vector> &)> builtin; + std::function(std::shared_ptr, + const std::vector> &)> + mtFn; + }; + int builtinParamCount = -1; std::shared_ptr closureContext; // ===== Constructors ===== - Function() : - id(nextId()) {} + Function() : id(nextId()) {} - Function(Ast::FunctionParameters _paras, TypeInfo _retType, Ast::BlockStatement _body, ContextPtr _closureContext) : + Function(Ast::FunctionParameters _paras, + TypeInfo _retType, + Ast::BlockStatement _body, + ContextPtr _closureContext) : id(nextId()), // 分配唯一 ID paras(std::move(_paras)), retType(std::move(_retType)), body(std::move(_body)), closureContext(std::move(_closureContext)) { + type = Normal; } Function(std::function(const std::vector> &)> fn, int argc) : - id(nextId()), isBuiltin(true), builtin(fn), builtinParamCount(argc) {} + id(nextId()), type(Builtin), builtin(fn), builtinParamCount(argc) + { + type = Builtin; + } + + Function(std::function(std::shared_ptr, + const std::vector> &)> fn, + int argc) : + id(nextId()), type(MemberType), mtFn(fn), builtinParamCount(argc) + { + type = MemberType; + } // ===== Copy / Move ===== - Function(const Function &other) = default; - Function(Function &&) noexcept = default; - Function &operator=(const Function &) = default; - Function &operator=(Function &&) noexcept = default; + Function(const Function &other) + { + copyFrom(other); + } + Function &operator=(const Function &other) + { + if (this != &other) + { + destroy(); + copyFrom(other); + } + return *this; + }; + + ~Function() + { + destroy(); + } // ===== Comparison ===== - bool operator==(const Function &other) const noexcept - { - return id == other.id; - } - bool operator!=(const Function &other) const noexcept - { - return !(*this == other); - } + bool operator==(const Function &other) const noexcept { return id == other.id; } + bool operator!=(const Function &other) const noexcept { return !(*this == other); } private: static std::size_t nextId() @@ -63,5 +103,43 @@ namespace Fig static std::atomic counter{1}; return counter++; } + void destroy() + { + switch (type) + { + case Normal: + paras.~FunctionParameters(); + retType.~TypeInfo(); + body.~shared_ptr(); + break; + case Builtin: builtin.~function(); break; + case MemberType: mtFn.~function(); break; + } + } + + void copyFrom(const Function &other) + { + type = other.type; + id = nextId(); // 每个复制都生成新的ID + builtinParamCount = other.builtinParamCount; + closureContext = other.closureContext; + + switch (type) + { + case Normal: + new (¶s) Ast::FunctionParameters(other.paras); + new (&retType) TypeInfo(other.retType); + new (&body) Ast::BlockStatement(other.body); + break; + case Builtin: + new (&builtin) std::function(const std::vector> &)>( + other.builtin); + break; + case MemberType: + new (&mtFn) std::function( + std::shared_ptr, const std::vector> &)>(other.mtFn); + break; + } + } }; } // namespace Fig diff --git a/src/Evaluator/Value/value.hpp b/src/Evaluator/Value/value.hpp index fe33fe9..ee8b472 100644 --- a/src/Evaluator/Value/value.hpp +++ b/src/Evaluator/Value/value.hpp @@ -1,4 +1,5 @@ #pragma once +#include "Core/fig_string.hpp" #include #include #include @@ -8,6 +9,8 @@ #include #include +#include +#include #include #include #include @@ -41,26 +44,18 @@ namespace Fig struct Element { ObjectPtr value; - Element(ObjectPtr _value) : - value(_value) {} - - bool operator==(const Element &other) const - { - return *value == *other.value; - } + Element(ObjectPtr _value) : value(_value) {} - void deepCopy(const Element &e) - { - value = std::make_shared(*e.value); - } + bool operator==(const Element &other) const { return *value == *other.value; } + + void deepCopy(const Element &e) { value = std::make_shared(*e.value); } }; using List = std::vector; struct ValueKey { ObjectPtr value; - ValueKey(ObjectPtr _value) : - value(_value) {} + ValueKey(ObjectPtr _value) : value(_value) {} void deepCopy(const ValueKey &vk) { value = std::make_shared(*vk.value); } }; @@ -74,231 +69,237 @@ namespace Fig bool isTypeMatch(const TypeInfo &, ObjectPtr, ContextPtr); bool implements(const TypeInfo &, const TypeInfo &, ContextPtr); + using BuiltinTypeMemberFn = std::function)>; + class Object : public std::enable_shared_from_this { public: - using VariantType = std::variant< - ValueType::NullClass, - ValueType::IntClass, - ValueType::DoubleClass, - ValueType::StringClass, - ValueType::BoolClass, - Function, - StructType, - StructInstance, - List, - Map, - Module, - InterfaceType>; + using VariantType = std::variant; + + + + static std::unordered_map, TypeInfoHash> getMemberTypeFunctions() + { + static const std::unordered_map, TypeInfoHash> + memberTypeFunctions{ + {ValueType::Null, {}}, + {ValueType::Int, {}}, + {ValueType::Double, {}}, + {ValueType::String, + { + {u8"length", + [](ObjectPtr object, std::vector args) -> ObjectPtr { + if (args.size() != 0) + throw RuntimeError( + FString(std::format("`length` expects 0 arguments, {} got", args.size()))); + const FString &str = object->as(); + return std::make_shared(static_cast(str.length())); + }}, + {u8"replace", + [](ObjectPtr object, std::vector args) -> ObjectPtr { + if (args.size() != 2) + throw RuntimeError( + FString(std::format("`replace` expects 2 arguments, {} got", args.size()))); + FString &str = object->as(); + ObjectPtr arg1 = args[0]; + ObjectPtr arg2 = args[1]; + if (!arg1->is()) + { + throw RuntimeError(FString("`replace` arg 1 expects type Int")); + } + if (!arg2->is()) + { + throw RuntimeError(FString("`replace` arg 2 expects type String")); + } + str.realReplace(arg1->as(), arg2->as()); + return Object::getNullInstance(); + }}, + {u8"erase", + [](ObjectPtr object, std::vector args) -> ObjectPtr { + if (args.size() != 2) + throw RuntimeError( + FString(std::format("`erase` expects 2 arguments, {} got", args.size()))); + FString &str = object->as(); + ObjectPtr arg1 = args[0]; + ObjectPtr arg2 = args[1]; + if (!arg1->is()) + { + throw RuntimeError(FString("`erase` arg 1 expects type Int")); + } + if (!arg2->is()) + { + throw RuntimeError(FString("`erase` arg 2 expects type Int")); + } + ValueType::IntClass index = arg1->as(); + ValueType::IntClass n = arg2->as(); + if (index < 0 || n < 0) + { + throw RuntimeError(FString("`erase`: index and n must greater or equal to 0")); + } + if (index + n > str.length()) + { + throw RuntimeError(FString("`erase`: length is not long enough to erase")); + } + str.realErase(arg1->as(), arg2->as()); + return Object::getNullInstance(); + }}, + {u8"insert", + [](ObjectPtr object, std::vector args) -> ObjectPtr { + if (args.size() != 2) + throw RuntimeError( + FString(std::format("`insert` expects 2 arguments, {} got", args.size()))); + FString &str = object->as(); + ObjectPtr arg1 = args[0]; + ObjectPtr arg2 = args[1]; + if (!arg1->is()) + { + throw RuntimeError(FString("`insert` arg 1 expects type Int")); + } + if (!arg2->is()) + { + throw RuntimeError(FString("`insert` arg 2 expects type String")); + } + str.realInsert(arg1->as(), arg2->as()); + return Object::getNullInstance(); + }}, + }}, + {ValueType::Function, {}}, + {ValueType::StructType, {}}, + {ValueType::StructInstance, {}}, + {ValueType::List, + { + {u8"length", + [](ObjectPtr object, std::vector args) -> ObjectPtr { + if (args.size() != 0) + throw RuntimeError( + FString(std::format("`length` expects 0 arguments, {} got", args.size()))); + const List &list = object->as(); + return std::make_shared(static_cast(list.size())); + }}, + {u8"get", + [](ObjectPtr object, std::vector args) -> ObjectPtr { + if (args.size() != 1) + throw RuntimeError( + FString(std::format("`get` expects 1 arguments, {} got", args.size()))); + ObjectPtr arg = args[0]; + if (arg->getTypeInfo() != ValueType::Int) + throw RuntimeError( + FString(std::format("`get` argument 1 expects Int, {} got", + arg->getTypeInfo().toString().toBasicString()))); + ValueType::IntClass i = arg->as(); + const List &list = object->as(); + if (i >= list.size()) return Object::getNullInstance(); + return list[i].value; + }}, + {u8"push", + [](ObjectPtr object, std::vector args) -> ObjectPtr { + if (args.size() != 1) + throw RuntimeError( + FString(std::format("`push` expects 1 arguments, {} got", args.size()))); + ObjectPtr arg = args[0]; + List &list = object->as(); + list.push_back(arg); + return Object::getNullInstance(); + }}, + }}, + {ValueType::Map, + { + {u8"get", + [](ObjectPtr object, std::vector args) -> ObjectPtr { + if (args.size() != 1) + throw RuntimeError( + FString(std::format("`get` expects 1 arguments, {} got", args.size()))); + ObjectPtr index = args[0]; + const Map &map = object->as(); + if (!map.contains(index)) return Object::getNullInstance(); + return map.at(index); + }}, + {u8"contains", + [](ObjectPtr object, std::vector args) -> ObjectPtr { + if (args.size() != 1) + throw RuntimeError( + FString(std::format("`contains` expects 1 arguments, {} got", args.size()))); + ObjectPtr index = args[0]; + const Map &map = object->as(); + return std::make_shared(map.contains(index)); + }}, + }}, + {ValueType::Module, {}}, + {ValueType::InterfaceType, {}}, + }; + return memberTypeFunctions; + } + + + + static std::unordered_map, TypeInfoHash> getMemberTypeFunctionsParas() + { + static const std::unordered_map, TypeInfoHash> + memberTypeFunctionsParas{ + {ValueType::Null, {}}, + {ValueType::Int, {}}, + {ValueType::Double, {}}, + {ValueType::String, + { + {u8"length", 0}, + {u8"replace", 2}, + {u8"erase", 2}, + {u8"insert", 2}, + }}, + {ValueType::Function, {}}, + {ValueType::StructType, {}}, + {ValueType::StructInstance, {}}, + {ValueType::List, {{u8"length", 0}, {u8"get", 1}, {u8"push", 1}}}, + {ValueType::Map, + { + {u8"get", 1}, + {u8"contains", 1}, + }}, + {ValueType::Module, {}}, + {ValueType::InterfaceType, {}}, + }; + return memberTypeFunctionsParas; + } - std::unordered_map)>>, - TypeInfoHash> - memberTypeFunctions{ - {ValueType::Null, {}}, - {ValueType::Int, {}}, - {ValueType::Double, {}}, - {ValueType::String, { - {u8"length", [this](std::vector args) -> ObjectPtr { - if (args.size() != 0) - throw RuntimeError(FString( - std::format("`length` expects 0 arguments, {} got", args.size()))); - const FString &str = as(); - return std::make_shared(static_cast(str.length())); - }}, - {u8"replace", [this](std::vector args) -> ObjectPtr { - if (args.size() != 2) - throw RuntimeError(FString( - std::format("`replace` expects 2 arguments, {} got", args.size()))); - FString &str = as(); - ObjectPtr arg1 = args[0]; - ObjectPtr arg2 = args[1]; - if (!arg1->is()) - { - throw RuntimeError(FString( - "`replace` arg 1 expects type Int")); - } - if (!arg2->is()) - { - throw RuntimeError(FString( - "`replace` arg 2 expects type String")); - } - str.realReplace(arg1->as(), arg2->as()); - return Object::getNullInstance(); - }}, - {u8"erase", [this](std::vector args) -> ObjectPtr { - if (args.size() != 2) - throw RuntimeError(FString( - std::format("`erase` expects 2 arguments, {} got", args.size()))); - FString &str = as(); - ObjectPtr arg1 = args[0]; - ObjectPtr arg2 = args[1]; - if (!arg1->is()) - { - throw RuntimeError(FString( - "`erase` arg 1 expects type Int")); - } - if (!arg2->is()) - { - throw RuntimeError(FString( - "`erase` arg 2 expects type Int")); - } - ValueType::IntClass index = arg1->as(); - ValueType::IntClass n = arg2->as(); - if (index < 0 || n < 0) - { - throw RuntimeError(FString("`erase`: index and n must greater or equal to 0")); - } - if (index + n > str.length()) - { - throw RuntimeError(FString("`erase`: length is not long enough to erase")); - } - str.realErase(arg1->as(), arg2->as()); - return Object::getNullInstance(); - }}, - {u8"insert", [this](std::vector args) -> ObjectPtr { - if (args.size() != 2) - throw RuntimeError(FString( - std::format("`insert` expects 2 arguments, {} got", args.size()))); - FString &str = as(); - ObjectPtr arg1 = args[0]; - ObjectPtr arg2 = args[1]; - if (!arg1->is()) - { - throw RuntimeError(FString( - "`insert` arg 1 expects type Int")); - } - if (!arg2->is()) - { - throw RuntimeError(FString( - "`insert` arg 2 expects type String")); - } - str.realInsert(arg1->as(), arg2->as()); - return Object::getNullInstance(); - }}, - }}, - {ValueType::Function, {}}, - {ValueType::StructType, {}}, - {ValueType::StructInstance, {}}, - {ValueType::List, { - {u8"length", [this](std::vector args) -> ObjectPtr { - if (args.size() != 0) - throw RuntimeError(FString( - std::format("`length` expects 0 arguments, {} got", args.size()))); - const List &list = as(); - return std::make_shared(static_cast(list.size())); - }}, - {u8"get", [this](std::vector args) -> ObjectPtr { - if (args.size() != 1) - throw RuntimeError(FString( - std::format("`get` expects 1 arguments, {} got", args.size()))); - ObjectPtr arg = args[0]; - if (arg->getTypeInfo() != ValueType::Int) - throw RuntimeError(FString( - std::format("`get` argument 1 expects Int, {} got", arg->getTypeInfo().toString().toBasicString()))); - ValueType::IntClass i = arg->as(); - const List &list = as(); - if (i >= list.size()) - return Object::getNullInstance(); - return list[i].value; - }}, - {u8"push", [this](std::vector args) -> ObjectPtr { - if (args.size() != 1) - throw RuntimeError(FString( - std::format("`push` expects 1 arguments, {} got", args.size()))); - ObjectPtr arg = args[0]; - List &list = as(); - list.push_back(arg); - return Object::getNullInstance(); - }}, - }}, - {ValueType::Map, { - {u8"get", [this](std::vector args) -> ObjectPtr { - if (args.size() != 1) - throw RuntimeError(FString( - std::format("`get` expects 1 arguments, {} got", args.size()))); - ObjectPtr index = args[0]; - const Map &map = as(); - if (!map.contains(index)) - return Object::getNullInstance(); - return map.at(index); - }}, - {u8"contains", [this](std::vector args) -> ObjectPtr { - if (args.size() != 1) - throw RuntimeError(FString( - std::format("`contains` expects 1 arguments, {} got", args.size()))); - ObjectPtr index = args[0]; - const Map &map = as(); - return std::make_shared( - map.contains(index)); - }}, - }}, - {ValueType::Module, {}}, - {ValueType::InterfaceType, {}}, - }; - std::unordered_map, TypeInfoHash> memberTypeFunctionsParas{ - {ValueType::Null, {}}, - {ValueType::Int, {}}, - {ValueType::Double, {}}, - {ValueType::String, { - {u8"length", 0}, - {u8"replace", 2}, - {u8"erase", 2}, - {u8"insert", 2}, - }}, - {ValueType::Function, {}}, - {ValueType::StructType, {}}, - {ValueType::StructInstance, {}}, - {ValueType::List, {{u8"length", 0}, {u8"get", 1}, {u8"push", 1}}}, - {ValueType::Map, { - {u8"get", 1}, - {u8"contains", 1}, - }}, - {ValueType::Module, {}}, - {ValueType::InterfaceType, {}}, - }; bool hasMemberFunction(const FString &name) const { - return memberTypeFunctions.at(getTypeInfo()).contains(name); + return getMemberTypeFunctions().at(getTypeInfo()).contains(name); } - std::function)> getMemberFunction(const FString &name) const + BuiltinTypeMemberFn getMemberFunction(const FString &name) const { - return memberTypeFunctions.at(getTypeInfo()).at(name); + return getMemberTypeFunctions().at(getTypeInfo()).at(name); } int getMemberFunctionParaCount(const FString &name) const { - return memberTypeFunctionsParas.at(getTypeInfo()).at(name); + return getMemberTypeFunctionsParas().at(getTypeInfo()).at(name); } VariantType data; - Object() : - data(ValueType::NullClass{}) {} - Object(const ValueType::NullClass &n) : - data(n) {} - Object(const ValueType::IntClass &i) : - data(i) {} - explicit Object(const ValueType::DoubleClass &d) : - data(d) {} - Object(const ValueType::StringClass &s) : - data(s) {} - Object(const ValueType::BoolClass &b) : - data(b) {} - Object(const Function &f) : - data(f) {} - Object(const StructType &s) : - data(s) {} - Object(const StructInstance &s) : - data(s) {} - Object(const List &l) : - data(l) {} - Object(const Map &m) : - data(m) {} - Object(const Module &m) : - data(m) {} - Object(const InterfaceType &i) : - data(i) {} + Object() : data(ValueType::NullClass{}) {} + Object(const ValueType::NullClass &n) : data(n) {} + Object(const ValueType::IntClass &i) : data(i) {} + explicit Object(const ValueType::DoubleClass &d) : data(d) {} + Object(const ValueType::StringClass &s) : data(s) {} + Object(const ValueType::BoolClass &b) : data(b) {} + Object(const Function &f) : data(f) {} + Object(const StructType &s) : data(s) {} + Object(const StructInstance &s) : data(s) {} + Object(const List &l) : data(l) {} + Object(const Map &m) : data(m) {} + Object(const Module &m) : data(m) {} + Object(const InterfaceType &i) : data(i) {} Object(const Object &) = default; Object(Object &&) noexcept = default; @@ -359,49 +360,50 @@ namespace Fig TypeInfo getTypeInfo() const { - return std::visit([](auto &&val) -> TypeInfo { - using T = std::decay_t; + return std::visit( + [](auto &&val) -> TypeInfo { + using T = std::decay_t; - if constexpr (std::is_same_v) - return ValueType::Null; + if constexpr (std::is_same_v) + return ValueType::Null; - else if constexpr (std::is_same_v) - return ValueType::Int; + else if constexpr (std::is_same_v) + return ValueType::Int; - else if constexpr (std::is_same_v) - return ValueType::Double; + else if constexpr (std::is_same_v) + return ValueType::Double; - else if constexpr (std::is_same_v) - return ValueType::String; + else if constexpr (std::is_same_v) + return ValueType::String; - else if constexpr (std::is_same_v) - return ValueType::Bool; + else if constexpr (std::is_same_v) + return ValueType::Bool; - else if constexpr (std::is_same_v) - return ValueType::Function; + else if constexpr (std::is_same_v) + return ValueType::Function; - else if constexpr (std::is_same_v) - return ValueType::StructType; + else if constexpr (std::is_same_v) + return ValueType::StructType; - else if constexpr (std::is_same_v) - return ValueType::StructInstance; + else if constexpr (std::is_same_v) + return ValueType::StructInstance; - else if constexpr (std::is_same_v) - return ValueType::List; + else if constexpr (std::is_same_v) + return ValueType::List; - else if constexpr (std::is_same_v) - return ValueType::Map; + else if constexpr (std::is_same_v) + return ValueType::Map; - else if constexpr (std::is_same_v) - return ValueType::Module; + else if constexpr (std::is_same_v) + return ValueType::Module; - else if constexpr (std::is_same_v) - return ValueType::InterfaceType; + else if constexpr (std::is_same_v) + return ValueType::InterfaceType; - else - return ValueType::Any; - }, - data); + else + return ValueType::Any; + }, + data); } bool isNull() const { return is(); } @@ -427,13 +429,12 @@ namespace Fig { if (is()) return FString(u8"null"); if (is()) return FString(std::to_string(as())); - if (is()) return FString(std::format("{}", as())); + if (is()) return FString(std::format("{:g}", as())); if (is()) return FString(u8"\"" + as() + u8"\""); if (is()) return as() ? FString(u8"true") : FString(u8"false"); if (is()) - return FString(std::format("", - as().id, - static_cast(&as()))); + return FString(std::format( + "", as().id, static_cast(&as()))); if (is()) return FString(std::format("", as().type.toString().toBasicString(), @@ -449,8 +450,7 @@ namespace Fig bool first_flag = true; for (auto &ele : list) { - if (!first_flag) - output += u8", "; + if (!first_flag) output += u8", "; output += ele.value->toString(); first_flag = false; } @@ -464,8 +464,7 @@ namespace Fig bool first_flag = true; for (auto &[key, value] : map) { - if (!first_flag) - output += u8", "; + if (!first_flag) output += u8", "; output += key.value->toString() + FString(u8" : ") + value->toString(); first_flag = false; } @@ -474,24 +473,22 @@ namespace Fig } if (is()) { - return FString(std::format( - "", - as().name.toBasicString(), - static_cast(&as()))); + return FString(std::format("", + as().name.toBasicString(), + static_cast(&as()))); } if (is()) { - return FString(std::format( - "", - as().type.toString().toBasicString(), - static_cast(&as()))); + return FString(std::format("", + as().type.toString().toBasicString(), + static_cast(&as()))); } return FString(u8""); } private: - static std::string makeTypeErrorMessage(const char *prefix, const char *op, - const Object &lhs, const Object &rhs) + static std::string + makeTypeErrorMessage(const char *prefix, const char *op, const Object &lhs, const Object &rhs) { auto lhs_type = lhs.getTypeInfo().name.toBasicString(); auto rhs_type = rhs.getTypeInfo().name.toBasicString(); @@ -508,8 +505,7 @@ namespace Fig { bool bothInt = lhs.is() && rhs.is(); auto result = lhs.getNumericValue() + rhs.getNumericValue(); - if (bothInt) - return Object(static_cast(result)); + if (bothInt) return Object(static_cast(result)); return Object(result); } if (lhs.is() && rhs.is()) @@ -525,8 +521,7 @@ namespace Fig { bool bothInt = lhs.is() && rhs.is(); auto result = lhs.getNumericValue() - rhs.getNumericValue(); - if (bothInt) - return Object(static_cast(result)); + if (bothInt) return Object(static_cast(result)); return Object(result); } throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "-", lhs, rhs))); @@ -540,18 +535,14 @@ namespace Fig { bool bothInt = lhs.is() && rhs.is(); auto result = lhs.getNumericValue() * rhs.getNumericValue(); - if (bothInt) - return Object(static_cast(result)); + if (bothInt) return Object(static_cast(result)); return Object(result); } if (lhs.is() && rhs.is()) { FString result; const FString &l = lhs.as(); - for (size_t i=0; i < rhs.getNumericValue(); ++i) - { - result += l; - } + for (size_t i = 0; i < rhs.getNumericValue(); ++i) { result += l; } return Object(result); } throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "*", lhs, rhs))); @@ -564,8 +555,7 @@ namespace Fig if (lhs.isNumeric() && rhs.isNumeric()) { auto rnv = rhs.getNumericValue(); - if (rnv == 0) - throw ValueError(FString(makeTypeErrorMessage("Division by zero", "/", lhs, rhs))); + if (rnv == 0) throw ValueError(FString(makeTypeErrorMessage("Division by zero", "/", lhs, rhs))); // bool bothInt = lhs.is() && rhs.is(); auto result = lhs.getNumericValue() / rnv; // if (bothInt) @@ -593,12 +583,11 @@ namespace Fig if (r != 0 && ((lv < 0) != (rv < 0))) { q -= 1; } return q; } - + if (lhs.isNumeric() && rhs.isNumeric()) { auto rnv = rhs.getNumericValue(); - if (rnv == 0) - throw ValueError(FString(makeTypeErrorMessage("Modulo by zero", "/", lhs, rhs))); + if (rnv == 0) throw ValueError(FString(makeTypeErrorMessage("Modulo by zero", "/", lhs, rhs))); auto result = std::fmod(lhs.getNumericValue(), rnv); return Object(result); } @@ -623,25 +612,25 @@ namespace Fig friend Object operator!(const Object &v) { if (!v.is()) - throw ValueError(FString(std::format("Logical NOT requires bool: '{}'", v.getTypeInfo().name.toBasicString()))); + throw ValueError( + FString(std::format("Logical NOT requires bool: '{}'", v.getTypeInfo().name.toBasicString()))); return Object(!v.as()); } friend Object operator-(const Object &v) { - if (v.isNull()) - throw ValueError(FString(u8"Unary minus cannot be applied to null")); - if (v.is()) - return Object(-v.as()); - if (v.is()) - return Object(-v.as()); - throw ValueError(FString(std::format("Unary minus requires int or double: '{}'", v.getTypeInfo().name.toBasicString()))); + if (v.isNull()) throw ValueError(FString(u8"Unary minus cannot be applied to null")); + if (v.is()) return Object(-v.as()); + if (v.is()) return Object(-v.as()); + throw ValueError( + FString(std::format("Unary minus requires int or double: '{}'", v.getTypeInfo().name.toBasicString()))); } friend Object operator~(const Object &v) { if (!v.is()) - throw ValueError(FString(std::format("Bitwise NOT requires int: '{}'", v.getTypeInfo().name.toBasicString()))); + throw ValueError( + FString(std::format("Bitwise NOT requires int: '{}'", v.getTypeInfo().name.toBasicString()))); return Object(~v.as()); } @@ -690,7 +679,8 @@ namespace Fig friend Object bit_not(const Object &v) { if (!v.is()) - throw ValueError(FString(std::format("Bitwise NOT requires int: '{}'", v.getTypeInfo().name.toBasicString()))); + throw ValueError( + FString(std::format("Bitwise NOT requires int: '{}'", v.getTypeInfo().name.toBasicString()))); return Object(~v.as()); } @@ -716,14 +706,19 @@ namespace Fig { bool bothInt = base.is() && exp.is(); auto result = std::pow(base.getNumericValue(), exp.getNumericValue()); - if (bothInt) - return Object(static_cast(result)); + if (bothInt) return Object(static_cast(result)); return Object(result); } throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "**", base, exp))); } }; + inline bool isBoolObjectTruthy(ObjectPtr obj) + { + assert(obj->is()); + return obj->as(); + } + using RvObject = ObjectPtr; inline bool operator==(const ValueKey &l, const ValueKey &r) diff --git a/src/Evaluator/evaluator.hpp b/src/Evaluator/evaluator.hpp index 23fb593..835554a 100644 --- a/src/Evaluator/evaluator.hpp +++ b/src/Evaluator/evaluator.hpp @@ -1,3 +1,4 @@ +#pragma once #include #include #include diff --git a/src/IR/IR.hpp b/src/IR/IR.hpp new file mode 100644 index 0000000..c0bc9f4 --- /dev/null +++ b/src/IR/IR.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include + +#include +#include +namespace Fig::IR +{ + using Reg = uint16_t; + using Label = uint32_t; + + constexpr Reg INVALID_REG = 0xFFFF; + + enum class Op : uint8_t + { + // ---- control ---- + Nop, + Jmp, + Br, // conditional branch + Ret, + Call, + + // ---- arithmetic ---- + Add, + Sub, + Mul, + Div, + + // ---- compare ---- + Lt, + Le, + Gt, + Ge, + Eq, + + // ---- data ---- + LoadImm, // immediate -> reg + Mov, + }; + + struct Inst + { + Op op; + + Reg dst; // 结果寄存器 + Reg a; // operand a + Reg b; // operand b + + int64_t imm; // immediate / jump offset + }; + + struct Function + { + FString name; + + uint16_t paramCount; + uint16_t localCount; // 不含参数 + uint16_t regCount; // param + locals + temps + + std::vector code; + }; +}; \ No newline at end of file diff --git a/src/IR/IRInterpreter.hpp b/src/IR/IRInterpreter.hpp new file mode 100644 index 0000000..f0466cc --- /dev/null +++ b/src/IR/IRInterpreter.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include + +#include + +namespace Fig::IR +{ + struct VirtualMachine + { + std::vector functions; + + int64_t execute(Function *fn, const int64_t *args) + { + assert(fn != nullptr); + std::vector regs(fn->regCount); + // load params + + size_t ip = 0; + + while (ip < fn->code.size()) + { + const Inst &ins = fn->code[ip++]; + + switch (ins.op) + { + case Op::Nop: break; + + case Op::LoadImm: regs[ins.dst] = ins.imm; break; + + case Op::Mov: regs[ins.dst] = regs[ins.a]; break; + + case Op::Add: regs[ins.dst] = regs[ins.a] + regs[ins.b]; break; + + case Op::Sub: regs[ins.dst] = regs[ins.a] - regs[ins.b]; break; + + case Op::Mul: regs[ins.dst] = regs[ins.a] * regs[ins.b]; break; + + case Op::Div: regs[ins.dst] = regs[ins.a] / regs[ins.b]; break; + + case Op::Lt: regs[ins.dst] = regs[ins.a] < regs[ins.b]; break; + + case Op::Le: regs[ins.dst] = regs[ins.a] <= regs[ins.b]; break; + + case Op::Gt: regs[ins.dst] = regs[ins.a] > regs[ins.b]; break; + + case Op::Ge: regs[ins.dst] = regs[ins.a] >= regs[ins.b]; break; + + case Op::Eq: regs[ins.dst] = regs[ins.a] == regs[ins.b]; break; + + case Op::Jmp: ip = static_cast(static_cast(ip) + ins.imm); break; + + case Op::Br: + if (regs[ins.a] == 0) { ip = static_cast(static_cast(ip) + ins.imm); } + break; + + case Op::Call: { + // currently supports 1-arg call via reg a + Function *callee = functions.at(static_cast(ins.imm)); + int64_t arg0 = regs[ins.a]; + int64_t ret = execute(callee, &arg0); + regs[ins.dst] = ret; + break; + } + + case Op::Ret: return regs[ins.a]; + } + } + + // unreachable normally + return 0; + } + }; +}; // namespace Fig::IR \ No newline at end of file diff --git a/src/IR/ir_test_main.cpp b/src/IR/ir_test_main.cpp new file mode 100644 index 0000000..b1ab6c2 --- /dev/null +++ b/src/IR/ir_test_main.cpp @@ -0,0 +1,14 @@ +#include +#include + + +int main() +{ + using namespace Fig; + IR::VirtualMachine vm; + + using namespace IR; + IR::Inst fib_ir[] = { + + }; +} \ No newline at end of file diff --git a/src/Lexer/lexer.cpp b/src/Lexer/lexer.cpp index db4cd3f..89722f2 100644 --- a/src/Lexer/lexer.cpp +++ b/src/Lexer/lexer.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #if 0 @@ -201,6 +200,11 @@ namespace Fig next(); str += u8"\""; } + else if (ec == U'r') + { + next(); + str += u8"\r"; + } else if (ec == U'\'') { next(); diff --git a/src/Repl/Repl.cpp b/src/Repl/Repl.cpp new file mode 100644 index 0000000..c914d63 --- /dev/null +++ b/src/Repl/Repl.cpp @@ -0,0 +1,65 @@ +#include "Ast/astBase.hpp" +#include "Error/error.hpp" +#include "Error/errorLog.hpp" +#include +#include +#include + +namespace Fig +{ + void Repl::Start() const + { + ostream << getPrompt() << "\n"; + + const FString &sourcePath = u8""; + const std::vector &sourceLines{}; + + Evaluator evaluator; + + evaluator.CreateGlobalContext(); + evaluator.RegisterBuiltinsValue(); + evaluator.SetSourcePath(sourcePath); + evaluator.SetSourceLines(sourceLines); + + while (true) + { + ostream << "\r\n>>"; + const FString &line = readline(); + + if (line.empty()) + { + ostream << Object::getNullInstance()->toString().toBasicString(); + continue; + } + if (line == u8"!exit") + { + break; + } + + Lexer lexer(line, sourcePath, sourceLines); + Parser parser(lexer, sourcePath, sourceLines); + + std::vector program; + try + { + program = parser.parseAll(); + + StatementResult sr = evaluator.Run(program); + ObjectPtr result = sr.result; + ostream << result->toString().toBasicString() << '\n'; + } + catch (AddressableError &e) + { + ostream << "Oops!\n"; + ErrorLog::logAddressableError(e); + ostream << "\n"; + } + catch (UnaddressableError &e) + { + ostream << "Oops!\n"; + ErrorLog::logUnaddressableError(e); + ostream << "\n"; + } + } + } +} // namespace Fig \ No newline at end of file diff --git a/src/Repl/Repl.hpp b/src/Repl/Repl.hpp new file mode 100644 index 0000000..c1d9f71 --- /dev/null +++ b/src/Repl/Repl.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Fig +{ + class Repl + { + private: + std::istream &istream; + std::ostream &ostream; + + public: + Repl(std::istream &_istream = std::cin, std::ostream &_ostream = std::cout) : + istream(_istream), ostream(_ostream) + { + } + + FString readline() const + { + std::string buf; + std::getline(istream, buf); + + return FString(buf); + } + + static std::string getPrompt() + { + static const std::string prompt = std::format("Fig {} ({})[{} {}-bit on `{}`]\n", + Core::VERSION, + Core::COMPILE_TIME, + Core::COMPILER, + Core::ARCH, + Core::PLATFORM) + + "Type '!exit' to exit\n" + "feel free to type!"; + return prompt; + } + + void Start() const; + }; + +}; // namespace Fig \ No newline at end of file diff --git a/src/VMValue/VMValue.cpp b/src/VMValue/VMValue.cpp deleted file mode 100644 index 0694d84..0000000 --- a/src/VMValue/VMValue.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include - -#include -#include - -namespace Fig -{ - FString Value::toString() const - { - if (static_cast(type) > static_cast(Double)) - { - return heap->toString(); - } - switch (type) - { - case Null: - return u8"null"; - case Bool: - return (b ? u8"true" : u8"false"); - case Int: - return FString(std::to_string(i)); - case Double: - return FString(std::to_string(d)); - - default: - assert(false); - } - } -}; \ No newline at end of file diff --git a/src/VMValue/VMValue.hpp b/src/VMValue/VMValue.hpp deleted file mode 100644 index 4be4873..0000000 --- a/src/VMValue/VMValue.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -/* - value system for virtual machine - not Evaluator value -*/ - -#include - -#include - -namespace Fig -{ - struct HeapObject; - - struct Value - { - struct NullObject {}; - - enum _Type : uint8_t - { - Null = 0, - Bool, - Int, - Double, - - /* Complex types, alloc in heap*/ - String, - // Function, - // StructType, - // StructInstance, - // List, - // Map, - // Module, - // InterfaceType - } type; - - union - { - NullObject n; - bool b; - int64_t i; - double d; - - HeapObject *heap; - }; - - Value() - { - type = Null; - n = NullObject(); - } - - Value(bool _b) - { - type = Bool; - b = _b; - } - - Value(int64_t _i) - { - type = Int; - i = _i; - } - - Value(double _d) - { - type = Double; - d = _d; - } - - FString toString() const; - }; - - struct HeapObject - { - Value::_Type type; // > 3 - - virtual FString toString() const = 0; - }; - - struct String : HeapObject - { - FString value; - - virtual FString toString() const override - { - return value; - } - }; -}; \ No newline at end of file diff --git a/src/VirtualMachine/VirtualMachine.cpp b/src/VirtualMachine/VirtualMachine.cpp new file mode 100644 index 0000000..6e12a1a --- /dev/null +++ b/src/VirtualMachine/VirtualMachine.cpp @@ -0,0 +1,225 @@ +#include +#include + +#include +#include +#include + +namespace Fig +{ + Object VirtualMachine::Execute() + { + while (currentFrame->ip < currentFrame->fn.chunk.ins.size()) + { + Instruction ins = currentFrame->fn.chunk.ins[currentFrame->ip++]; + + switch (ins.code) + { + case OpCode::HALT: { + return *Object::getNullInstance(); + } + case OpCode::RETURN: { + const Object &ret = pop(); + + uint64_t base = currentFrame->base; + popFrame(); + + if (frames.empty()) + { + return ret; + } + + stack.resize(base); // 清除函数的临时值 + push(ret); + break; + } + case OpCode::LOAD_LOCAL: { + uint64_t operand = static_cast(ins.operand); + // LOCAL编号都为正数 + + push(stack[currentFrame->base + operand]); + break; + } + + case OpCode::LOAD_CONST: { + uint64_t operand = static_cast(ins.operand); + // CONST编号都为正数 + + push(currentFrame->fn.chunk.constants[operand]); + break; + } + + case OpCode::STORE_LOCAL: { + uint64_t operand = static_cast(ins.operand); + // LOCAL编号都为正数 + + stack[currentFrame->base + operand] = pop(); + break; + } + + case OpCode::LT: { + const Object &rhs = pop(); + const Object &lhs = pop(); + + Object result = lhs < rhs; + push(result); + break; + } + + case OpCode::LTET: { + const Object &rhs = pop(); + const Object &lhs = pop(); + + Object result = lhs <= rhs; + push(result); + break; + } + + case OpCode::GT: { + const Object &rhs = pop(); + const Object &lhs = pop(); + + Object result = lhs > rhs; + push(result); + break; + } + + case OpCode::GTET: { + const Object &rhs = pop(); + const Object &lhs = pop(); + + Object result = lhs >= rhs; + push(result); + break; + } + + case OpCode::ADD: { + const Object &rhs = pop(); + const Object &lhs = pop(); + + if (lhs.is() && rhs.is()) + { + ValueType::IntClass result = lhs.as() + rhs.as(); + push(Object(result)); + break; + } + + Object result = lhs + rhs; + push(result); + break; + } + + case OpCode::SUB: { + const Object &rhs = pop(); + const Object &lhs = pop(); + + if (lhs.is() && rhs.is()) + { + ValueType::IntClass result = lhs.as() - rhs.as(); + push(Object(result)); + break; + } + + Object result = lhs - rhs; + push(result); + break; + } + + case OpCode::MUL: { + const Object &rhs = pop(); + const Object &lhs = pop(); + + if (lhs.is() && rhs.is()) + { + ValueType::IntClass result = lhs.as() * rhs.as(); + push(Object(result)); + break; + } + + Object result = lhs * rhs; + push(result); + break; + } + + case OpCode::DIV: { + const Object &rhs = pop(); + const Object &lhs = pop(); + + if (lhs.is() && rhs.is()) + { + ValueType::DoubleClass result = (double)lhs.as() / (double)rhs.as(); + push(Object(result)); + break; + } + + Object result = lhs / rhs; + push(result); + break; + } + + case OpCode::JUMP: { + int64_t target = ins.operand; + currentFrame->ip += target; + break; + } + + case OpCode::JUMP_IF_FALSE: { + const Object &cond = pop(); + + if (!cond.is()) + { + throw RuntimeError( + FString(u8"Condition must be boolean!") + ); + } + if (!cond.as()) + { + // cond is falsity + int64_t target = ins.operand; + currentFrame->ip += target; + } + break; + } + + case OpCode::CALL: + { + uint16_t argCount = static_cast(ins.operand); // number of max arg is UINT16_MAX + + const Object &obj = stack.back(); + if (!obj.is()) + { + throw RuntimeError(FString(std::format("{} is not callable", obj.toString().toBasicString()))); + } + + // const Function &fn_obj = obj.as(); + // assert(fn_obj.isCompiled && "function must be compiled"); + + CompiledFunction fn; + + assert(stack.size() >= argCount && "stack does not have enough arguments"); + assert(fn.slotCount >= argCount && "slotCount < argCount"); + + uint64_t base = stack.size() - 1 - argCount; + + pop(); // pop function + + for (int64_t i = 0; i < fn.slotCount - argCount; ++i) + { + push(*Object::getNullInstance()); + } + + CallFrame newFrame + { + 0, + base, // 参数已经加载到stack, base为第一个参数 + fn + }; + + addFrame(newFrame); + break; + } + } + } + return *Object::getNullInstance(); + } +}; // namespace Fig \ No newline at end of file diff --git a/src/VirtualMachine/VirtualMachine.hpp b/src/VirtualMachine/VirtualMachine.hpp new file mode 100644 index 0000000..985ee25 --- /dev/null +++ b/src/VirtualMachine/VirtualMachine.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include +#include + +#include + +namespace Fig +{ + class VirtualMachine + { + private: + std::vector frames; + CallFrame *currentFrame; + + std::vector stack; + Object *stack_top; + + public: + void Clean() + { + frames.clear(); + stack.clear(); + + currentFrame = nullptr; + stack_top = nullptr; + } + + void addFrame(const CallFrame &_frame) + { + frames.push_back(_frame); + currentFrame = &frames.back(); + } + + CallFrame popFrame() + { + assert((!frames.empty()) && "frames is empty!"); + + CallFrame back = *currentFrame; + frames.pop_back(); + currentFrame = (frames.empty() ? nullptr : &frames.back()); + + return back; + } + + void push(const Object &_object) + { + stack.push_back(_object); + stack_top = &stack.back(); + } + + void nextIns(CallFrame &frame) const + { + frame.ip += 1; + } + + Object pop() + { + assert((!stack.empty()) && "stack is empty!"); + + Object back = *stack_top; + stack.pop_back(); + stack_top = (stack.empty() ? nullptr : &stack.back()); + + return back; + } + + VirtualMachine(const CallFrame &_frame) + { + addFrame(_frame); + } + + Object Execute(); + }; +}; \ No newline at end of file diff --git a/src/VirtualMachine/main.cpp b/src/VirtualMachine/main.cpp deleted file mode 100644 index 7eb5599..0000000 --- a/src/VirtualMachine/main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - - ███████████ █████ █████ ██████████ ███████████ █████ █████████ █████ █████████ ██████ █████ █████████ █████ █████ █████████ █████████ ██████████ -░█░░░███░░░█░░███ ░░███ ░░███░░░░░█ ░░███░░░░░░█░░███ ███░░░░░███ ░░███ ███░░░░░███ ░░██████ ░░███ ███░░░░░███░░███ ░░███ ███░░░░░███ ███░░░░░███░░███░░░░░█ -░ ░███ ░ ░███ ░███ ░███ █ ░ ░███ █ ░ ░███ ███ ░░░ ░███ ░███ ░███ ░███░███ ░███ ███ ░░░ ░███ ░███ ░███ ░███ ███ ░░░ ░███ █ ░ - ░███ ░███████████ ░██████ ░███████ ░███ ░███ ░███ ░███████████ ░███░░███░███ ░███ ░███ ░███ ░███████████ ░███ ░██████ - ░███ ░███░░░░░███ ░███░░█ ░███░░░█ ░███ ░███ █████ ░███ ░███░░░░░███ ░███ ░░██████ ░███ █████ ░███ ░███ ░███░░░░░███ ░███ █████ ░███░░█ - ░███ ░███ ░███ ░███ ░ █ ░███ ░ ░███ ░░███ ░░███ ░███ █ ░███ ░███ ░███ ░░█████ ░░███ ░░███ ░███ ░███ ░███ ░███ ░░███ ░░███ ░███ ░ █ - █████ █████ █████ ██████████ █████ █████ ░░█████████ ███████████ █████ █████ █████ ░░█████ ░░█████████ ░░████████ █████ █████ ░░█████████ ██████████ - ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░░░░░░ - - Copyright (C) 2020-2026 PuqiAR - -This software is licensed under the MIT License. See LICENSE for details. -*/ - - -// DO NOT USE CLANG-FORMAT FOR THIS FILE - -#include -#include - -int main() -{ - using namespace Fig; - - - std::vector src; - - { - using enum Bytecode; - src = { - 0x21, // LOAD_TRUE - 0x23, 0x7f, // LOAD_CON8 0x7f - 0x24, 0x12, 0x34, // LOAD_CON16 0x1234 - 0x25, 0x12, 0x34, 0x56, 0x78, // LOAD_CON32 0x12345678 - 0x63, 0x12, 0x34, 0x56, 0x78 // JUMP32_IF_TRUE - }; - - FString r = reverseCompile(src); - std::cout << r.toBasicString() << std::endl; - } -} \ No newline at end of file diff --git a/src/Evaluator/main.cpp b/src/main.cpp similarity index 97% rename from src/Evaluator/main.cpp rename to src/main.cpp index 0ab453a..2253d0a 100644 --- a/src/Evaluator/main.cpp +++ b/src/main.cpp @@ -39,6 +39,7 @@ This software is licensed under the MIT License. See LICENSE.txt for details. #include #include #include +#include static size_t addressableErrorCount = 0; static size_t unaddressableErrorCount = 0; @@ -54,6 +55,10 @@ int main(int argc, char **argv) program.add_argument("source") .help("source file to be interpreted") .default_value(std::string("")); + program.add_argument("-r", "--repl") + .help("start repl") + .default_value(false) + .implicit_value(true); // program.add_argument("-v", "--version") // .help("get the version of Fig Interpreter") // .default_value(false) @@ -74,6 +79,14 @@ int main(int argc, char **argv) // std::print("Fig Interpreter version {}\n", Fig::Core::VERSION); // return 0; // } + + if (program.get("--repl")) + { + Fig::Repl repl; + repl.Start(); + exit(0); + } + Fig::FString sourcePath(program.get("source")); if (sourcePath.empty()) { diff --git a/xmake.lua b/xmake.lua index 59855bb..6d3b9e7 100644 --- a/xmake.lua +++ b/xmake.lua @@ -3,81 +3,63 @@ add_rules("plugin.compile_commands.autoupdate", {outputdir = ".vscode"}) set_policy("run.autobuild", false) +set_languages("c++23") + +add_ldflags("-static", {force = true}) +if is_plat("linux") then + -- Linux: clang + libc++ + set_toolchains("clang") + add_cxxflags("-stdlib=libc++") + add_ldflags("-stdlib=libc++") +elseif is_plat("windows") then + -- 1. CI cross (Linux -> Windows) + -- 2. local dev (Windows + llvm-mingw) + set_toolchains("mingw") -- llvm-mingw + add_ldflags("-Wl,--stack,268435456") + -- set_toolchains("clang") + -- static lib + -- add_ldflags("-target x86_64-w64-mingw32", "-static") + -- add_cxxflags("-stdlib=libc++") + -- add_ldflags("-stdlib=libc++") +end + + + +add_files("src/Core/warning.cpp") +add_files("src/Core/runtimeTime.cpp") + +add_files("src/Lexer/lexer.cpp") +add_files("src/Parser/parser.cpp") + +add_files("src/Module/builtins.cpp") + +add_files("src/Evaluator/Value/value.cpp") +add_includedirs("src") + +add_defines("__FCORE_COMPILE_TIME=\"" .. os.date("%Y-%m-%d %H:%M:%S") .. "\"") + target("Fig") set_kind("binary") - set_languages("c++23") - add_ldflags("-static", {force = true}) - if is_plat("linux") then - -- Linux: clang + libc++ - set_toolchains("clang") - add_cxxflags("-stdlib=libc++") - add_ldflags("-stdlib=libc++") - elseif is_plat("windows") then - -- 1. CI cross (Linux -> Windows) - -- 2. local dev (Windows + llvm-mingw) - set_toolchains("mingw") -- llvm-mingw - add_ldflags("-Wl,--stack,268435456") - -- set_toolchains("clang") - -- static lib - -- add_ldflags("-target x86_64-w64-mingw32", "-static") - -- add_cxxflags("-stdlib=libc++") - -- add_ldflags("-stdlib=libc++") - end - - - - add_files("src/Core/warning.cpp") - add_files("src/Core/runtimeTime.cpp") - - add_files("src/Lexer/lexer.cpp") - add_files("src/Parser/parser.cpp") - - add_files("src/Module/builtins.cpp") - - add_files("src/Evaluator/Value/value.cpp") add_files("src/Evaluator/Core/*.cpp") + add_files("src/VirtualMachine/VirtualMachine.cpp") add_files("src/Evaluator/evaluator.cpp") - add_files("src/Evaluator/main.cpp") - - add_includedirs("src") - -- add_includedirs("src/Evaluator") - + add_files("src/Repl/Repl.cpp") + add_files("src/main.cpp") + set_warnings("all") - add_defines("__FCORE_COMPILE_TIME=\"" .. os.date("%Y-%m-%d %H:%M:%S") .. "\"") +target("vm_test_main") + set_kind("binary") --- -- Bytecode VM target --- target("Figc") --- set_kind("binary") --- set_languages("c++23") - --- add_ldflags("-static", {force = true}) --- if is_plat("linux") then --- -- Linux: clang + libc++ --- set_toolchains("clang") --- add_cxxflags("-stdlib=libc++") --- add_ldflags("-stdlib=libc++") --- elseif is_plat("windows") then --- -- 1. CI cross (Linux -> Windows) --- -- 2. local dev (Windows + llvm-mingw) --- set_toolchains("mingw") -- llvm-mingw --- add_ldflags("-Wl,--stack,268435456") --- -- set_toolchains("clang") --- -- static lib --- -- add_ldflags("-target x86_64-w64-mingw32", "-static") --- -- add_cxxflags("-stdlib=libc++") --- -- add_ldflags("-stdlib=libc++") --- end + add_files("src/VirtualMachine/VirtualMachine.cpp") + add_files("src/Bytecode/vm_test_main.cpp") --- add_includedirs("src/Evaluator") --- add_files("src/Evaluator/Value/value.cpp") --- add_files("src/VirtualMachine/main.cpp") --- add_files("src/Core/warning.cpp") --- add_files("src/Lexer/lexer.cpp") --- add_files("src/Parser/parser.cpp") + set_warnings("all") --- add_includedirs("src") --- set_warnings("all") +target("ir_test_main") + set_kind("binary") --- add_defines("__FCORE_COMPILE_TIME=\"" .. os.date("%Y-%m-%d %H:%M:%S") .. "\"") + add_files("src/IR/ir_test_main.cpp") + + set_warnings("all") \ No newline at end of file