From aea716ced2e278686e161416a0025e87f42651de Mon Sep 17 00:00:00 2001 From: PuqiAR Date: Sun, 1 Feb 2026 15:52:28 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=9E=E6=A1=A3=E4=B9=8B=E5=90=8E=E7=9A=84?= =?UTF-8?q?=E9=87=8D=E5=86=99=E3=80=82=E9=83=A8=E5=88=86=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E3=80=82=E6=B7=BB=E5=8A=A0=E4=BA=86=E4=BB=80?= =?UTF-8?q?=E4=B9=88=E6=88=91=E4=B9=9F=E5=BF=98=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Ast/Expressions/FunctionCall.hpp | 2 +- src/Ast/Expressions/ValueExpr.hpp | 2 +- src/Ast/Statements/FunctionDefSt.hpp | 2 +- src/Ast/Statements/VarDef.hpp | 2 +- src/Ast/functionParameters.hpp | 6 +- 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/Evaluator/Context/context.hpp | 79 +- src/Evaluator/Core/Eval.cpp | 124 ++ src/Evaluator/Core/EvalBinary.cpp | 287 ++++ src/Evaluator/Core/EvalFunctionCall.cpp | 183 +++ src/Evaluator/Core/EvalInitExpr.cpp | 364 +++++ src/Evaluator/Core/EvalLvObject.cpp | 195 +++ src/Evaluator/Core/EvalStatement.cpp | 510 +++++++ src/Evaluator/Core/EvalTernary.cpp | 20 + src/Evaluator/Core/EvalUnary.cpp | 31 + src/Evaluator/Core/ResolveModulePath.cpp | 114 ++ src/Evaluator/Core/StatementResult.hpp | 37 + src/Evaluator/Value/IntPool.hpp | 11 +- src/Evaluator/Value/LvObject.hpp | 4 +- src/Evaluator/Value/Type.hpp | 72 +- src/Evaluator/Value/VariableSlot.hpp | 4 +- src/Evaluator/Value/function.hpp | 128 +- src/Evaluator/Value/interface.hpp | 2 +- src/Evaluator/Value/module.hpp | 2 +- src/Evaluator/Value/structInstance.hpp | 4 +- src/Evaluator/Value/structType.hpp | 4 +- src/Evaluator/Value/value.cpp | 16 +- src/Evaluator/Value/value.hpp | 607 ++++---- src/Evaluator/evaluator.cpp | 1785 +--------------------- src/Evaluator/evaluator.hpp | 60 +- src/IR/IR.hpp | 62 + src/IR/IRInterpreter.hpp | 74 + src/IR/ir_test_main.cpp | 14 + src/Module/builtins.cpp | 380 +++++ src/Module/builtins.hpp | 393 +---- 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 | 112 +- 50 files changed, 3676 insertions(+), 2997 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 create mode 100644 src/Evaluator/Core/Eval.cpp create mode 100644 src/Evaluator/Core/EvalBinary.cpp create mode 100644 src/Evaluator/Core/EvalFunctionCall.cpp create mode 100644 src/Evaluator/Core/EvalInitExpr.cpp create mode 100644 src/Evaluator/Core/EvalLvObject.cpp create mode 100644 src/Evaluator/Core/EvalStatement.cpp create mode 100644 src/Evaluator/Core/EvalTernary.cpp create mode 100644 src/Evaluator/Core/EvalUnary.cpp create mode 100644 src/Evaluator/Core/ResolveModulePath.cpp create mode 100644 src/Evaluator/Core/StatementResult.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/Module/builtins.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/Expressions/FunctionCall.hpp b/src/Ast/Expressions/FunctionCall.hpp index e29dc1a..fb260a8 100644 --- a/src/Ast/Expressions/FunctionCall.hpp +++ b/src/Ast/Expressions/FunctionCall.hpp @@ -2,7 +2,7 @@ #pragma once #include -#include +#include namespace Fig::Ast { diff --git a/src/Ast/Expressions/ValueExpr.hpp b/src/Ast/Expressions/ValueExpr.hpp index 5e2ec3f..6978c48 100644 --- a/src/Ast/Expressions/ValueExpr.hpp +++ b/src/Ast/Expressions/ValueExpr.hpp @@ -2,7 +2,7 @@ #include -#include +#include namespace Fig::Ast { diff --git a/src/Ast/Statements/FunctionDefSt.hpp b/src/Ast/Statements/FunctionDefSt.hpp index e668062..b7f1426 100644 --- a/src/Ast/Statements/FunctionDefSt.hpp +++ b/src/Ast/Statements/FunctionDefSt.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Fig::Ast { diff --git a/src/Ast/Statements/VarDef.hpp b/src/Ast/Statements/VarDef.hpp index d53d879..d0f0280 100644 --- a/src/Ast/Statements/VarDef.hpp +++ b/src/Ast/Statements/VarDef.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include namespace Fig::Ast { diff --git a/src/Ast/functionParameters.hpp b/src/Ast/functionParameters.hpp index 4f1e6c6..095c9c3 100644 --- a/src/Ast/functionParameters.hpp +++ b/src/Ast/functionParameters.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include @@ -56,7 +56,7 @@ namespace Fig::Ast { return FString(variadicPara + u8"..."); } - static const auto posParasToString = [this]() { + const auto posParasToString = [this]() { FString out; for (auto &p : posParas) { @@ -70,7 +70,7 @@ namespace Fig::Ast out.pop_back(); return out; }; - static const auto defParasToString = [this]() { + const auto defParasToString = [this]() { FString out; for (auto &p : defParas) { 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/Evaluator/Context/context.hpp b/src/Evaluator/Context/context.hpp index 903876e..ef202e4 100644 --- a/src/Evaluator/Context/context.hpp +++ b/src/Evaluator/Context/context.hpp @@ -1,17 +1,19 @@ #pragma once -#include "Ast/Statements/InterfaceDefSt.hpp" -#include "Value/interface.hpp" -#include +#include "Evaluator/Value/function.hpp" +#include +#include +#include #include +#include #include #include #include -#include +#include #include -#include -#include +#include +#include namespace Fig { @@ -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(); @@ -142,34 +148,20 @@ namespace Fig throw RuntimeError( FString(std::format("Variable '{}' already defined in this scope", name.toBasicString()))); } - 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; - } + variables[name] = std::make_shared(name, target->value, ti, am, true, target); } + 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) // { @@ -233,6 +225,13 @@ namespace Fig return parent && parent->hasImplRegisted(structType, interfaceType); } + std::unordered_map, TypeInfoHash> getImplRegistry() const + { + return implRegistry; + } + + std::unordered_map, TypeInfoHash> &getImplRegistry() { return implRegistry; } + std::optional getImplRecord(const TypeInfo &structType, const TypeInfo &interfaceType) const { auto it = implRegistry.find(structType); diff --git a/src/Evaluator/Core/Eval.cpp b/src/Evaluator/Core/Eval.cpp new file mode 100644 index 0000000..a36c1d0 --- /dev/null +++ b/src/Evaluator/Core/Eval.cpp @@ -0,0 +1,124 @@ +#include +#include + +namespace Fig +{ + RvObject Evaluator::eval(Ast::Expression exp, ContextPtr ctx) + { + using Ast::AstType; + AstType type = exp->getType(); + switch (type) + { + case AstType::ValueExpr: { + auto val = std::static_pointer_cast(exp); + + return val->val; + } + case AstType::VarExpr: { + auto varExpr = std::static_pointer_cast(exp); + + return evalVarExpr(varExpr, ctx).get(); // LvObject -> RvObject + } + case AstType::BinaryExpr: { + auto bin = std::static_pointer_cast(exp); + + return evalBinary(bin, ctx); + } + case AstType::UnaryExpr: { + auto un = std::static_pointer_cast(exp); + + return evalUnary(un, ctx); + } + case AstType::TernaryExpr: { + auto te = std::static_pointer_cast(exp); + + return evalTernary(te, ctx); + } + case AstType::MemberExpr: + case AstType::IndexExpr: return evalLv(exp, ctx).get(); + + case AstType::FunctionCall: { + auto fnCall = std::static_pointer_cast(exp); + + Ast::Expression callee = fnCall->callee; + ObjectPtr fnObj = eval(callee, ctx); + if (fnObj->getTypeInfo() != ValueType::Function) + { + throw EvaluatorError(u8"ObjectNotCallable", + std::format("Object `{}` isn't callable", fnObj->toString().toBasicString()), + callee); + } + const Function &fn = fnObj->as(); + size_t fnId = fn.id; + // const auto &fnNameOpt = ctx->getFunctionName(fnId); + // const FString &fnName = (fnNameOpt ? *fnNameOpt : + // u8""); + + auto fnNameOpt = ctx->getFunctionName(fnId); + if (!fnNameOpt && fn.closureContext) fnNameOpt = fn.closureContext->getFunctionName(fnId); + + 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); + + + Ast::BlockStatement body = nullptr; + if (fnLiteral->isExprMode()) + { + Ast::Expression exprBody = fnLiteral->getExprBody(); + + + const Ast::AstAddressInfo &aai = exprBody->getAAI(); + Ast::Return st = std::make_shared(exprBody); + st->setAAI(aai); + + body = std::make_shared(); + body->stmts.push_back(st); // convert to Ast::Statement + body->setAAI(aai); + } + else + { + body = fnLiteral->getBlockBody(); + + } + Function fn(fnLiteral->paras, ValueType::Any, body, ctx + /* + pass the ctx(fnLiteral eval context) as closure context + */ + ); + return std::make_shared(std::move(fn)); + } + case AstType::InitExpr: { + auto initExpr = std::static_pointer_cast(exp); + + return evalInitExpr(initExpr, ctx); + } + + case AstType::ListExpr: { + auto lstExpr = std::static_pointer_cast(exp); + + + List list; + for (auto &exp : lstExpr->val) { list.push_back(eval(exp, ctx)); } + return std::make_shared(std::move(list)); + } + + case AstType::MapExpr: { + auto mapExpr = std::static_pointer_cast(exp); + + + Map map; + for (auto &[key, value] : mapExpr->val) { map[eval(key, ctx)] = eval(value, ctx); } + return std::make_shared(std::move(map)); + } + + default: { + throw RuntimeError(FString(std::format("err type of expr: {}", magic_enum::enum_name(type)))); + } + } + return Object::getNullInstance(); // ignore warning + } +}; \ No newline at end of file diff --git a/src/Evaluator/Core/EvalBinary.cpp b/src/Evaluator/Core/EvalBinary.cpp new file mode 100644 index 0000000..a51247d --- /dev/null +++ b/src/Evaluator/Core/EvalBinary.cpp @@ -0,0 +1,287 @@ +#include +#include +#include +#include +#include + +namespace Fig +{ + RvObject Evaluator::evalBinary(Ast::BinaryExpr bin, ContextPtr ctx) + { + using Ast::Operator; + Operator op = bin->op; + Ast::Expression lexp = bin->lexp, rexp = bin->rexp; + switch (op) + { + case Operator::Add: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass result = lhs->as() + rhs->as(); + return IntPool::getInstance().createInt(result); + } + + return std::make_shared(*lhs + *rhs); + } + case Operator::Subtract: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass result = lhs->as() - rhs->as(); + return IntPool::getInstance().createInt(result); + } + + return std::make_shared(*lhs - *rhs); + }; + case Operator::Multiply: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass result = lhs->as() * rhs->as(); + return IntPool::getInstance().createInt(result); + } + + return std::make_shared((*lhs) * (*rhs)); + }; + case Operator::Divide: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs / *rhs); + }; + case Operator::Modulo: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass lv = lhs->as(); + ValueType::IntClass rv = lhs->as(); + if (rv == 0) { throw ValueError(FString(std::format("Modulo by zero: {} % {}", lv, rv))); } + ValueType::IntClass result = lv / rv; + ValueType::IntClass r = lv % rv; + if (r != 0 && ((lv < 0) != (rv < 0))) { result -= 1; } + return IntPool::getInstance().createInt(result); + } + + return std::make_shared(*lhs % *rhs); + }; + case Operator::Power: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass result = + std::pow(lhs->as(), rhs->as()); + return IntPool::getInstance().createInt(result); + } + return std::make_shared(power(*lhs, *rhs)); + } + 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); + }; + case Operator::Equal: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs == *rhs); + } + case Operator::NotEqual: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs != *rhs); + } + case Operator::Less: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs < *rhs); + } + case Operator::LessEqual: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs <= *rhs); + } + case Operator::Greater: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs > *rhs); + } + case Operator::GreaterEqual: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs >= *rhs); + } + case Operator::Is: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + + const TypeInfo &lhsType = lhs->getTypeInfo(); + const TypeInfo &rhsType = rhs->getTypeInfo(); + + if (lhs->is() && rhs->is()) + { + const StructInstance &si = lhs->as(); + const StructType &st = rhs->as(); + return std::make_shared(si.parentType == st.type); + } + if (lhs->is() && rhs->is()) + { + const StructInstance &si = lhs->as(); + const InterfaceType &it = rhs->as(); + return std::make_shared(implements(si.parentType, it.type, ctx)); + } + + if (ValueType::isTypeBuiltin(lhsType) && rhsType == ValueType::StructType) + { + const StructType &st = rhs->as(); + const TypeInfo &type = st.type; + /* + 如果是内置类型(e.g. Int, String) + 那么 eval出来String这个字,出来的是StructType + 而出来的StructType.type就不会是一个独立的TypeInfo,而是内置的ValueType::String + 依次我们可以判断内置类型 + + e.g: + "123" is String + L OP R + + 其中 L 类型为 String + 而 R 类型为 StructType (builtins.hpp) 中注册 + 拿到 R 的 StructType, 其中的 type 为 String + */ + if (lhs->getTypeInfo() == type) { return Object::getTrueInstance(); } + return Object::getFalseInstance(); + } + + throw EvaluatorError(u8"TypeError", + std::format("Unsupported operator `is` for '{}' && '{}'", + prettyType(lhs).toBasicString(), + prettyType(rhs).toBasicString()), + bin->lexp); + } + + case Operator::BitAnd: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass result = lhs->as() & rhs->as(); + return IntPool::getInstance().createInt(result); + } + return std::make_shared(bit_and(*lhs, *rhs)); + } + case Operator::BitOr: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass result = lhs->as() | rhs->as(); + return IntPool::getInstance().createInt(result); + } + return std::make_shared(bit_or(*lhs, *rhs)); + } + case Operator::BitXor: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass result = lhs->as() ^ rhs->as(); + return IntPool::getInstance().createInt(result); + } + return std::make_shared(bit_xor(*lhs, *rhs)); + } + case Operator::ShiftLeft: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass result = lhs->as() << rhs->as(); + return IntPool::getInstance().createInt(result); + } + return std::make_shared(shift_left(*lhs, *rhs)); + } + case Operator::ShiftRight: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass result = lhs->as() >> rhs->as(); + return IntPool::getInstance().createInt(result); + } + return std::make_shared(shift_right(*lhs, *rhs)); + } + + case Operator::Assign: { + LvObject lv = evalLv(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + lv.set(rhs); + return rhs; + } + case Operator::PlusAssign: { + LvObject lv = evalLv(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + lv.set(std::make_shared(*(lv.get()) + *rhs)); + return rhs; + } + case Operator::MinusAssign: { + LvObject lv = evalLv(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + lv.set(std::make_shared(*(lv.get()) - *rhs)); + return rhs; + } + case Operator::AsteriskAssign: { + LvObject lv = evalLv(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + lv.set(std::make_shared(*(lv.get()) * (*rhs))); + return rhs; + } + case Operator::SlashAssign: { + LvObject lv = evalLv(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + lv.set(std::make_shared(*(lv.get()) / *rhs)); + return rhs; + } + case Operator::PercentAssign: { + LvObject lv = evalLv(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + lv.set(std::make_shared(*(lv.get()) / *rhs)); + return rhs; + } + // case Operator::CaretAssign: { + // LvObject lv = evalLv(lexp, ctx); + // ObjectPtr rhs = eval(rexp, ctx); + // lv.set(std::make_shared( + // *(lv.get()) ^ *rhs)); + // } + default: + throw EvaluatorError(u8"UnsupportedOp", + std::format("Unsupport operator '{}' for binary", magic_enum::enum_name(op)), + bin); + } + } +}; \ No newline at end of file diff --git a/src/Evaluator/Core/EvalFunctionCall.cpp b/src/Evaluator/Core/EvalFunctionCall.cpp new file mode 100644 index 0000000..2de0594 --- /dev/null +++ b/src/Evaluator/Core/EvalFunctionCall.cpp @@ -0,0 +1,183 @@ +#include +#include +#include +#include + +namespace Fig +{ + RvObject Evaluator::evalFunctionCall(const Function &fn, + const Ast::FunctionArguments &fnArgs, + const FString &fnName, + ContextPtr ctx) + { + const Function &fnStruct = fn; + Ast::FunctionCallArgs evaluatedArgs; + 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()) + { + throw EvaluatorError(u8"BuiltinArgumentMismatchError", + std::format("Builtin function '{}' expects {} arguments, but {} were provided", + fnName.toBasicString(), + fnStruct.builtinParamCount, + evaluatedArgs.getLength()), + fnArgs.argv.back()); + } + 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 + Ast::FunctionParameters fnParas = fnStruct.paras; + + // create new context for function call + auto newContext = std::make_shared(FString(std::format("", fnName.toBasicString())), + fnStruct.closureContext); + + if (fnParas.variadic) + goto VariadicFilling; + else + goto NormalFilling; + + NormalFilling: { + if (fnArgs.getLength() < fnParas.posParas.size() || fnArgs.getLength() > fnParas.size()) + { + throw RuntimeError(FString(std::format("Function '{}' expects {} to {} arguments, but {} were provided", + fnName.toBasicString(), + fnParas.posParas.size(), + fnParas.size(), + fnArgs.getLength()))); + } + + // positional parameters type check + size_t i; + for (i = 0; i < fnParas.posParas.size(); i++) + { + TypeInfo expectedType(fnParas.posParas[i].second); // look up type info, if exists a type with the + // name, use it, else throw + ObjectPtr argVal = eval(fnArgs.argv[i], ctx); + TypeInfo actualType = argVal->getTypeInfo(); + if (!isTypeMatch(expectedType, argVal, ctx)) + { + throw EvaluatorError(u8"ArgumentTypeMismatchError", + std::format("In function '{}', argument '{}' expects type '{}', but got type '{}'", + fnName.toBasicString(), + fnParas.posParas[i].first.toBasicString(), + expectedType.toString().toBasicString(), + actualType.toString().toBasicString()), + fnArgs.argv[i]); + } + evaluatedArgs.argv.push_back(argVal); + } + // default parameters type check + for (; i < fnArgs.getLength(); i++) + { + size_t defParamIndex = i - fnParas.posParas.size(); + TypeInfo expectedType(fnParas.defParas[defParamIndex].second.first); + + ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second, ctx); + if (!isTypeMatch(expectedType, defaultVal, ctx)) + { + throw EvaluatorError( + u8"DefaultParameterTypeError", + std::format( + "In function '{}', default parameter '{}' has type '{}', which does not match the expected type '{}'", + fnName.toBasicString(), + fnParas.defParas[defParamIndex].first.toBasicString(), + prettyType(defaultVal).toBasicString(), + expectedType.toString().toBasicString()), + fnArgs.argv[i]); + } + + ObjectPtr argVal = eval(fnArgs.argv[i], ctx); + TypeInfo actualType = argVal->getTypeInfo(); + if (!isTypeMatch(expectedType, argVal, ctx)) + { + throw EvaluatorError(u8"ArgumentTypeMismatchError", + std::format("In function '{}', argument '{}' expects type '{}', but got type '{}'", + fnName.toBasicString(), + fnParas.defParas[defParamIndex].first.toBasicString(), + expectedType.toString().toBasicString(), + actualType.toString().toBasicString()), + fnArgs.argv[i]); + } + evaluatedArgs.argv.push_back(argVal); + } + // default parameters filling + for (; i < fnParas.size(); i++) + { + size_t defParamIndex = i - fnParas.posParas.size(); + ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second, ctx); + evaluatedArgs.argv.push_back(defaultVal); + } + + // define parameters in new context + for (size_t j = 0; j < fnParas.size(); j++) + { + FString paramName; + TypeInfo paramType; + if (j < fnParas.posParas.size()) + { + paramName = fnParas.posParas[j].first; + paramType = TypeInfo(fnParas.posParas[j].second); + } + else + { + size_t defParamIndex = j - fnParas.posParas.size(); + paramName = fnParas.defParas[defParamIndex].first; + paramType = TypeInfo(fnParas.defParas[defParamIndex].second.first); + } + AccessModifier argAm = AccessModifier::Normal; + newContext->def(paramName, paramType, argAm, evaluatedArgs.argv[j]); + } + goto ExecuteBody; + } + + VariadicFilling: { + List list; + for (auto &exp : fnArgs.argv) + { + list.push_back(eval(exp, ctx)); // eval arguments in current scope + } + newContext->def(fnParas.variadicPara, ValueType::List, AccessModifier::Normal, std::make_shared(list)); + goto ExecuteBody; + } + + ExecuteBody: { + // execute function body + ObjectPtr retVal = Object::getNullInstance(); + for (const auto &stmt : fnStruct.body->stmts) + { + StatementResult sr = evalStatement(stmt, newContext); + if (sr.isError()) + { + throw EvaluatorError(u8"UncaughtExceptionError", + std::format("Uncaught exception: {}", sr.result->toString().toBasicString()), + stmt); + } + if (!sr.isNormal()) + { + retVal = sr.result; + break; + } + } + if (!isTypeMatch(fnStruct.retType, retVal, ctx)) + { + throw EvaluatorError(u8"ReturnTypeMismatchError", + std::format("Function '{}' expects return type '{}', but got type '{}'", + fnName.toBasicString(), + fnStruct.retType.toString().toBasicString(), + prettyType(retVal).toBasicString()), + fnStruct.body); + } + return retVal; + } + } +}; // namespace Fig \ No newline at end of file diff --git a/src/Evaluator/Core/EvalInitExpr.cpp b/src/Evaluator/Core/EvalInitExpr.cpp new file mode 100644 index 0000000..37e748b --- /dev/null +++ b/src/Evaluator/Core/EvalInitExpr.cpp @@ -0,0 +1,364 @@ +#include +#include +#include + +namespace Fig +{ + RvObject Evaluator::evalInitExpr(Ast::InitExpr initExpr, ContextPtr ctx) + { + LvObject structeLv = evalLv(initExpr->structe, ctx); + ObjectPtr structTypeVal = structeLv.get(); + const FString &structName = structeLv.name(); + if (!structTypeVal->is()) + { + throw EvaluatorError(u8"NotAStructTypeError", + std::format("'{}' is not a structure type", structName.toBasicString()), + initExpr); + } + const StructType &structT = structTypeVal->as(); + + if (structT.builtin) + { + const TypeInfo &type = structT.type; + auto &args = initExpr->args; + size_t argSize = args.size(); + + if (argSize > 1) + { + throw EvaluatorError(u8"StructInitArgumentMismatchError", + std::format("Builtin class `{}` expects 0 or 1 argument, but {} were provided", + type.toString().toBasicString(), + argSize), + initExpr); + } + + // default value + if (argSize == 0) + { + if (type == ValueType::Any || type == ValueType::Null || type == ValueType::Function) + { + throw EvaluatorError( + u8"BuiltinNotConstructibleError", + std::format("Builtin type `{}` cannot be constructed", type.toString().toBasicString()), + initExpr); + } + return std::make_shared(Object::defaultValue(type)); + } + + ObjectPtr val = eval(args[0].second, ctx); + + auto err = [&](const char *msg) { + throw EvaluatorError(u8"BuiltinInitTypeMismatchError", + std::format("Builtin `{}` constructor {}", type.toString().toBasicString(), msg), + initExpr); + }; + + // ===================== Int ===================== + if (type == ValueType::Int) + { + if (!val->is()) err("expects Int"); + return std::make_shared(val->as()); + } + + // ===================== Double ===================== + if (type == ValueType::Double) + { + if (!val->is()) err("expects Double"); + return std::make_shared(val->as()); + } + + // ===================== Bool ===================== + if (type == ValueType::Bool) + { + if (!val->is()) err("expects Bool"); + return std::make_shared(val->as()); + } + + // ===================== String ===================== + if (type == ValueType::String) + { + if (!val->is()) err("expects String"); + return std::make_shared(val->as()); + } + + // ===================== Null ===================== + if (type == ValueType::Null) + { + // Null basically ignores input but keep invariant strict: + if (!val->is()) err("expects Null"); + return Object::getNullInstance(); + } + + // ===================== List ===================== + if (type == ValueType::List) + { + if (!val->is()) err("expects List"); + + const auto &src = val->as(); + auto copied = std::make_shared(List{}); + + auto &dst = copied->as(); + dst.reserve(src.size()); + for (auto &e : src) dst.push_back(e); // shallow element copy, but new container + + return copied; + } + + // ===================== Map ===================== + if (type == ValueType::Map) + { + if (!val->is()) err("expects Map"); + + const auto &src = val->as(); + auto copied = std::make_shared(Map{}); + + auto &dst = copied->as(); + for (auto &[k, v] : src) dst.emplace(k, v); + + return copied; + } + + throw EvaluatorError( + u8"BuiltinNotConstructibleError", + std::format("Builtin type `{}` cannot be constructed", type.toString().toBasicString()), + initExpr); + } + + ContextPtr defContext = structT.defContext; // definition context + // check init args + + size_t minArgs = 0; + size_t maxArgs = structT.fields.size(); + + for (auto &f : structT.fields) + { + if (f.defaultValue == nullptr) minArgs++; + } + + size_t got = initExpr->args.size(); + if (got > maxArgs || got < minArgs) + { + throw EvaluatorError(u8"StructInitArgumentMismatchError", + std::format("Structure '{}' expects {} to {} fields, but {} were provided", + structName.toBasicString(), + minArgs, + maxArgs, + initExpr->args.size()), + initExpr); + } + + std::vector> evaluatedArgs; + + auto evalArguments = [&evaluatedArgs, initExpr, ctx, this]() { + for (const auto &[argName, argExpr] : initExpr->args) + { + evaluatedArgs.push_back({argName, eval(argExpr, ctx)}); + } + }; + + ContextPtr instanceCtx = + std::make_shared(FString(std::format("", structName.toBasicString())), ctx); + /* + 3 ways of calling constructor + .1 Person {"Fig", 1, "IDK"}; + .2 Person {name: "Fig", age: 1, sex: "IDK"}; // can be unordered + .3 Person {name, age, sex}; + */ + { + using enum Ast::InitExprAst::InitMode; + if (initExpr->initMode == Positional) + { + evalArguments(); + + for (size_t i = 0; i < maxArgs; ++i) + { + const Field &field = structT.fields[i]; + const FString &fieldName = field.name; + const TypeInfo &expectedType = field.type; + if (i >= evaluatedArgs.size()) + { + // we've checked argument count before, so here + // must be a default value + + // evaluate default value in definition context + ObjectPtr defaultVal = eval(field.defaultValue, + ctx); // it can't be null here + + // type check + if (!isTypeMatch(expectedType, defaultVal, ctx)) + { + throw EvaluatorError( + u8"StructFieldTypeMismatchError", + std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", + structName.toBasicString(), + fieldName.toBasicString(), + expectedType.toString().toBasicString(), + prettyType(defaultVal).toBasicString()), + initExpr); + } + + instanceCtx->def(fieldName, expectedType, field.am, defaultVal); + continue; + } + + const ObjectPtr &argVal = evaluatedArgs[i].second; + if (!isTypeMatch(expectedType, argVal, ctx)) + { + throw EvaluatorError( + u8"StructFieldTypeMismatchError", + std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", + structName.toBasicString(), + fieldName.toBasicString(), + expectedType.toString().toBasicString(), + prettyType(argVal).toBasicString()), + initExpr); + } + instanceCtx->def(fieldName, expectedType, field.am, argVal); + } + } + else if (initExpr->initMode == Named) + { + evalArguments(); + + // named + for (size_t i = 0; i < maxArgs; ++i) + { + const Field &field = structT.fields[i]; + const FString &fieldName = (field.name.empty() ? evaluatedArgs[i].first : field.name); + if (instanceCtx->containsInThisScope(fieldName)) + { + throw EvaluatorError(u8"StructFieldRedeclarationError", + std::format("Field '{}' already initialized in structure '{}'", + fieldName.toBasicString(), + structName.toBasicString()), + initExpr); + } + if (i + 1 > got) + { + // use default value // + // evaluate default value in definition context + ObjectPtr defaultVal = eval(field.defaultValue, + defContext); // it can't be null here + + // type check + const TypeInfo &expectedType = field.type; + if (!isTypeMatch(expectedType, defaultVal, ctx)) + { + throw EvaluatorError( + u8"StructFieldTypeMismatchError", + std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", + structName.toBasicString(), + fieldName.toBasicString(), + expectedType.toString().toBasicString(), + prettyType(defaultVal).toBasicString()), + initExpr); + } + + instanceCtx->def(fieldName, field.type, field.am, defaultVal); + continue; + } + const ObjectPtr &argVal = evaluatedArgs[i].second; + if (!isTypeMatch(field.type, argVal, ctx)) + { + throw EvaluatorError( + u8"StructFieldTypeMismatchError", + std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", + structName.toBasicString(), + fieldName.toBasicString(), + field.type.toString().toBasicString(), + prettyType(argVal).toBasicString()), + initExpr); + } + instanceCtx->def(fieldName, field.type, field.am, argVal); + } + } + else + { + // shorthand, can be unordered + // in this mode, initExpr args are all VarExpr + // field name is the variable name + for (const auto &[argName, argExpr] : initExpr->args) + { + // assert(argExpr->getType() == Ast::AstType::VarExpr); + // argName is var name + const ObjectPtr &argVal = eval(argExpr, ctx); // get the value + // find field + auto fieldIt = std::find_if(structT.fields.begin(), + structT.fields.end(), + [&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); + initExpr->initMode = Positional; + return evalInitExpr(initExpr, ctx); + } + const Field &field = *fieldIt; + if (!isTypeMatch(field.type, argVal, ctx)) + { + throw EvaluatorError( + u8"StructFieldTypeMismatchError", + std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", + structName.toBasicString(), + field.name.toBasicString(), + field.type.toString().toBasicString(), + prettyType(argVal).toBasicString()), + initExpr); + } + // field.name is argName (var name) + // Point{x=x, y=y} --> Point{x, y} + + instanceCtx->def(field.name, field.type, field.am, argVal); + } + // fill default values + size_t currentFieldCount = + initExpr->args.size(); // we have already check argument count, min <= got <= max + // so remain fields start from currentFieldCount to maxArgs + for (size_t i = currentFieldCount; i < maxArgs; ++i) + { + const Field &field = structT.fields[i]; + + // evaluate default value in definition context + ObjectPtr defaultVal = eval(field.defaultValue, + defContext); // it can't be null here + + // type check + if (!isTypeMatch(field.type, defaultVal, ctx)) + { + throw EvaluatorError( + u8"StructFieldTypeMismatchError", + std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", + structName.toBasicString(), + field.name.toBasicString(), + field.type.toString().toBasicString(), + prettyType(defaultVal).toBasicString()), + initExpr); + } + + instanceCtx->def(field.name, field.type, field.am, defaultVal); + } + } + } + ContextPtr stDefCtx = structT.defContext; + + // load struct method + for (auto &[id, fn] : stDefCtx->getFunctions()) + { + auto funcNameOpt = stDefCtx->getFunctionName(id); + assert(funcNameOpt.has_value()); + + const FString &funcName = *funcNameOpt; + auto funcSlot = stDefCtx->get(funcName); + + instanceCtx->def(funcName, + ValueType::Function, + funcSlot->am, + std::make_shared(Function(fn.paras, fn.retType, fn.body, instanceCtx))); + } + + return std::make_shared(StructInstance(structT.type, instanceCtx)); + } +}; \ No newline at end of file diff --git a/src/Evaluator/Core/EvalLvObject.cpp b/src/Evaluator/Core/EvalLvObject.cpp new file mode 100644 index 0000000..fd93001 --- /dev/null +++ b/src/Evaluator/Core/EvalLvObject.cpp @@ -0,0 +1,195 @@ +#include +#include +#include + +namespace Fig +{ + LvObject Evaluator::evalVarExpr(Ast::VarExpr var, ContextPtr ctx) + { + const FString &name = var->name; + if (!ctx->contains(name)) { throw EvaluatorError(u8"UndeclaredIdentifierError", name, var); } + return LvObject(ctx->get(name), ctx); + } + LvObject Evaluator::evalMemberExpr(Ast::MemberExpr me, ContextPtr ctx) + { + // LvObject base = evalLv(me->base, ctx); + RvObject baseVal = eval(me->base, ctx); + const FString &member = me->member; + if (baseVal->getTypeInfo() == ValueType::Module) + { + const Module &mod = baseVal->as(); + if (mod.ctx->contains(member) && mod.ctx->isVariablePublic(member)) + { + return LvObject(mod.ctx->get(member), ctx); + } + else + { + throw EvaluatorError(u8"VariableNotFoundError", + std::format("`{}` has not variable '{}', check if it is public", + baseVal->toString().toBasicString(), + member.toBasicString()), + me->base); + } + } + if (baseVal->hasMemberFunction(member)) + { + return LvObject(std::make_shared( + 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 + } + + if (ctx->hasMethodImplemented(baseVal->getTypeInfo(), member)) + { + // builtin type implementation! + // e.g. impl xxx for Int + + auto &fn = ctx->getImplementedMethod(baseVal->getTypeInfo(), member); + Function boundFn(fn.paras, + fn.retType, + fn.body, + ctx // current context + ); + return LvObject( + std::make_shared( + member, std::make_shared(boundFn), ValueType::Function, AccessModifier::PublicConst), + ctx); + } + + if (baseVal->getTypeInfo() != ValueType::StructInstance) // and not member function found + { + throw EvaluatorError( + u8"NoAttributeError", + std::format("`{}` has not attribute '{}'", baseVal->toString().toBasicString(), member.toBasicString()), + me->base); + } + const StructInstance &si = baseVal->as(); + if (ctx->hasMethodImplemented(si.parentType, member)) + { + auto &fn = ctx->getImplementedMethod(si.parentType, member); + Function boundFn(fn.paras, + fn.retType, + fn.body, + si.localContext // create a new function and set closure context + // to struct instance context + ); + return LvObject( + std::make_shared( + member, std::make_shared(boundFn), ValueType::Function, AccessModifier::PublicConst), + ctx); + } + else if (si.localContext->containsInThisScope(member) && si.localContext->isVariablePublic(member)) + { + return LvObject(si.localContext->get(member), ctx); + } + else if (ctx->hasDefaultImplementedMethod(si.parentType, member)) + { + const auto &ifm = ctx->getDefaultImplementedMethod(si.parentType, member); + Function fn(ifm.paras, actualType(eval(ifm.returnType, ctx)), ifm.defaultBody, ctx); + + return LvObject(std::make_shared( + member, std::make_shared(fn), ValueType::Function, AccessModifier::PublicConst), + ctx); + } + else + { + throw EvaluatorError(u8"NoAttributeError", + std::format("`{}` has not attribute '{}' and no interfaces have been implemented it", + baseVal->toString().toBasicString(), + member.toBasicString()), + me->base); + } + } + LvObject Evaluator::evalIndexExpr(Ast::IndexExpr ie, ContextPtr ctx) + { + RvObject base = eval(ie->base, ctx); + RvObject index = eval(ie->index, ctx); + + const TypeInfo &type = base.get()->getTypeInfo(); + + if (type == ValueType::List) + { + if (index->getTypeInfo() != ValueType::Int) + { + throw EvaluatorError( + u8"TypeError", + std::format("Type `List` indices must be `Int`, got '{}'", prettyType(index).toBasicString()), + ie->index); + } + List &list = base.get()->as(); + ValueType::IntClass indexVal = index->as(); + if (indexVal >= list.size()) + { + throw EvaluatorError( + u8"IndexOutOfRangeError", + std::format("Index {} out of list `{}` range", indexVal, base->toString().toBasicString()), + ie->index); + } + return LvObject(base, indexVal, LvObject::Kind::ListElement, ctx); + } + else if (type == ValueType::Map) { return LvObject(base, index, LvObject::Kind::MapElement, ctx); } + else if (type == ValueType::String) + { + if (index->getTypeInfo() != ValueType::Int) + { + throw EvaluatorError( + u8"TypeError", + std::format("Type `String` indices must be `Int`, got '{}'", prettyType(index).toBasicString()), + ie->index); + } + 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->toString().toBasicString()), + ie->index); + } + return LvObject(base, indexVal, LvObject::Kind::StringElement, ctx); + } + else + { + throw EvaluatorError( + u8"NoSubscriptableError", + std::format("`{}` object is not subscriptable", base->getTypeInfo().toString().toBasicString()), + ie->base); + } + } + LvObject Evaluator::evalLv(Ast::Expression exp, ContextPtr ctx) + { + using Ast::Operator; + using Ast::AstType; + switch (exp->getType()) + { + case AstType::VarExpr: { + Ast::VarExpr var = std::static_pointer_cast(exp); + + return evalVarExpr(var, ctx); + } + case AstType::MemberExpr: { + Ast::MemberExpr me = std::static_pointer_cast(exp); + + return evalMemberExpr(me, ctx); + } + case AstType::IndexExpr: { + Ast::IndexExpr ie = std::static_pointer_cast(exp); + + return evalIndexExpr(ie, ctx); + } + default: { + throw EvaluatorError( + u8"TypeError", + std::format("Expression '{}' doesn't refer to a lvalue", exp->typeName().toBasicString()), + exp); + } + } + } +}; // namespace Fig \ No newline at end of file diff --git a/src/Evaluator/Core/EvalStatement.cpp b/src/Evaluator/Core/EvalStatement.cpp new file mode 100644 index 0000000..1eb1db6 --- /dev/null +++ b/src/Evaluator/Core/EvalStatement.cpp @@ -0,0 +1,510 @@ +#include +#include +#include + +#include + +namespace Fig +{ + StatementResult Evaluator::evalStatement(Ast::Statement stmt, ContextPtr ctx) + { + using enum Ast::AstType; + switch (stmt->getType()) + { + case ImportSt: { + auto i = std::static_pointer_cast(stmt); + return evalImportSt(i, ctx); + } + case VarDefSt: { + auto varDef = std::static_pointer_cast(stmt); + + + if (ctx->containsInThisScope(varDef->name)) + { + throw EvaluatorError( + u8"RedeclarationError", + std::format("Variable `{}` already declared in this scope", varDef->name.toBasicString()), + varDef); + } + + RvObject value = nullptr; + if (varDef->expr) { value = eval(varDef->expr, ctx); } + + TypeInfo declaredType; // default is Any + const Ast::Expression &declaredTypeExp = varDef->declaredType; + + if (varDef->followupType) { declaredType = actualType(value); } + else if (declaredTypeExp) + { + ObjectPtr declaredTypeValue = eval(declaredTypeExp, ctx); + declaredType = actualType(declaredTypeValue); + + if (value != nullptr && !isTypeMatch(declaredType, value, ctx)) + { + throw EvaluatorError(u8"TypeError", + std::format("Variable `{}` expects init-value type `{}`, but got '{}'", + varDef->name.toBasicString(), + prettyType(declaredTypeValue).toBasicString(), + prettyType(value).toBasicString()), + varDef->expr); + } + else if (value == nullptr) + { + value = std::make_shared(Object::defaultValue(declaredType)); + } // else -> Ok + } // else -> type is Any (default) + AccessModifier am = + (varDef->isConst ? (varDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const) : + (varDef->isPublic ? AccessModifier::Public : AccessModifier::Normal)); + ctx->def(varDef->name, declaredType, am, value); + return StatementResult::normal(); + } + + case FunctionDefSt: { + auto fnDef = std::static_pointer_cast(stmt); + + + const FString &fnName = fnDef->name; + if (ctx->containsInThisScope(fnName)) + { + throw EvaluatorError( + u8"RedeclarationError", + std::format("Function `{}` already declared in this scope", fnName.toBasicString()), + fnDef); + } + TypeInfo returnType = ValueType::Any; + if (fnDef->retType) + { + ObjectPtr returnTypeValue = eval(fnDef->retType, ctx); + returnType = actualType(returnTypeValue); + } + + Function fn(fnDef->paras, returnType, fnDef->body, ctx); + ctx->def(fnName, + ValueType::Function, + (fnDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const), + std::make_shared(fn)); + return StatementResult::normal(); + } + + case StructSt: { + auto stDef = std::static_pointer_cast(stmt); + + + if (ctx->containsInThisScope(stDef->name)) + { + throw EvaluatorError( + u8"RedeclarationError", + std::format("Structure '{}' already defined in this scope", stDef->name.toBasicString()), + stDef); + } + std::vector fields; + std::vector _fieldNames; + for (Ast::StructDefField field : stDef->fields) + { + if (Utils::vectorContains(field.fieldName, _fieldNames)) + { + throw EvaluatorError(u8"RedeclarationError", + std::format("Field '{}' already defined in structure '{}'", + field.fieldName.toBasicString(), + stDef->name.toBasicString()), + stDef); + } + TypeInfo fieldType = ValueType::Any; + if (field.declaredType) + { + ObjectPtr declaredTypeValue = eval(field.declaredType, ctx); + fieldType = actualType(declaredTypeValue); + } + + fields.push_back(Field(field.am, field.fieldName, fieldType, field.defaultValueExpr)); + } + ContextPtr defContext = std::make_shared(FString(std::format("", + stDef->name.toBasicString(), + stDef->getAAI().line, + stDef->getAAI().column)), + ctx); + const Ast::BlockStatement &body = stDef->body; + for (auto &st : body->stmts) + { + if (st->getType() != Ast::AstType::FunctionDefSt) + { + throw EvaluatorError(u8"UnexpectedStatementInStructError", + std::format("Unexpected statement `{}` in struct declaration", + st->toString().toBasicString()), + st); + } + evalStatement(st, defContext); // function def st + } + + AccessModifier am = (stDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const); + TypeInfo type(stDef->name, true); // register type name + ctx->def(stDef->name, + ValueType::StructType, + am, + std::make_shared(StructType(type, defContext, fields))); + return StatementResult::normal(); + } + + case InterfaceDefSt: { + auto ifd = std::static_pointer_cast(stmt); + + + const FString &interfaceName = ifd->name; + + if (ctx->containsInThisScope(interfaceName)) + { + throw EvaluatorError( + u8"RedeclarationError", + std::format("Interface `{}` already declared in this scope", interfaceName.toBasicString()), + ifd); + } + TypeInfo type(interfaceName, true); // register interface + ctx->def(interfaceName, + type, + (ifd->isPublic ? AccessModifier::PublicConst : AccessModifier::Const), + std::make_shared(InterfaceType(type, ifd->methods))); + return StatementResult::normal(); + } + + case ImplementSt: { + auto ip = std::static_pointer_cast(stmt); + + + TypeInfo structType(ip->structName); + TypeInfo interfaceType(ip->interfaceName); + if (ctx->hasImplRegisted(structType, interfaceType)) + { + throw EvaluatorError(u8"DuplicateImplError", + std::format("Duplicate implement `{}` for `{}`", + interfaceType.toString().toBasicString(), + structType.toString().toBasicString()), + ip); + } + if (!ctx->contains(ip->interfaceName)) + { + throw EvaluatorError(u8"InterfaceNotFoundError", + std::format("Interface '{}' not found", ip->interfaceName.toBasicString()), + ip); + } + if (!ctx->contains(ip->structName)) + { + throw EvaluatorError(u8"StructNotFoundError", + std::format("Struct '{}' not found", ip->structName.toBasicString()), + ip); + } + auto interfaceSlot = ctx->get(ip->interfaceName); + auto structSlot = ctx->get(ip->structName); + + LvObject interfaceLv(interfaceSlot, ctx); + LvObject structLv(structSlot, ctx); + + ObjectPtr interfaceObj = interfaceLv.get(); + ObjectPtr structTypeObj = structLv.get(); + + if (!interfaceObj->is()) + { + throw EvaluatorError( + u8"NotAInterfaceError", + std::format("Variable `{}` is not a interface", ip->interfaceName.toBasicString()), + ip); + } + if (!structTypeObj->is()) + { + throw EvaluatorError( + u8"NotAStructType", + std::format("Variable `{}` is not a struct type", ip->structName.toBasicString()), + ip); + } + auto &implementMethods = ip->methods; + InterfaceType &interface = interfaceObj->as(); + + // ===== interface implementation validation ===== + ImplRecord record{interfaceType, structType, {}}; + + std::unordered_map ifaceMethods; + for (auto &m : interface.methods) + { + if (ifaceMethods.contains(m.name)) + { + throw EvaluatorError(u8"InterfaceDuplicateMethodError", + std::format("Interface '{}' has duplicate method '{}'", + interfaceType.toString().toBasicString(), + m.name.toBasicString()), + ip); + } + ifaceMethods[m.name] = m; + } + + std::unordered_set implemented; + + for (auto &implMethod : implementMethods) + { + const FString &name = implMethod.name; + + // ---- redundant impl ---- + if (!ifaceMethods.contains(name)) + { + throw EvaluatorError(u8"RedundantImplementationError", + std::format("Struct '{}' implements extra method '{}' " + "which is not required by interface '{}'", + structType.toString().toBasicString(), + name.toBasicString(), + interfaceType.toString().toBasicString()), + ip); + } + + if (implemented.contains(name)) + { + throw EvaluatorError(u8"DuplicateImplementMethodError", + std::format("Duplicate implement method '{}'", name.toBasicString()), + ip); + } + + auto &ifMethod = ifaceMethods[name]; + + // ---- signature check ---- + if (!isInterfaceSignatureMatch(implMethod, ifMethod)) + { + throw EvaluatorError(u8"InterfaceSignatureMismatch", + std::format("Interface method '{}({})' signature mismatch with " + "implementation '{}({})'", + ifMethod.name.toBasicString(), + ifMethod.paras.toString().toBasicString(), + implMethod.name.toBasicString(), + implMethod.paras.toString().toBasicString()), + ip); + } + + if (ctx->hasMethodImplemented(structType, name)) + { + throw EvaluatorError(u8"DuplicateImplementMethodError", + std::format("Method '{}' already implemented by another interface " + "for struct '{}'", + name.toBasicString(), + structType.toString().toBasicString()), + ip); + } + + implemented.insert(name); + + ObjectPtr returnTypeValue = eval(ifMethod.returnType, ctx); + + record.implMethods[name] = + Function(implMethod.paras, actualType(returnTypeValue), implMethod.body, ctx); + } + + for (auto &m : interface.methods) + { + if (implemented.contains(m.name)) continue; + + if (m.hasDefaultBody()) continue; + + throw EvaluatorError(u8"MissingImplementationError", + std::format("Struct '{}' does not implement required interface method '{}' " + "and interface '{}' provides no default implementation", + structType.toString().toBasicString(), + m.name.toBasicString(), + interfaceType.toString().toBasicString()), + ip); + } + + ctx->setImplRecord(structType, interfaceType, record); + return StatementResult::normal(); + } + + case IfSt: { + auto ifSt = std::static_pointer_cast(stmt); + ObjectPtr condVal = eval(ifSt->condition, ctx); + if (condVal->getTypeInfo() != ValueType::Bool) + { + throw EvaluatorError( + u8"TypeError", + std::format("Condition must be boolean, but got '{}'", prettyType(condVal).toBasicString()), + ifSt->condition); + } + if (condVal->as()) { return evalBlockStatement(ifSt->body, ctx); } + // else + for (const auto &elif : ifSt->elifs) + { + ObjectPtr elifCondVal = eval(elif->condition, ctx); + if (elifCondVal->getTypeInfo() != ValueType::Bool) + { + throw EvaluatorError( + u8"TypeError", + std::format("Condition must be boolean, but got '{}'", prettyType(condVal).toBasicString()), + ifSt->condition); + } + if (elifCondVal->as()) { return evalBlockStatement(elif->body, ctx); } + } + if (ifSt->els) { return evalBlockStatement(ifSt->els->body, ctx); } + return StatementResult::normal(); + }; + case WhileSt: { + auto whileSt = std::static_pointer_cast(stmt); + while (true) + { + ObjectPtr condVal = eval(whileSt->condition, ctx); + if (condVal->getTypeInfo() != ValueType::Bool) + { + throw EvaluatorError( + u8"TypeError", + std::format("Condition must be boolean, but got '{}'", prettyType(condVal).toBasicString()), + whileSt->condition); + } + if (!condVal->as()) { break; } + ContextPtr loopContext = std::make_shared( + FString(std::format("", whileSt->getAAI().line, whileSt->getAAI().column)), + ctx); // every loop has its own context + StatementResult sr = evalBlockStatement(whileSt->body, loopContext); + if (sr.shouldReturn()) { return sr; } + if (sr.shouldBreak()) { break; } + if (sr.shouldContinue()) { continue; } + } + return StatementResult::normal(); + }; + case ForSt: { + auto forSt = std::static_pointer_cast(stmt); + ContextPtr loopContext = std::make_shared( + FString(std::format("", forSt->getAAI().line, forSt->getAAI().column)), + ctx); // for loop has its own context + + evalStatement(forSt->initSt, + loopContext); // ignore init statement result + size_t iteration = 0; + + while (true) // use while loop to simulate for loop, cause we + // need to check condition type every iteration + { + ObjectPtr condVal = eval(forSt->condition, loopContext); + if (condVal->getTypeInfo() != ValueType::Bool) + { + throw EvaluatorError( + u8"TypeError", + std::format("Condition must be boolean, but got '{}'", prettyType(condVal).toBasicString()), + forSt->condition); + } + if (!condVal->as()) { break; } + iteration++; + ContextPtr iterationContext = std::make_shared( + FString(std::format( + "", forSt->getAAI().line, forSt->getAAI().column, iteration)), + loopContext); // every loop has its own context + StatementResult sr = evalBlockStatement(forSt->body, iterationContext); + if (sr.shouldReturn()) { return sr; } + if (sr.shouldBreak()) { break; } + if (sr.shouldContinue()) + { + // continue to next iteration + continue; + } + evalStatement(forSt->incrementSt, + loopContext); // ignore increment statement result + } + return StatementResult::normal(); + } + + case TrySt: { + auto tryst = std::static_pointer_cast(stmt); + + + ContextPtr tryCtx = std::make_shared( + FString(std::format("", tryst->getAAI().line, tryst->getAAI().column)), ctx); + StatementResult sr = StatementResult::normal(); + for (auto &stmt : tryst->body->stmts) + { + sr = evalStatement(stmt, tryCtx); // eval in try context + if (sr.isError()) { break; } + } + bool catched = false; + for (auto &cat : tryst->catches) + { + const FString &errVarName = cat.errVarName; + TypeInfo errVarType = (cat.hasType ? TypeInfo(cat.errVarType) : ValueType::Any); + if (isTypeMatch(errVarType, sr.result, ctx)) + { + ContextPtr catchCtx = std::make_shared( + FString( + std::format("", cat.body->getAAI().line, cat.body->getAAI().column)), + ctx); + catchCtx->def(errVarName, errVarType, AccessModifier::Normal, sr.result); + sr = evalBlockStatement(cat.body, catchCtx); + catched = true; + break; + } + } + if (!catched) + { + throw EvaluatorError(u8"UncaughtExceptionError", + std::format("Uncaught exception: {}", sr.result->toString().toBasicString()), + tryst); + } + if (tryst->finallyBlock) { sr = evalBlockStatement(tryst->finallyBlock, ctx); } + return sr; + } + + case ThrowSt: { + auto ts = std::static_pointer_cast(stmt); + + + ObjectPtr value = eval(ts->value, ctx); + if (value->is()) + { + throw EvaluatorError(u8"TypeError", u8"Why did you throw a null?", ts); + } + return StatementResult::errorFlow(value); + } + + case ReturnSt: { + auto returnSt = std::static_pointer_cast(stmt); + + + ObjectPtr returnValue = Object::getNullInstance(); // default is null + if (returnSt->retValue) returnValue = eval(returnSt->retValue, ctx); + return StatementResult::returnFlow(returnValue); + } + + case BreakSt: { + if (!ctx->parent) + { + throw EvaluatorError(u8"BreakOutsideLoopError", u8"`break` statement outside loop", stmt); + } + if (!ctx->isInLoopContext()) + { + throw EvaluatorError(u8"BreakOutsideLoopError", u8"`break` statement outside loop", stmt); + } + return StatementResult::breakFlow(); + } + + case ContinueSt: { + if (!ctx->parent) + { + throw EvaluatorError(u8"ContinueOutsideLoopError", u8"`continue` statement outside loop", stmt); + } + if (!ctx->isInLoopContext()) + { + throw EvaluatorError(u8"ContinueOutsideLoopError", u8"`continue` statement outside loop", stmt); + } + return StatementResult::continueFlow(); + } + + case ExpressionStmt: { + auto exprStmt = std::static_pointer_cast(stmt); + return StatementResult::normal(eval(exprStmt->exp, ctx)); + } + + case BlockStatement: { + auto block = std::static_pointer_cast(stmt); + + + ContextPtr blockCtx = std::make_shared( + FString(std::format("", block->getAAI().line, block->getAAI().column)), ctx); + return evalBlockStatement(block, blockCtx); + } + + default: + throw RuntimeError( + FString(std::format("Feature stmt {} unsupported yet", magic_enum::enum_name(stmt->getType())))); + } + } +}; \ No newline at end of file diff --git a/src/Evaluator/Core/EvalTernary.cpp b/src/Evaluator/Core/EvalTernary.cpp new file mode 100644 index 0000000..6559904 --- /dev/null +++ b/src/Evaluator/Core/EvalTernary.cpp @@ -0,0 +1,20 @@ +#include +#include + +namespace Fig +{ + + RvObject Evaluator::evalTernary(Ast::TernaryExpr te, ContextPtr ctx) + { + RvObject condVal = eval(te->condition, ctx); + if (condVal->getTypeInfo() != ValueType::Bool) + { + throw EvaluatorError( + u8"TypeError", + std::format("Condition must be boolean, got '{}'", prettyType(condVal).toBasicString()), + te->condition); + } + ValueType::BoolClass cond = condVal->as(); + return (cond ? eval(te->valueT, ctx) : eval(te->valueF, ctx)); + } +}; \ No newline at end of file diff --git a/src/Evaluator/Core/EvalUnary.cpp b/src/Evaluator/Core/EvalUnary.cpp new file mode 100644 index 0000000..a2e1d44 --- /dev/null +++ b/src/Evaluator/Core/EvalUnary.cpp @@ -0,0 +1,31 @@ +#include +#include +#include + +namespace Fig +{ + RvObject Evaluator::evalUnary(Ast::UnaryExpr un, ContextPtr ctx) + { + using Ast::Operator; + Operator op = un->op; + Ast::Expression exp = un->exp; + ObjectPtr value = eval(exp, ctx); + switch (op) + { + case Operator::Not: { + return std::make_shared(!(*value)); + } + case Operator::Subtract: { + return std::make_shared(-(*value)); + } + case Operator::BitNot: { + return std::make_shared(bit_not((*value))); + } + default: { + throw EvaluatorError(u8"UnsupportedOpError", + std::format("Unsupported op '{}' for unary expression", magic_enum::enum_name(op)), + un); + } + } + } +}; \ No newline at end of file diff --git a/src/Evaluator/Core/ResolveModulePath.cpp b/src/Evaluator/Core/ResolveModulePath.cpp new file mode 100644 index 0000000..12c460d --- /dev/null +++ b/src/Evaluator/Core/ResolveModulePath.cpp @@ -0,0 +1,114 @@ +#include + +#include +#include + +namespace Fig +{ + + std::filesystem::path Evaluator::resolveModulePath(const std::vector &pathVec) + { + namespace fs = std::filesystem; + + static const std::vector defaultLibraryPath{"Library", "Library/fpm"}; + + std::vector pathToFind(defaultLibraryPath); + + fs::path interpreterPath = getExecutablePath().parent_path(); + + for (fs::path &p : pathToFind) + { + p = interpreterPath / p; // 相对路径 -> 绝对路径 + } + + pathToFind.insert( + pathToFind.begin(), + fs::path(this->sourcePath.toBasicString()).parent_path()); // first search module at the source file path + + fs::path path; + + /* + Example: + import comp.config; + */ + + const FString &modPathStrTop = pathVec.at(0); + fs::path modPath; + + bool found = false; + for (auto &parentFolder : pathToFind) + { + modPath = parentFolder / FString(modPathStrTop + u8".fig").toBasicString(); + if (fs::exists(modPath)) + { + path = modPath; + found = true; + break; + } + else + { + modPath = parentFolder / modPathStrTop.toBasicString(); + if (fs::is_directory(modPath)) // comp is a directory + { + modPath = modPath / FString(modPathStrTop + u8".fig").toBasicString(); + /* + if module name is a directory, we require [module + name].fig at the directory + */ + if (!fs::exists(modPath)) + { + throw RuntimeError(FString(std::format("requires module file, {}\\{}", + modPathStrTop.toBasicString(), + FString(modPathStrTop + u8".fig").toBasicString()))); + } + found = true; + path = modPath; + break; + } + } + } + + if (!found) + throw RuntimeError(FString(std::format("Could not find module `{}`", modPathStrTop.toBasicString()))); + + bool found2 = false; + + for (size_t i = 1; i < pathVec.size(); ++i) // has next module + { + const FString &next = pathVec.at(i); + modPath = modPath.parent_path(); // get the folder + modPath = modPath / FString(next + u8".fig").toBasicString(); + if (fs::exists(modPath)) + { + if (i != pathVec.size() - 1) + throw RuntimeError(FString(std::format( + "expects {} as parent directory and find next module, but got a file", next.toBasicString()))); + // it's the last module + found2 = true; + path = modPath; + break; + } + // `next` is a folder + modPath = modPath.parent_path() / next.toBasicString(); + if (!fs::exists(modPath)) + throw RuntimeError(FString(std::format("Could not find module `{}`", next.toBasicString()))); + if (i == pathVec.size() - 1) + { + // `next` is the last module + modPath = modPath / FString(next + u8".fig").toBasicString(); + if (!fs::exists(modPath)) + { + throw RuntimeError(FString(std::format( + "expects {} as parent directory and find next module, but got a file", next.toBasicString()))); + } + found2 = true; + path = modPath; + } + } + + if (!found2 && !fs::exists(modPath)) + throw RuntimeError(FString(std::format("Could not find module `{}`", pathVec.end()->toBasicString()))); + + return path; + } +}; \ No newline at end of file diff --git a/src/Evaluator/Core/StatementResult.hpp b/src/Evaluator/Core/StatementResult.hpp new file mode 100644 index 0000000..f94374f --- /dev/null +++ b/src/Evaluator/Core/StatementResult.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +namespace Fig +{ + struct StatementResult + { + ObjectPtr result; + enum class Flow + { + Normal, + Return, + Break, + Continue, + Error + } flow; + + StatementResult(ObjectPtr val, Flow f = Flow::Normal) : result(val), flow(f) {} + + static StatementResult normal(ObjectPtr val = Object::getNullInstance()) + { + return StatementResult(val, Flow::Normal); + } + static StatementResult returnFlow(ObjectPtr val) { return StatementResult(val, Flow::Return); } + static StatementResult breakFlow() { return StatementResult(Object::getNullInstance(), Flow::Break); } + static StatementResult continueFlow() { return StatementResult(Object::getNullInstance(), Flow::Continue); } + static StatementResult errorFlow(ObjectPtr val) { return StatementResult(val, Flow::Error); } + + bool isNormal() const { return flow == Flow::Normal; } + bool shouldReturn() const { return flow == Flow::Return; } + bool shouldBreak() const { return flow == Flow::Break; } + bool shouldContinue() const { return flow == Flow::Continue; } + bool isError() const { return flow == Flow::Error; } + }; +}; \ No newline at end of file diff --git a/src/Evaluator/Value/IntPool.hpp b/src/Evaluator/Value/IntPool.hpp index 203f323..55f5fe4 100644 --- a/src/Evaluator/Value/IntPool.hpp +++ b/src/Evaluator/Value/IntPool.hpp @@ -1,9 +1,9 @@ #pragma once -#include "Value/value.hpp" +#include #include -#include -#include +#include +#include #include #include @@ -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/LvObject.hpp b/src/Evaluator/Value/LvObject.hpp index 4636c34..002a4e6 100644 --- a/src/Evaluator/Value/LvObject.hpp +++ b/src/Evaluator/Value/LvObject.hpp @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include namespace Fig { diff --git a/src/Evaluator/Value/Type.hpp b/src/Evaluator/Value/Type.hpp index b8ace30..ded549a 100644 --- a/src/Evaluator/Value/Type.hpp +++ b/src/Evaluator/Value/Type.hpp @@ -14,44 +14,32 @@ namespace Fig private: size_t id; + std::map &getTypeMap() + { + static std::map typeMap; + return typeMap; + } + public: friend class TypeInfoHash; FString name; - FString toString() const - { - return name; - } + FString toString() const { return name; } - static std::map typeMap; - - static size_t getID(FString _name) - { - return typeMap.at(_name); - } - size_t getInstanceID() const - { - return id; - } + size_t getInstanceID() const { return id; } TypeInfo(); explicit TypeInfo(const FString &_name, bool reg = false); TypeInfo(const TypeInfo &other) = default; - bool operator==(const TypeInfo &other) const - { - return id == other.id; - } + bool operator==(const TypeInfo &other) const { return id == other.id; } }; - + class TypeInfoHash { public: - std::size_t operator()(const TypeInfo &ti) const - { - return std::hash{}(ti.id); - } + std::size_t operator()(const TypeInfo &ti) const { return std::hash{}(ti.id); } }; // class Value; @@ -77,39 +65,31 @@ namespace Fig using NullClass = std::monostate; using StringClass = FString; - static const std::unordered_set builtinTypes - { - Any, - Null, - Int, - String, - Bool, - Double, - Function, - StructType, - StructInstance, - List, - Map, - Module, - InterfaceType - }; - inline bool isTypeBuiltin(const TypeInfo &type) { + static const std::unordered_set builtinTypes{Any, + Null, + Int, + String, + Bool, + Double, + Function, + StructType, + StructInstance, + List, + Map, + Module, + InterfaceType}; return builtinTypes.contains(type); } }; // namespace ValueType }; // namespace Fig - namespace std { template <> struct hash { - size_t operator()(const Fig::TypeInfo &t) - { - return std::hash{}(t.getInstanceID()); - } + size_t operator()(const Fig::TypeInfo &t) { return std::hash{}(t.getInstanceID()); } }; -}; \ No newline at end of file +}; // namespace std \ No newline at end of file diff --git a/src/Evaluator/Value/VariableSlot.hpp b/src/Evaluator/Value/VariableSlot.hpp index f65cb17..f2080a6 100644 --- a/src/Evaluator/Value/VariableSlot.hpp +++ b/src/Evaluator/Value/VariableSlot.hpp @@ -3,8 +3,8 @@ #include #include -#include -#include +#include +#include #include namespace Fig diff --git a/src/Evaluator/Value/function.hpp b/src/Evaluator/Value/function.hpp index 98b15e2..8e1d368 100644 --- a/src/Evaluator/Value/function.hpp +++ b/src/Evaluator/Value/function.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include @@ -11,51 +11,97 @@ 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()), type(Normal) + { + // 需要初始化 union ! + new (¶s) Ast::FunctionParameters(); + new (&retType) TypeInfo(); + new (&body) Ast::BlockStatement(); + } - 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 +109,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/interface.hpp b/src/Evaluator/Value/interface.hpp index 59697c5..1c27a84 100644 --- a/src/Evaluator/Value/interface.hpp +++ b/src/Evaluator/Value/interface.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include diff --git a/src/Evaluator/Value/module.hpp b/src/Evaluator/Value/module.hpp index 9689032..5720add 100644 --- a/src/Evaluator/Value/module.hpp +++ b/src/Evaluator/Value/module.hpp @@ -2,7 +2,7 @@ #include -#include +#include namespace Fig { diff --git a/src/Evaluator/Value/structInstance.hpp b/src/Evaluator/Value/structInstance.hpp index 7d12dd0..eccc158 100644 --- a/src/Evaluator/Value/structInstance.hpp +++ b/src/Evaluator/Value/structInstance.hpp @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include namespace Fig { diff --git a/src/Evaluator/Value/structType.hpp b/src/Evaluator/Value/structType.hpp index 4a78830..35ec7eb 100644 --- a/src/Evaluator/Value/structType.hpp +++ b/src/Evaluator/Value/structType.hpp @@ -3,9 +3,9 @@ #include #include -#include +#include -#include +#include #include #include diff --git a/src/Evaluator/Value/value.cpp b/src/Evaluator/Value/value.cpp index 16cbbd5..0fd58a2 100644 --- a/src/Evaluator/Value/value.cpp +++ b/src/Evaluator/Value/value.cpp @@ -1,13 +1,13 @@ -#include "Value/structType.hpp" -#include -#include -#include +#include +#include +#include +#include +#include // #include namespace Fig { - std::map TypeInfo::typeMap = {}; TypeInfo::TypeInfo() : // only allow use in evaluate time !! <---- dynamic type system requirement id(1), name(FString(u8"Any")) @@ -20,17 +20,17 @@ namespace Fig // std::cerr << "TypeInfo constructor called for type name: " << name.toBasicString() << "\n"; if (reg) { - typeMap[name] = ++id_count; + getTypeMap()[name] = ++id_count; id = id_count; } else { - if (!typeMap.contains(_name)) + if (!getTypeMap().contains(_name)) { throw RuntimeError(FString(std::format("No type named '{}'", _name.toBasicString()))); // *this = ValueType::String; } - id = typeMap.at(name); // may throw + id = getTypeMap().at(name); // may throw } } diff --git a/src/Evaluator/Value/value.hpp b/src/Evaluator/Value/value.hpp index feaa44f..ee8b472 100644 --- a/src/Evaluator/Value/value.hpp +++ b/src/Evaluator/Value/value.hpp @@ -1,13 +1,16 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include -#include +#include "Core/fig_string.hpp" +#include +#include +#include +#include +#include +#include +#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.cpp b/src/Evaluator/evaluator.cpp index 8073f6d..f6057c5 100644 --- a/src/Evaluator/evaluator.cpp +++ b/src/Evaluator/evaluator.cpp @@ -1,32 +1,12 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include -#include + +#include +#include #include #include #include -#include #ifndef SourceInfo #define SourceInfo(ptr) (ptr->sourcePath), (ptr->sourceLines) @@ -41,1153 +21,6 @@ namespace Fig return implMethod.name == ifMethod.name && implMethod.paras == ifMethod.paras; } - LvObject Evaluator::evalVarExpr(Ast::VarExpr var, ContextPtr ctx) - { - const FString &name = var->name; - if (!ctx->contains(name)) { throw EvaluatorError(u8"UndeclaredIdentifierError", name, var); } - return LvObject(ctx->get(name), ctx); - } - LvObject Evaluator::evalMemberExpr(Ast::MemberExpr me, ContextPtr ctx) - { - // LvObject base = evalLv(me->base, ctx); - RvObject baseVal = eval(me->base, ctx); - const FString &member = me->member; - if (baseVal->getTypeInfo() == ValueType::Module) - { - const Module &mod = baseVal->as(); - if (mod.ctx->contains(member) && mod.ctx->isVariablePublic(member)) - { - return LvObject(mod.ctx->get(member), ctx); - } - else - { - throw EvaluatorError(u8"VariableNotFoundError", - std::format("`{}` has not variable '{}', check if it is public", - baseVal->toString().toBasicString(), - member.toBasicString()), - me->base); - } - } - if (baseVal->hasMemberFunction(member)) - { - return LvObject(std::make_shared( - member, - std::make_shared(Function(baseVal->getMemberFunction(member), - baseVal->getMemberFunctionParaCount(member))), - ValueType::Function, - AccessModifier::PublicConst), - ctx); // fake l-value - } - - if (ctx->hasMethodImplemented(baseVal->getTypeInfo(), member)) - { - // builtin type implementation! - // e.g. impl xxx for Int - - auto &fn = ctx->getImplementedMethod(baseVal->getTypeInfo(), member); - Function boundFn(fn.paras, - fn.retType, - fn.body, - ctx // current context - ); - return LvObject( - std::make_shared( - member, std::make_shared(boundFn), ValueType::Function, AccessModifier::PublicConst), - ctx); - } - - if (baseVal->getTypeInfo() != ValueType::StructInstance) // and not member function found - { - throw EvaluatorError( - u8"NoAttributeError", - std::format("`{}` has not attribute '{}'", baseVal->toString().toBasicString(), member.toBasicString()), - me->base); - } - const StructInstance &si = baseVal->as(); - if (ctx->hasMethodImplemented(si.parentType, member)) - { - auto &fn = ctx->getImplementedMethod(si.parentType, member); - Function boundFn(fn.paras, - fn.retType, - fn.body, - si.localContext // create a new function and set closure context - // to struct instance context - ); - return LvObject( - std::make_shared( - member, std::make_shared(boundFn), ValueType::Function, AccessModifier::PublicConst), - ctx); - } - else if (si.localContext->containsInThisScope(member) && si.localContext->isVariablePublic(member)) - { - return LvObject(si.localContext->get(member), ctx); - } - else if (ctx->hasDefaultImplementedMethod(si.parentType, member)) - { - const auto &ifm = ctx->getDefaultImplementedMethod(si.parentType, member); - Function fn(ifm.paras, actualType(eval(ifm.returnType, ctx)), ifm.defaultBody, ctx); - - return LvObject(std::make_shared( - member, std::make_shared(fn), ValueType::Function, AccessModifier::PublicConst), - ctx); - } - else - { - throw EvaluatorError(u8"NoAttributeError", - std::format("`{}` has not attribute '{}' and no interfaces have been implemented it", - baseVal->toString().toBasicString(), - member.toBasicString()), - me->base); - } - } - LvObject Evaluator::evalIndexExpr(Ast::IndexExpr ie, ContextPtr ctx) - { - LvObject base = evalLv(ie->base, ctx); - RvObject index = eval(ie->index, ctx); - - const TypeInfo &type = base.get()->getTypeInfo(); - - if (type == ValueType::List) - { - if (index->getTypeInfo() != ValueType::Int) - { - throw EvaluatorError( - u8"TypeError", - std::format("Type `List` indices must be `Int`, got '{}'", prettyType(index).toBasicString()), - ie->index); - } - List &list = base.get()->as(); - ValueType::IntClass indexVal = index->as(); - if (indexVal >= list.size()) - { - throw EvaluatorError( - u8"IndexOutOfRangeError", - std::format("Index {} out of list `{}` range", indexVal, base.get()->toString().toBasicString()), - ie->index); - } - return LvObject(base.get(), indexVal, LvObject::Kind::ListElement, ctx); - } - else if (type == ValueType::Map) { return LvObject(base.get(), index, LvObject::Kind::MapElement, ctx); } - else if (type == ValueType::String) - { - if (index->getTypeInfo() != ValueType::Int) - { - throw EvaluatorError( - u8"TypeError", - std::format("Type `String` indices must be `Int`, got '{}'", prettyType(index).toBasicString()), - ie->index); - } - FString &string = base.get()->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()), - ie->index); - } - return LvObject(base.get(), indexVal, LvObject::Kind::StringElement, ctx); - } - else - { - throw EvaluatorError( - u8"NoSubscriptableError", - std::format("`{}` object is not subscriptable", base.declaredType().toString().toBasicString()), - ie->base); - } - } - LvObject Evaluator::evalLv(Ast::Expression exp, ContextPtr ctx) - { - using Ast::Operator; - using Ast::AstType; - switch (exp->getType()) - { - 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: { - throw EvaluatorError( - u8"TypeError", - std::format("Expression '{}' doesn't refer to a lvalue", exp->typeName().toBasicString()), - exp); - } - } - } - - RvObject Evaluator::evalInitExpr(Ast::InitExpr initExpr, ContextPtr ctx) - { - LvObject structeLv = evalLv(initExpr->structe, ctx); - ObjectPtr structTypeVal = structeLv.get(); - const FString &structName = structeLv.name(); - if (!structTypeVal->is()) - { - throw EvaluatorError(u8"NotAStructTypeError", - std::format("'{}' is not a structure type", structName.toBasicString()), - initExpr); - } - const StructType &structT = structTypeVal->as(); - - if (structT.builtin) - { - const TypeInfo &type = structT.type; - auto &args = initExpr->args; - size_t argSize = args.size(); - - if (argSize > 1) - { - throw EvaluatorError(u8"StructInitArgumentMismatchError", - std::format("Builtin class `{}` expects 0 or 1 argument, but {} were provided", - type.toString().toBasicString(), - argSize), - initExpr); - } - - // default value - if (argSize == 0) - { - if (type == ValueType::Any || type == ValueType::Null || type == ValueType::Function) - { - throw EvaluatorError( - u8"BuiltinNotConstructibleError", - std::format("Builtin type `{}` cannot be constructed", type.toString().toBasicString()), - initExpr); - } - return std::make_shared(Object::defaultValue(type)); - } - - ObjectPtr val = eval(args[0].second, ctx); - - auto err = [&](const char *msg) { - throw EvaluatorError(u8"BuiltinInitTypeMismatchError", - std::format("Builtin `{}` constructor {}", type.toString().toBasicString(), msg), - initExpr); - }; - - // ===================== Int ===================== - if (type == ValueType::Int) - { - if (!val->is()) err("expects Int"); - return std::make_shared(val->as()); - } - - // ===================== Double ===================== - if (type == ValueType::Double) - { - if (!val->is()) err("expects Double"); - return std::make_shared(val->as()); - } - - // ===================== Bool ===================== - if (type == ValueType::Bool) - { - if (!val->is()) err("expects Bool"); - return std::make_shared(val->as()); - } - - // ===================== String ===================== - if (type == ValueType::String) - { - if (!val->is()) err("expects String"); - return std::make_shared(val->as()); - } - - // ===================== Null ===================== - if (type == ValueType::Null) - { - // Null basically ignores input but keep invariant strict: - if (!val->is()) err("expects Null"); - return Object::getNullInstance(); - } - - // ===================== List ===================== - if (type == ValueType::List) - { - if (!val->is()) err("expects List"); - - const auto &src = val->as(); - auto copied = std::make_shared(List{}); - - auto &dst = copied->as(); - dst.reserve(src.size()); - for (auto &e : src) dst.push_back(e); // shallow element copy, but new container - - return copied; - } - - // ===================== Map ===================== - if (type == ValueType::Map) - { - if (!val->is()) err("expects Map"); - - const auto &src = val->as(); - auto copied = std::make_shared(Map{}); - - auto &dst = copied->as(); - for (auto &[k, v] : src) dst.emplace(k, v); - - return copied; - } - - throw EvaluatorError( - u8"BuiltinNotConstructibleError", - std::format("Builtin type `{}` cannot be constructed", type.toString().toBasicString()), - initExpr); - } - - ContextPtr defContext = structT.defContext; // definition context - // check init args - - size_t minArgs = 0; - size_t maxArgs = structT.fields.size(); - - for (auto &f : structT.fields) - { - if (f.defaultValue == nullptr) minArgs++; - } - - size_t got = initExpr->args.size(); - if (got > maxArgs || got < minArgs) - { - throw EvaluatorError(u8"StructInitArgumentMismatchError", - std::format("Structure '{}' expects {} to {} fields, but {} were provided", - structName.toBasicString(), - minArgs, - maxArgs, - initExpr->args.size()), - initExpr); - } - - std::vector> evaluatedArgs; - - auto evalArguments = [&evaluatedArgs, initExpr, ctx, this]() { - for (const auto &[argName, argExpr] : initExpr->args) - { - evaluatedArgs.push_back({argName, eval(argExpr, ctx)}); - } - }; - - ContextPtr instanceCtx = - std::make_shared(FString(std::format("", structName.toBasicString())), ctx); - /* - 3 ways of calling constructor - .1 Person {"Fig", 1, "IDK"}; - .2 Person {name: "Fig", age: 1, sex: "IDK"}; // can be unordered - .3 Person {name, age, sex}; - */ - { - using enum Ast::InitExprAst::InitMode; - if (initExpr->initMode == Positional) - { - evalArguments(); - - for (size_t i = 0; i < maxArgs; ++i) - { - const Field &field = structT.fields[i]; - const FString &fieldName = field.name; - const TypeInfo &expectedType = field.type; - if (i >= evaluatedArgs.size()) - { - // we've checked argument count before, so here - // must be a default value - - // evaluate default value in definition context - ObjectPtr defaultVal = eval(field.defaultValue, - ctx); // it can't be null here - - // type check - if (!isTypeMatch(expectedType, defaultVal, ctx)) - { - throw EvaluatorError( - u8"StructFieldTypeMismatchError", - std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", - structName.toBasicString(), - fieldName.toBasicString(), - expectedType.toString().toBasicString(), - prettyType(defaultVal).toBasicString()), - initExpr); - } - - instanceCtx->def(fieldName, expectedType, field.am, defaultVal); - continue; - } - - const ObjectPtr &argVal = evaluatedArgs[i].second; - if (!isTypeMatch(expectedType, argVal, ctx)) - { - throw EvaluatorError( - u8"StructFieldTypeMismatchError", - std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", - structName.toBasicString(), - fieldName.toBasicString(), - expectedType.toString().toBasicString(), - prettyType(argVal).toBasicString()), - initExpr); - } - instanceCtx->def(fieldName, expectedType, field.am, argVal); - } - } - else if (initExpr->initMode == Named) - { - evalArguments(); - - // named - for (size_t i = 0; i < maxArgs; ++i) - { - const Field &field = structT.fields[i]; - const FString &fieldName = (field.name.empty() ? evaluatedArgs[i].first : field.name); - if (instanceCtx->containsInThisScope(fieldName)) - { - throw EvaluatorError(u8"StructFieldRedeclarationError", - std::format("Field '{}' already initialized in structure '{}'", - fieldName.toBasicString(), - structName.toBasicString()), - initExpr); - } - if (i + 1 > got) - { - // use default value // - // evaluate default value in definition context - ObjectPtr defaultVal = eval(field.defaultValue, - defContext); // it can't be null here - - // type check - const TypeInfo &expectedType = field.type; - if (!isTypeMatch(expectedType, defaultVal, ctx)) - { - throw EvaluatorError( - u8"StructFieldTypeMismatchError", - std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", - structName.toBasicString(), - fieldName.toBasicString(), - expectedType.toString().toBasicString(), - prettyType(defaultVal).toBasicString()), - initExpr); - } - - instanceCtx->def(fieldName, field.type, field.am, defaultVal); - continue; - } - const ObjectPtr &argVal = evaluatedArgs[i].second; - if (!isTypeMatch(field.type, argVal, ctx)) - { - throw EvaluatorError( - u8"StructFieldTypeMismatchError", - std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", - structName.toBasicString(), - fieldName.toBasicString(), - field.type.toString().toBasicString(), - prettyType(argVal).toBasicString()), - initExpr); - } - instanceCtx->def(fieldName, field.type, field.am, argVal); - } - } - else - { - // shorthand, can be unordered - // in this mode, initExpr args are all VarExpr - // field name is the variable name - for (const auto &[argName, argExpr] : initExpr->args) - { - // assert(argExpr->getType() == Ast::AstType::VarExpr); - // argName is var name - const ObjectPtr &argVal = eval(argExpr, ctx); // get the value - // find field - auto fieldIt = std::find_if( - structT.fields.begin(), - structT.fields.end(), - [&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); - } - const Field &field = *fieldIt; - if (!isTypeMatch(field.type, argVal, ctx)) - { - throw EvaluatorError( - u8"StructFieldTypeMismatchError", - std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", - structName.toBasicString(), - field.name.toBasicString(), - field.type.toString().toBasicString(), - prettyType(argVal).toBasicString()), - initExpr); - } - // field.name is argName (var name) - // Point{x=x, y=y} --> Point{x, y} - - instanceCtx->def(field.name, field.type, field.am, argVal); - } - // fill default values - size_t currentFieldCount = initExpr->args.size(); // we have already check argument count, min <= got <= max - // so remain fields start from currentFieldCount to maxArgs - for (size_t i = currentFieldCount; i < maxArgs; ++i) - { - const Field &field = structT.fields[i]; - - // evaluate default value in definition context - ObjectPtr defaultVal = eval(field.defaultValue, - defContext); // it can't be null here - - // type check - if (!isTypeMatch(field.type, defaultVal, ctx)) - { - throw EvaluatorError( - u8"StructFieldTypeMismatchError", - std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", - structName.toBasicString(), - field.name.toBasicString(), - field.type.toString().toBasicString(), - prettyType(defaultVal).toBasicString()), - initExpr); - } - - instanceCtx->def(field.name, field.type, field.am, defaultVal); - } - } - } - ContextPtr stDefCtx = structT.defContext; - - // load struct method - for (auto &[id, fn] : stDefCtx->getFunctions()) - { - auto funcNameOpt = stDefCtx->getFunctionName(id); - assert(funcNameOpt.has_value()); - - const FString &funcName = *funcNameOpt; - auto funcSlot = stDefCtx->get(funcName); - - instanceCtx->def(funcName, - ValueType::Function, - funcSlot->am, - std::make_shared(Function(fn.paras, fn.retType, fn.body, instanceCtx))); - } - - return std::make_shared(StructInstance(structT.type, instanceCtx)); - } - - RvObject Evaluator::evalBinary(Ast::BinaryExpr bin, ContextPtr ctx) - { - using Ast::Operator; - Operator op = bin->op; - Ast::Expression lexp = bin->lexp, rexp = bin->rexp; - switch (op) - { - case Operator::Add: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - - if (lhs->is() && rhs->is()) - { - ValueType::IntClass result = lhs->as() + rhs->as(); - return IntPool::getInstance().createInt(result); - } - - return std::make_shared(*lhs + *rhs); - } - case Operator::Subtract: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - - if (lhs->is() && rhs->is()) - { - ValueType::IntClass result = lhs->as() - rhs->as(); - return IntPool::getInstance().createInt(result); - } - - return std::make_shared(*lhs - *rhs); - }; - case Operator::Multiply: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - - if (lhs->is() && rhs->is()) - { - ValueType::IntClass result = lhs->as() * rhs->as(); - return IntPool::getInstance().createInt(result); - } - - return std::make_shared((*lhs) * (*rhs)); - }; - case Operator::Divide: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - return std::make_shared(*lhs / *rhs); - }; - case Operator::Modulo: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - - if (lhs->is() && rhs->is()) - { - ValueType::IntClass lv = lhs->as(); - ValueType::IntClass rv = lhs->as(); - if (rv == 0) - { - throw ValueError( - FString( - std::format( - "Modulo by zero: {} % {}", - lv, - rv - ) - ) - ); - } - ValueType::IntClass result = lv / rv; - ValueType::IntClass r = lv % rv; - if (r != 0 && ((lv < 0) != (rv < 0))) { result -= 1; } - return IntPool::getInstance().createInt(result); - } - - return std::make_shared(*lhs % *rhs); - }; - case Operator::Power: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - - if (lhs->is() && rhs->is()) - { - ValueType::IntClass result = std::pow(lhs->as(),rhs->as()); - return IntPool::getInstance().createInt(result); - } - return std::make_shared(power(*lhs, *rhs)); - } - case Operator::And: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - return std::make_shared(*lhs && *rhs); - }; - case Operator::Or: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - return std::make_shared(*lhs || *rhs); - }; - case Operator::Equal: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - return std::make_shared(*lhs == *rhs); - } - case Operator::NotEqual: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - return std::make_shared(*lhs != *rhs); - } - case Operator::Less: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - return std::make_shared(*lhs < *rhs); - } - case Operator::LessEqual: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - return std::make_shared(*lhs <= *rhs); - } - case Operator::Greater: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - return std::make_shared(*lhs > *rhs); - } - case Operator::GreaterEqual: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - return std::make_shared(*lhs >= *rhs); - } - case Operator::Is: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - - const TypeInfo &lhsType = lhs->getTypeInfo(); - const TypeInfo &rhsType = rhs->getTypeInfo(); - - if (lhs->is() && rhs->is()) - { - const StructInstance &si = lhs->as(); - const StructType &st = rhs->as(); - return std::make_shared(si.parentType == st.type); - } - if (lhs->is() && rhs->is()) - { - const StructInstance &si = lhs->as(); - const InterfaceType &it = rhs->as(); - return std::make_shared(implements(si.parentType, it.type, ctx)); - } - - if (ValueType::isTypeBuiltin(lhsType) && rhsType == ValueType::StructType) - { - const StructType &st = rhs->as(); - const TypeInfo &type = st.type; - /* - 如果是内置类型(e.g. Int, String) - 那么 eval出来String这个字,出来的是StructType - 而出来的StructType.type就不会是一个独立的TypeInfo,而是内置的ValueType::String - 依次我们可以判断内置类型 - - e.g: - "123" is String - L OP R - - 其中 L 类型为 String - 而 R 类型为 StructType (builtins.hpp) 中注册 - 拿到 R 的 StructType, 其中的 type 为 String - */ - if (lhs->getTypeInfo() == type) { return Object::getTrueInstance(); } - return Object::getFalseInstance(); - } - - throw EvaluatorError(u8"TypeError", - std::format("Unsupported operator `is` for '{}' && '{}'", - prettyType(lhs).toBasicString(), - prettyType(rhs).toBasicString()), - bin->lexp); - } - - case Operator::BitAnd: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - - if (lhs->is() && rhs->is()) - { - ValueType::IntClass result = lhs->as() & rhs->as(); - return IntPool::getInstance().createInt(result); - } - return std::make_shared(bit_and(*lhs, *rhs)); - } - case Operator::BitOr: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - - if (lhs->is() && rhs->is()) - { - ValueType::IntClass result = lhs->as() | rhs->as(); - return IntPool::getInstance().createInt(result); - } - return std::make_shared(bit_or(*lhs, *rhs)); - } - case Operator::BitXor: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - - if (lhs->is() && rhs->is()) - { - ValueType::IntClass result = lhs->as() ^ rhs->as(); - return IntPool::getInstance().createInt(result); - } - return std::make_shared(bit_xor(*lhs, *rhs)); - } - case Operator::ShiftLeft: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - - if (lhs->is() && rhs->is()) - { - ValueType::IntClass result = lhs->as() << rhs->as(); - return IntPool::getInstance().createInt(result); - } - return std::make_shared(shift_left(*lhs, *rhs)); - } - case Operator::ShiftRight: { - ObjectPtr lhs = eval(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - - if (lhs->is() && rhs->is()) - { - ValueType::IntClass result = lhs->as() >> rhs->as(); - return IntPool::getInstance().createInt(result); - } - return std::make_shared(shift_right(*lhs, *rhs)); - } - - case Operator::Assign: { - LvObject lv = evalLv(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - lv.set(rhs); - return rhs; - } - case Operator::PlusAssign: { - LvObject lv = evalLv(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - lv.set(std::make_shared(*(lv.get()) + *rhs)); - return rhs; - } - case Operator::MinusAssign: { - LvObject lv = evalLv(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - lv.set(std::make_shared(*(lv.get()) - *rhs)); - return rhs; - } - case Operator::AsteriskAssign: { - LvObject lv = evalLv(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - lv.set(std::make_shared(*(lv.get()) * (*rhs))); - return rhs; - } - case Operator::SlashAssign: { - LvObject lv = evalLv(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - lv.set(std::make_shared(*(lv.get()) / *rhs)); - return rhs; - } - case Operator::PercentAssign: { - LvObject lv = evalLv(lexp, ctx); - ObjectPtr rhs = eval(rexp, ctx); - lv.set(std::make_shared(*(lv.get()) / *rhs)); - return rhs; - } - // case Operator::CaretAssign: { - // LvObject lv = evalLv(lexp, ctx); - // ObjectPtr rhs = eval(rexp, ctx); - // lv.set(std::make_shared( - // *(lv.get()) ^ *rhs)); - // } - default: - throw EvaluatorError(u8"UnsupportedOp", - std::format("Unsupport operator '{}' for binary", magic_enum::enum_name(op)), - bin); - } - } - RvObject Evaluator::evalUnary(Ast::UnaryExpr un, ContextPtr ctx) - { - using Ast::Operator; - Operator op = un->op; - Ast::Expression exp = un->exp; - ObjectPtr value = eval(exp, ctx); - switch (op) - { - case Operator::Not: { - return std::make_shared(!(*value)); - } - case Operator::Subtract: { - return std::make_shared(-(*value)); - } - case Operator::BitNot: { - return std::make_shared(bit_not((*value))); - } - default: { - throw EvaluatorError(u8"UnsupportedOpError", - std::format("Unsupported op '{}' for unary expression", magic_enum::enum_name(op)), - un); - } - } - } - RvObject Evaluator::evalTernary(Ast::TernaryExpr te, ContextPtr ctx) - { - RvObject condVal = eval(te->condition, ctx); - if (condVal->getTypeInfo() != ValueType::Bool) - { - throw EvaluatorError( - u8"TypeError", - std::format("Condition must be boolean, got '{}'", prettyType(condVal).toBasicString()), - te->condition); - } - ValueType::BoolClass cond = condVal->as(); - return (cond ? eval(te->valueT, ctx) : eval(te->valueF, ctx)); - } - - RvObject Evaluator::evalFunctionCall(const Function &fn, - const Ast::FunctionArguments &fnArgs, - const FString &fnName, - ContextPtr ctx) - { - const Function &fnStruct = fn; - Ast::FunctionCallArgs evaluatedArgs; - if (fnStruct.isBuiltin) - { - for (const auto &argExpr : fnArgs.argv) { evaluatedArgs.argv.push_back(eval(argExpr, ctx)); } - if (fnStruct.builtinParamCount != -1 && fnStruct.builtinParamCount != evaluatedArgs.getLength()) - { - throw EvaluatorError(u8"BuiltinArgumentMismatchError", - std::format("Builtin function '{}' expects {} arguments, but {} were provided", - fnName.toBasicString(), - fnStruct.builtinParamCount, - evaluatedArgs.getLength()), - fnArgs.argv.back()); - } - return fnStruct.builtin(evaluatedArgs.argv); - } - - // check argument, all types of parameters - Ast::FunctionParameters fnParas = fnStruct.paras; - - // create new context for function call - auto newContext = std::make_shared(FString(std::format("", fnName.toBasicString())), - fnStruct.closureContext); - - if (fnParas.variadic) - goto VariadicFilling; - else - goto NormalFilling; - - NormalFilling: { - if (fnArgs.getLength() < fnParas.posParas.size() || fnArgs.getLength() > fnParas.size()) - { - throw RuntimeError(FString(std::format("Function '{}' expects {} to {} arguments, but {} were provided", - fnName.toBasicString(), - fnParas.posParas.size(), - fnParas.size(), - fnArgs.getLength()))); - } - - // positional parameters type check - size_t i; - for (i = 0; i < fnParas.posParas.size(); i++) - { - TypeInfo expectedType(fnParas.posParas[i].second); // look up type info, if exists a type with the - // name, use it, else throw - ObjectPtr argVal = eval(fnArgs.argv[i], ctx); - TypeInfo actualType = argVal->getTypeInfo(); - if (!isTypeMatch(expectedType, argVal, ctx)) - { - throw EvaluatorError(u8"ArgumentTypeMismatchError", - std::format("In function '{}', argument '{}' expects type '{}', but got type '{}'", - fnName.toBasicString(), - fnParas.posParas[i].first.toBasicString(), - expectedType.toString().toBasicString(), - actualType.toString().toBasicString()), - fnArgs.argv[i]); - } - evaluatedArgs.argv.push_back(argVal); - } - // default parameters type check - for (; i < fnArgs.getLength(); i++) - { - size_t defParamIndex = i - fnParas.posParas.size(); - TypeInfo expectedType(fnParas.defParas[defParamIndex].second.first); - - ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second, ctx); - if (!isTypeMatch(expectedType, defaultVal, ctx)) - { - throw EvaluatorError( - u8"DefaultParameterTypeError", - std::format( - "In function '{}', default parameter '{}' has type '{}', which does not match the expected type '{}'", - fnName.toBasicString(), - fnParas.defParas[defParamIndex].first.toBasicString(), - prettyType(defaultVal).toBasicString(), - expectedType.toString().toBasicString()), - fnArgs.argv[i]); - } - - ObjectPtr argVal = eval(fnArgs.argv[i], ctx); - TypeInfo actualType = argVal->getTypeInfo(); - if (!isTypeMatch(expectedType, argVal, ctx)) - { - throw EvaluatorError(u8"ArgumentTypeMismatchError", - std::format("In function '{}', argument '{}' expects type '{}', but got type '{}'", - fnName.toBasicString(), - fnParas.defParas[defParamIndex].first.toBasicString(), - expectedType.toString().toBasicString(), - actualType.toString().toBasicString()), - fnArgs.argv[i]); - } - evaluatedArgs.argv.push_back(argVal); - } - // default parameters filling - for (; i < fnParas.size(); i++) - { - size_t defParamIndex = i - fnParas.posParas.size(); - ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second, ctx); - evaluatedArgs.argv.push_back(defaultVal); - } - - // define parameters in new context - for (size_t j = 0; j < fnParas.size(); j++) - { - FString paramName; - TypeInfo paramType; - if (j < fnParas.posParas.size()) - { - paramName = fnParas.posParas[j].first; - paramType = TypeInfo(fnParas.posParas[j].second); - } - else - { - size_t defParamIndex = j - fnParas.posParas.size(); - paramName = fnParas.defParas[defParamIndex].first; - paramType = TypeInfo(fnParas.defParas[defParamIndex].second.first); - } - AccessModifier argAm = AccessModifier::Const; - newContext->def(paramName, paramType, argAm, evaluatedArgs.argv[j]); - } - goto ExecuteBody; - } - - VariadicFilling: { - List list; - for (auto &exp : fnArgs.argv) - { - list.push_back(eval(exp, ctx)); // eval arguments in current scope - } - newContext->def(fnParas.variadicPara, ValueType::List, AccessModifier::Const, std::make_shared(list)); - goto ExecuteBody; - } - - ExecuteBody: { - // execute function body - ObjectPtr retVal = Object::getNullInstance(); - for (const auto &stmt : fnStruct.body->stmts) - { - StatementResult sr = evalStatement(stmt, newContext); - if (sr.isError()) - { - throw EvaluatorError(u8"UncaughtExceptionError", - std::format("Uncaught exception: {}", sr.result->toString().toBasicString()), - stmt); - } - if (!sr.isNormal()) - { - retVal = sr.result; - break; - } - } - if (!isTypeMatch(fnStruct.retType, retVal, ctx)) - { - throw EvaluatorError(u8"ReturnTypeMismatchError", - std::format("Function '{}' expects return type '{}', but got type '{}'", - fnName.toBasicString(), - fnStruct.retType.toString().toBasicString(), - prettyType(retVal).toBasicString()), - fnStruct.body); - } - return retVal; - } - } - - RvObject Evaluator::eval(Ast::Expression exp, ContextPtr ctx) - { - using Ast::AstType; - AstType type = exp->getType(); - switch (type) - { - 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: - case AstType::IndexExpr: return evalLv(exp, ctx).get(); - - 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) - { - throw EvaluatorError(u8"ObjectNotCallable", - std::format("Object `{}` isn't callable", fnObj->toString().toBasicString()), - callee); - } - const Function &fn = fnObj->as(); - size_t fnId = fn.id; - // const auto &fnNameOpt = ctx->getFunctionName(fnId); - // const FString &fnName = (fnNameOpt ? *fnNameOpt : - // u8""); - - auto fnNameOpt = ctx->getFunctionName(fnId); - if (!fnNameOpt && fn.closureContext) fnNameOpt = fn.closureContext->getFunctionName(fnId); - - const FString &fnName = (fnNameOpt ? *fnNameOpt : u8""); - - 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); - st->setAAI(aai); - - body = std::make_shared(); - body->stmts.push_back(st); // convert to Ast::Statement - body->setAAI(aai); - } - else - { - body = fnLiteral->getBlockBody(); - assert(body != nullptr); - } - Function fn(fnLiteral->paras, ValueType::Any, body, ctx - /* - pass the ctx(fnLiteral eval context) as closure context - */ - ); - return std::make_shared(std::move(fn)); - } - 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)); } - return std::make_shared(std::move(list)); - } - - 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); } - return std::make_shared(std::move(map)); - } - - default: { - throw RuntimeError(FString(std::format("err type of expr: {}", magic_enum::enum_name(type)))); - } - } - return Object::getNullInstance(); // ignore warning - } StatementResult Evaluator::evalBlockStatement(Ast::BlockStatement block, ContextPtr ctx) { StatementResult sr = StatementResult::normal(); @@ -1198,616 +31,6 @@ namespace Fig } return sr; } - StatementResult Evaluator::evalStatement(Ast::Statement stmt, ContextPtr ctx) - { - using enum Ast::AstType; - switch (stmt->getType()) - { - 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)) - { - throw EvaluatorError( - u8"RedeclarationError", - std::format("Variable `{}` already declared in this scope", varDef->name.toBasicString()), - varDef); - } - - RvObject value = nullptr; - if (varDef->expr) { value = eval(varDef->expr, ctx); } - - TypeInfo declaredType; // default is Any - const Ast::Expression &declaredTypeExp = varDef->declaredType; - - if (varDef->followupType) { declaredType = actualType(value); } - else if (declaredTypeExp) - { - ObjectPtr declaredTypeValue = eval(declaredTypeExp, ctx); - declaredType = actualType(declaredTypeValue); - - if (value != nullptr && !isTypeMatch(declaredType, value, ctx)) - { - throw EvaluatorError(u8"TypeError", - std::format("Variable `{}` expects init-value type `{}`, but got '{}'", - varDef->name.toBasicString(), - prettyType(declaredTypeValue).toBasicString(), - prettyType(value).toBasicString()), - varDef->expr); - } - else if (value == nullptr) - { - value = std::make_shared(Object::defaultValue(declaredType)); - } // else -> Ok - } // else -> type is Any (default) - AccessModifier am = - (varDef->isConst ? (varDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const) : - (varDef->isPublic ? AccessModifier::Public : AccessModifier::Normal)); - ctx->def(varDef->name, declaredType, am, value); - return StatementResult::normal(); - } - - case FunctionDefSt: { - auto fnDef = std::static_pointer_cast(stmt); - assert(fnDef != nullptr); - - const FString &fnName = fnDef->name; - if (ctx->containsInThisScope(fnName)) - { - throw EvaluatorError( - u8"RedeclarationError", - std::format("Function `{}` already declared in this scope", fnName.toBasicString()), - fnDef); - } - TypeInfo returnType = ValueType::Any; - if (fnDef->retType) - { - ObjectPtr returnTypeValue = eval(fnDef->retType, ctx); - returnType = actualType(returnTypeValue); - } - - Function fn(fnDef->paras, returnType, fnDef->body, ctx); - ctx->def(fnName, - ValueType::Function, - (fnDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const), - std::make_shared(fn)); - return StatementResult::normal(); - } - - case StructSt: { - auto stDef = std::static_pointer_cast(stmt); - assert(stDef != nullptr); - - if (ctx->containsInThisScope(stDef->name)) - { - throw EvaluatorError( - u8"RedeclarationError", - std::format("Structure '{}' already defined in this scope", stDef->name.toBasicString()), - stDef); - } - std::vector fields; - std::vector _fieldNames; - for (Ast::StructDefField field : stDef->fields) - { - if (Utils::vectorContains(field.fieldName, _fieldNames)) - { - throw EvaluatorError(u8"RedeclarationError", - std::format("Field '{}' already defined in structure '{}'", - field.fieldName.toBasicString(), - stDef->name.toBasicString()), - stDef); - } - TypeInfo fieldType = ValueType::Any; - if (field.declaredType) - { - ObjectPtr declaredTypeValue = eval(field.declaredType, ctx); - fieldType = actualType(declaredTypeValue); - } - - fields.push_back(Field(field.am, field.fieldName, fieldType, field.defaultValueExpr)); - } - ContextPtr defContext = std::make_shared(FString(std::format("", - stDef->name.toBasicString(), - stDef->getAAI().line, - stDef->getAAI().column)), - ctx); - const Ast::BlockStatement &body = stDef->body; - for (auto &st : body->stmts) - { - if (st->getType() != Ast::AstType::FunctionDefSt) - { - throw EvaluatorError(u8"UnexpectedStatementInStructError", - std::format("Unexpected statement `{}` in struct declaration", - st->toString().toBasicString()), - st); - } - evalStatement(st, defContext); // function def st - } - - AccessModifier am = (stDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const); - TypeInfo type(stDef->name, true); // register type name - ctx->def(stDef->name, - ValueType::StructType, - am, - std::make_shared(StructType(type, defContext, fields))); - return StatementResult::normal(); - } - - case InterfaceDefSt: { - auto ifd = std::static_pointer_cast(stmt); - assert(ifd != nullptr); - - const FString &interfaceName = ifd->name; - - if (ctx->containsInThisScope(interfaceName)) - { - throw EvaluatorError( - u8"RedeclarationError", - std::format("Interface `{}` already declared in this scope", interfaceName.toBasicString()), - ifd); - } - TypeInfo type(interfaceName, true); // register interface - ctx->def(interfaceName, - type, - (ifd->isPublic ? AccessModifier::PublicConst : AccessModifier::Const), - std::make_shared(InterfaceType(type, ifd->methods))); - return StatementResult::normal(); - } - - case ImplementSt: { - auto ip = std::static_pointer_cast(stmt); - assert(ip != nullptr); - - TypeInfo structType(ip->structName); - TypeInfo interfaceType(ip->interfaceName); - if (ctx->hasImplRegisted(structType, interfaceType)) - { - throw EvaluatorError(u8"DuplicateImplError", - std::format("Duplicate implement `{}` for `{}`", - interfaceType.toString().toBasicString(), - structType.toString().toBasicString()), - ip); - } - if (!ctx->contains(ip->interfaceName)) - { - throw EvaluatorError(u8"InterfaceNotFoundError", - std::format("Interface '{}' not found", ip->interfaceName.toBasicString()), - ip); - } - if (!ctx->contains(ip->structName)) - { - throw EvaluatorError(u8"StructNotFoundError", - std::format("Struct '{}' not found", ip->structName.toBasicString()), - ip); - } - auto interfaceSlot = ctx->get(ip->interfaceName); - auto structSlot = ctx->get(ip->structName); - - LvObject interfaceLv(interfaceSlot, ctx); - LvObject structLv(structSlot, ctx); - - ObjectPtr interfaceObj = interfaceLv.get(); - ObjectPtr structTypeObj = structLv.get(); - - if (!interfaceObj->is()) - { - throw EvaluatorError( - u8"NotAInterfaceError", - std::format("Variable `{}` is not a interface", ip->interfaceName.toBasicString()), - ip); - } - if (!structTypeObj->is()) - { - throw EvaluatorError( - u8"NotAStructType", - std::format("Variable `{}` is not a struct type", ip->structName.toBasicString()), - ip); - } - auto &implementMethods = ip->methods; - InterfaceType &interface = interfaceObj->as(); - - // ===== interface implementation validation ===== - ImplRecord record{interfaceType, structType, {}}; - - std::unordered_map ifaceMethods; - for (auto &m : interface.methods) - { - if (ifaceMethods.contains(m.name)) - { - throw EvaluatorError(u8"InterfaceDuplicateMethodError", - std::format("Interface '{}' has duplicate method '{}'", - interfaceType.toString().toBasicString(), - m.name.toBasicString()), - ip); - } - ifaceMethods[m.name] = m; - } - - std::unordered_set implemented; - - for (auto &implMethod : implementMethods) - { - const FString &name = implMethod.name; - - // ---- redundant impl ---- - if (!ifaceMethods.contains(name)) - { - throw EvaluatorError(u8"RedundantImplementationError", - std::format("Struct '{}' implements extra method '{}' " - "which is not required by interface '{}'", - structType.toString().toBasicString(), - name.toBasicString(), - interfaceType.toString().toBasicString()), - ip); - } - - if (implemented.contains(name)) - { - throw EvaluatorError(u8"DuplicateImplementMethodError", - std::format("Duplicate implement method '{}'", name.toBasicString()), - ip); - } - - auto &ifMethod = ifaceMethods[name]; - - // ---- signature check ---- - if (!isInterfaceSignatureMatch(implMethod, ifMethod)) - { - throw EvaluatorError(u8"InterfaceSignatureMismatch", - std::format("Interface method '{}({})' signature mismatch with " - "implementation '{}({})'", - ifMethod.name.toBasicString(), - ifMethod.paras.toString().toBasicString(), - implMethod.name.toBasicString(), - implMethod.paras.toString().toBasicString()), - ip); - } - - if (ctx->hasMethodImplemented(structType, name)) - { - throw EvaluatorError(u8"DuplicateImplementMethodError", - std::format("Method '{}' already implemented by another interface " - "for struct '{}'", - name.toBasicString(), - structType.toString().toBasicString()), - ip); - } - - implemented.insert(name); - - ObjectPtr returnTypeValue = eval(ifMethod.returnType, ctx); - - record.implMethods[name] = - Function(implMethod.paras, actualType(returnTypeValue), implMethod.body, ctx); - } - - for (auto &m : interface.methods) - { - if (implemented.contains(m.name)) continue; - - if (m.hasDefaultBody()) continue; - - throw EvaluatorError(u8"MissingImplementationError", - std::format("Struct '{}' does not implement required interface method '{}' " - "and interface '{}' provides no default implementation", - structType.toString().toBasicString(), - m.name.toBasicString(), - interfaceType.toString().toBasicString()), - ip); - } - - ctx->setImplRecord(structType, interfaceType, record); - return StatementResult::normal(); - } - - case IfSt: { - auto ifSt = std::static_pointer_cast(stmt); - ObjectPtr condVal = eval(ifSt->condition, ctx); - if (condVal->getTypeInfo() != ValueType::Bool) - { - throw EvaluatorError( - u8"TypeError", - std::format("Condition must be boolean, but got '{}'", prettyType(condVal).toBasicString()), - ifSt->condition); - } - if (condVal->as()) { return evalBlockStatement(ifSt->body, ctx); } - // else - for (const auto &elif : ifSt->elifs) - { - ObjectPtr elifCondVal = eval(elif->condition, ctx); - if (elifCondVal->getTypeInfo() != ValueType::Bool) - { - throw EvaluatorError( - u8"TypeError", - std::format("Condition must be boolean, but got '{}'", prettyType(condVal).toBasicString()), - ifSt->condition); - } - if (elifCondVal->as()) { return evalBlockStatement(elif->body, ctx); } - } - if (ifSt->els) { return evalBlockStatement(ifSt->els->body, ctx); } - return StatementResult::normal(); - }; - case WhileSt: { - auto whileSt = std::static_pointer_cast(stmt); - while (true) - { - ObjectPtr condVal = eval(whileSt->condition, ctx); - if (condVal->getTypeInfo() != ValueType::Bool) - { - throw EvaluatorError( - u8"TypeError", - std::format("Condition must be boolean, but got '{}'", prettyType(condVal).toBasicString()), - whileSt->condition); - } - if (!condVal->as()) { break; } - ContextPtr loopContext = std::make_shared( - FString(std::format("", whileSt->getAAI().line, whileSt->getAAI().column)), - ctx); // every loop has its own context - StatementResult sr = evalBlockStatement(whileSt->body, loopContext); - if (sr.shouldReturn()) { return sr; } - if (sr.shouldBreak()) { break; } - if (sr.shouldContinue()) { continue; } - } - return StatementResult::normal(); - }; - case ForSt: { - auto forSt = std::static_pointer_cast(stmt); - ContextPtr loopContext = std::make_shared( - FString(std::format("", forSt->getAAI().line, forSt->getAAI().column)), - ctx); // for loop has its own context - - evalStatement(forSt->initSt, - loopContext); // ignore init statement result - size_t iteration = 0; - - while (true) // use while loop to simulate for loop, cause we - // need to check condition type every iteration - { - ObjectPtr condVal = eval(forSt->condition, loopContext); - if (condVal->getTypeInfo() != ValueType::Bool) - { - throw EvaluatorError( - u8"TypeError", - std::format("Condition must be boolean, but got '{}'", prettyType(condVal).toBasicString()), - forSt->condition); - } - if (!condVal->as()) { break; } - iteration++; - ContextPtr iterationContext = std::make_shared( - FString(std::format( - "", forSt->getAAI().line, forSt->getAAI().column, iteration)), - loopContext); // every loop has its own context - StatementResult sr = evalBlockStatement(forSt->body, iterationContext); - if (sr.shouldReturn()) { return sr; } - if (sr.shouldBreak()) { break; } - if (sr.shouldContinue()) - { - // continue to next iteration - continue; - } - evalStatement(forSt->incrementSt, - loopContext); // ignore increment statement result - } - return StatementResult::normal(); - } - - 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); - StatementResult sr = StatementResult::normal(); - for (auto &stmt : tryst->body->stmts) - { - sr = evalStatement(stmt, tryCtx); // eval in try context - if (sr.isError()) { break; } - } - bool catched = false; - for (auto &cat : tryst->catches) - { - const FString &errVarName = cat.errVarName; - TypeInfo errVarType = (cat.hasType ? TypeInfo(cat.errVarType) : ValueType::Any); - if (isTypeMatch(errVarType, sr.result, ctx)) - { - ContextPtr catchCtx = std::make_shared( - FString( - std::format("", cat.body->getAAI().line, cat.body->getAAI().column)), - ctx); - catchCtx->def(errVarName, errVarType, AccessModifier::Normal, sr.result); - sr = evalBlockStatement(cat.body, catchCtx); - catched = true; - break; - } - } - if (!catched) - { - throw EvaluatorError(u8"UncaughtExceptionError", - std::format("Uncaught exception: {}", sr.result->toString().toBasicString()), - tryst); - } - if (tryst->finallyBlock) { sr = evalBlockStatement(tryst->finallyBlock, ctx); } - return sr; - } - - case ThrowSt: { - auto ts = std::static_pointer_cast(stmt); - assert(ts != nullptr); - - ObjectPtr value = eval(ts->value, ctx); - if (value->is()) - { - throw EvaluatorError(u8"TypeError", u8"Why did you throw a null?", ts); - } - return StatementResult::errorFlow(value); - } - - 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); - return StatementResult::returnFlow(returnValue); - } - - case BreakSt: { - if (!ctx->parent) - { - throw EvaluatorError(u8"BreakOutsideLoopError", u8"`break` statement outside loop", stmt); - } - if (!ctx->isInLoopContext()) - { - throw EvaluatorError(u8"BreakOutsideLoopError", u8"`break` statement outside loop", stmt); - } - return StatementResult::breakFlow(); - } - - case ContinueSt: { - if (!ctx->parent) - { - throw EvaluatorError(u8"ContinueOutsideLoopError", u8"`continue` statement outside loop", stmt); - } - if (!ctx->isInLoopContext()) - { - throw EvaluatorError(u8"ContinueOutsideLoopError", u8"`continue` statement outside loop", stmt); - } - return StatementResult::continueFlow(); - } - - 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); - return evalBlockStatement(block, blockCtx); - } - - default: - throw RuntimeError( - FString(std::format("Feature stmt {} unsupported yet", magic_enum::enum_name(stmt->getType())))); - } - } - - std::filesystem::path Evaluator::resolveModulePath(const std::vector &pathVec) - { - namespace fs = std::filesystem; - - static const std::vector defaultLibraryPath{"Library", "Library/fpm"}; - - std::vector pathToFind(defaultLibraryPath); - - fs::path interpreterPath = getExecutablePath().parent_path(); - - for (fs::path &p : pathToFind) - { - p = interpreterPath / p; // 相对路径 -> 绝对路径 - } - - pathToFind.insert( - pathToFind.begin(), - fs::path(this->sourcePath.toBasicString()).parent_path()); // first search module at the source file path - - fs::path path; - - /* - Example: - import comp.config; - */ - - const FString &modPathStrTop = pathVec.at(0); - fs::path modPath; - - bool found = false; - for (auto &parentFolder : pathToFind) - { - modPath = parentFolder / FString(modPathStrTop + u8".fig").toBasicString(); - if (fs::exists(modPath)) - { - path = modPath; - found = true; - break; - } - else - { - modPath = parentFolder / modPathStrTop.toBasicString(); - if (fs::is_directory(modPath)) // comp is a directory - { - modPath = modPath / FString(modPathStrTop + u8".fig").toBasicString(); - /* - if module name is a directory, we require [module - name].fig at the directory - */ - if (!fs::exists(modPath)) - { - throw RuntimeError(FString(std::format("requires module file, {}\\{}", - modPathStrTop.toBasicString(), - FString(modPathStrTop + u8".fig").toBasicString()))); - } - found = true; - path = modPath; - break; - } - } - } - - if (!found) - throw RuntimeError(FString(std::format("Could not find module `{}`", modPathStrTop.toBasicString()))); - - bool found2 = false; - - for (size_t i = 1; i < pathVec.size(); ++i) // has next module - { - const FString &next = pathVec.at(i); - modPath = modPath.parent_path(); // get the folder - modPath = modPath / FString(next + u8".fig").toBasicString(); - if (fs::exists(modPath)) - { - if (i != pathVec.size() - 1) - throw RuntimeError(FString(std::format( - "expects {} as parent directory and find next module, but got a file", next.toBasicString()))); - // it's the last module - found2 = true; - path = modPath; - break; - } - // `next` is a folder - modPath = modPath.parent_path() / next.toBasicString(); - if (!fs::exists(modPath)) - throw RuntimeError(FString(std::format("Could not find module `{}`", next.toBasicString()))); - if (i == pathVec.size() - 1) - { - // `next` is the last module - modPath = modPath / FString(next + u8".fig").toBasicString(); - if (!fs::exists(modPath)) - { - throw RuntimeError(FString(std::format( - "expects {} as parent directory and find next module, but got a file", next.toBasicString()))); - } - found2 = true; - path = modPath; - } - } - - if (!found2 && !fs::exists(modPath)) - throw RuntimeError(FString(std::format("Could not find module `{}`", pathVec.end()->toBasicString()))); - - return path; - } ContextPtr Evaluator::loadModule(const std::filesystem::path &path) { @@ -1852,6 +75,8 @@ namespace Fig auto path = resolveModulePath(pathVec); ContextPtr modCtx = loadModule(path); + ctx->getImplRegistry().insert(modCtx->getImplRegistry().begin(), modCtx->getImplRegistry().end()); // load impl + // std::cerr << modName.toBasicString() << '\n'; DEBUG if (ctx->containsInThisScope(modName)) diff --git a/src/Evaluator/evaluator.hpp b/src/Evaluator/evaluator.hpp index b6c6172..835554a 100644 --- a/src/Evaluator/evaluator.hpp +++ b/src/Evaluator/evaluator.hpp @@ -1,62 +1,20 @@ -#include "Ast/Expressions/InitExpr.hpp" +#pragma once +#include #include #include -#include +#include #include -#include +#include #include #include -#include +#include #include +#include + namespace Fig { - struct StatementResult - { - ObjectPtr result; - enum class Flow - { - Normal, - Return, - Break, - Continue, - Error - } flow; - - StatementResult(ObjectPtr val, Flow f = Flow::Normal) : - result(val), flow(f) - { - } - - static StatementResult normal(ObjectPtr val = Object::getNullInstance()) - { - return StatementResult(val, Flow::Normal); - } - static StatementResult returnFlow(ObjectPtr val) - { - return StatementResult(val, Flow::Return); - } - static StatementResult breakFlow() - { - return StatementResult(Object::getNullInstance(), Flow::Break); - } - static StatementResult continueFlow() - { - return StatementResult(Object::getNullInstance(), Flow::Continue); - } - static StatementResult errorFlow(ObjectPtr val) - { - return StatementResult(val, Flow::Error); - } - - bool isNormal() const { return flow == Flow::Normal; } - bool shouldReturn() const { return flow == Flow::Return; } - bool shouldBreak() const { return flow == Flow::Break; } - bool shouldContinue() const { return flow == Flow::Continue; } - bool isError() const { return flow == Flow::Error; } - }; - class Evaluator { private: @@ -92,7 +50,7 @@ namespace Fig { assert(global != nullptr); - for (auto &[name, fn] : Builtins::builtinFunctions) + for (auto &[name, fn] : Builtins::getBuiltinFunctions()) { int argc = Builtins::getBuiltinFunctionParamCount(name); Function f(fn, argc); @@ -108,7 +66,7 @@ namespace Fig { assert(global != nullptr); - for (auto &[name, val] : Builtins::builtinValues) + for (auto &[name, val] : Builtins::getBuiltinValues()) { global->def( name, 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/Module/builtins.cpp b/src/Module/builtins.cpp new file mode 100644 index 0000000..b87b904 --- /dev/null +++ b/src/Module/builtins.cpp @@ -0,0 +1,380 @@ +#include + +#include +#include +#include +#include +#include + +namespace Fig::Builtins +{ + const std::unordered_map &getBuiltinValues() + { + static const std::unordered_map builtinValues = { + {u8"null", Object::getNullInstance()}, + {u8"true", Object::getTrueInstance()}, + {u8"false", Object::getFalseInstance()}, + {u8"Error", + std::make_shared(InterfaceType(getErrorInterfaceTypeInfo(), + {Ast::InterfaceMethod(u8"toString", + Ast::FunctionParameters({}, {}), + std::make_shared(u8"String"), + nullptr), + Ast::InterfaceMethod(u8"getErrorClass", + Ast::FunctionParameters({}, {}), + std::make_shared(u8"String"), + nullptr), + Ast::InterfaceMethod(u8"getErrorMessage", + Ast::FunctionParameters({}, {}), + std::make_shared(u8"String"), + nullptr)}))}, + + {u8"Any", std::make_shared(StructType(ValueType::Any, nullptr, {}, true))}, + {u8"Int", std::make_shared(StructType(ValueType::Int, nullptr, {}, true))}, + {u8"Null", std::make_shared(StructType(ValueType::Null, nullptr, {}, true))}, + {u8"String", std::make_shared(StructType(ValueType::String, nullptr, {}, true))}, + {u8"Bool", std::make_shared(StructType(ValueType::Bool, nullptr, {}, true))}, + {u8"Double", std::make_shared(StructType(ValueType::Double, nullptr, {}, true))}, + {u8"Function", std::make_shared(StructType(ValueType::Function, nullptr, {}, true))}, + {u8"List", std::make_shared(StructType(ValueType::List, nullptr, {}, true))}, + {u8"Map", std::make_shared(StructType(ValueType::Map, nullptr, {}, true))}, + // Type `StructType` `StructInstance` `Module` `InterfaceType` + // Not allowed to call constructor! + }; + return builtinValues; + } + const std::unordered_map &getBuiltinFunctionArgCounts() + { + static const std::unordered_map builtinFunctionArgCounts = { + {u8"__fstdout_print", -1}, // variadic + {u8"__fstdout_println", -1}, // variadic + {u8"__fstdin_read", 0}, + {u8"__fstdin_readln", 0}, + {u8"__fvalue_type", 1}, + {u8"__fvalue_int_parse", 1}, + {u8"__fvalue_int_from", 1}, + {u8"__fvalue_double_parse", 1}, + {u8"__fvalue_double_from", 1}, + {u8"__fvalue_string_from", 1}, + {u8"__ftime_now_ns", 0}, + /* math start */ + {u8"__fmath_acos", 1}, + {u8"__fmath_acosh", 1}, + {u8"__fmath_asin", 1}, + {u8"__fmath_asinh", 1}, + {u8"__fmath_atan", 1}, + {u8"__fmath_atan2", 2}, + {u8"__fmath_atanh", 1}, + {u8"__fmath_ceil", 1}, + {u8"__fmath_cos", 1}, + {u8"__fmath_cosh", 1}, + {u8"__fmath_exp", 1}, + {u8"__fmath_expm1", 1}, + {u8"__fmath_fabs", 1}, + {u8"__fmath_floor", 1}, + {u8"__fmath_fmod", 2}, + {u8"__fmath_frexp", 1}, + {u8"__fmath_gcd", 2}, + {u8"__fmath_hypot", 2}, + {u8"__fmath_isequal", 2}, + {u8"__fmath_log", 1}, + {u8"__fmath_log10", 1}, + {u8"__fmath_log1p", 1}, + {u8"__fmath_log2", 1}, + {u8"__fmath_sin", 1}, + {u8"__fmath_sinh", 1}, + {u8"__fmath_sqrt", 1}, + {u8"__fmath_tan", 1}, + {u8"__fmath_tanh", 1}, + {u8"__fmath_trunc", 1}, + }; + return builtinFunctionArgCounts; + } + const std::unordered_map &getBuiltinFunctions() + { + static const std::unordered_map builtinFunctions{ + {u8"__fstdout_print", + [](const std::vector &args) -> ObjectPtr { + for (auto arg : args) { std::print("{}", arg->toStringIO().toBasicString()); } + return std::make_shared(ValueType::IntClass(args.size())); + }}, + {u8"__fstdout_println", + [](const std::vector &args) -> ObjectPtr { + for (auto arg : args) { std::print("{}", arg->toStringIO().toBasicString()); } + std::print("\n"); + return std::make_shared(ValueType::IntClass(args.size())); + }}, + {u8"__fstdin_read", + [](const std::vector &args) -> ObjectPtr { + std::string input; + std::cin >> input; + return std::make_shared(FString::fromBasicString(input)); + }}, + {u8"__fstdin_readln", + [](const std::vector &args) -> ObjectPtr { + std::string line; + std::getline(std::cin, line); + return std::make_shared(FString::fromBasicString(line)); + }}, + {u8"__fvalue_type", + [](const std::vector &args) -> ObjectPtr { return std::make_shared(); }}, + {u8"__fvalue_int_parse", + [](const std::vector &args) -> ObjectPtr { + FString str = args[0]->as(); + try + { + ValueType::IntClass val = std::stoi(str.toBasicString()); + return std::make_shared(val); + } + catch (...) + { + throw RuntimeError(FString(std::format("Invalid int string for parsing", str.toBasicString()))); + } + }}, + {u8"__fvalue_int_from", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + if (val->is()) + { + return std::make_shared( + static_cast(val->as())); + } + else if (val->is()) + { + return std::make_shared( + static_cast(val->as() ? 1 : 0)); + } + else + { + throw RuntimeError(FString(std::format("Type '{}' cannot be converted to int", + val->getTypeInfo().toString().toBasicString()))); + } + }}, + {u8"__fvalue_double_parse", + [](const std::vector &args) -> ObjectPtr { + FString str = args[0]->as(); + try + { + ValueType::DoubleClass val = std::stod(str.toBasicString()); + return std::make_shared(ValueType::DoubleClass(val)); + } + catch (...) + { + throw RuntimeError(FString(std::format("Invalid double string for parsing", str.toBasicString()))); + } + }}, + {u8"__fvalue_double_from", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + if (val->is()) + { + return std::make_shared( + static_cast(val->as())); + } + else if (val->is()) + { + return std::make_shared( + ValueType::DoubleClass(val->as() ? 1.0 : 0.0)); + } + else + { + throw RuntimeError(FString(std::format("Type '{}' cannot be converted to double", + val->getTypeInfo().toString().toBasicString()))); + } + }}, + {u8"__fvalue_string_from", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + return std::make_shared(val->toStringIO()); + }}, + {u8"__ftime_now_ns", + [](const std::vector &args) -> ObjectPtr { + // returns nanoseconds + using namespace Fig::Time; + auto now = Clock::now(); + return std::make_shared(static_cast( + std::chrono::duration_cast(now - start_time).count())); + }}, + + /* math start */ + {u8"__fmath_acos", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(acos(d)); + }}, + {u8"__fmath_acosh", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(acosh(d)); + }}, + {u8"__fmath_asin", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(asin(d)); + }}, + {u8"__fmath_asinh", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(asinh(d)); + }}, + {u8"__fmath_atan", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(atan(d)); + }}, + {u8"__fmath_atan2", + [](const std::vector &args) -> ObjectPtr { + ValueType::DoubleClass y = args[0]->getNumericValue(); + ValueType::DoubleClass x = args[1]->getNumericValue(); + return std::make_shared(atan2(y, x)); + }}, + {u8"__fmath_atanh", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(atanh(d)); + }}, + {u8"__fmath_ceil", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(ceil(d)); + }}, + {u8"__fmath_cos", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(cos(d)); + }}, + {u8"__fmath_cosh", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(cosh(d)); + }}, + {u8"__fmath_exp", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(exp(d)); + }}, + {u8"__fmath_expm1", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(expm1(d)); + }}, + {u8"__fmath_fabs", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(fabs(d)); + }}, + {u8"__fmath_floor", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(floor(d)); + }}, + {u8"__fmath_fmod", + [](const std::vector &args) -> ObjectPtr { + ValueType::DoubleClass x = args[0]->getNumericValue(); + ValueType::DoubleClass y = args[1]->getNumericValue(); + return std::make_shared(fmod(x, y)); + }}, + {u8"__fmath_frexp", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + int e; + return std::make_shared(List({std::make_shared(frexp(d, &e)), + std::make_shared(static_cast(e))})); + }}, + {u8"__fmath_gcd", + [](const std::vector &args) -> ObjectPtr { + ValueType::IntClass x = args[0]->as(); + ValueType::IntClass y = args[1]->as(); + return std::make_shared(std::gcd(x, y)); + }}, + {u8"__fmath_hypot", + [](const std::vector &args) -> ObjectPtr { + ValueType::DoubleClass x = args[0]->getNumericValue(); + ValueType::DoubleClass y = args[1]->getNumericValue(); + return std::make_shared(hypot(x, y)); + }}, + {u8"__fmath_isequal", + [](const std::vector &args) -> ObjectPtr { + ValueType::DoubleClass x = args[0]->getNumericValue(); + ValueType::DoubleClass y = args[1]->getNumericValue(); + static const double epsilon = 1e-9; + return std::make_shared(fabs(x - y) < epsilon); + }}, + {u8"__fmath_log", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(log(d)); + }}, + {u8"__fmath_log10", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(log10(d)); + }}, + {u8"__fmath_log1p", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(log1p(d)); + }}, + {u8"__fmath_log2", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(log2(d)); + }}, + {u8"__fmath_sin", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(sin(d)); + }}, + {u8"__fmath_sinh", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(sinh(d)); + }}, + {u8"__fmath_sqrt", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(sqrt(d)); + }}, + {u8"__fmath_tan", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(tan(d)); + }}, + {u8"__fmath_tanh", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(tanh(d)); + }}, + {u8"__fmath_trunc", + [](const std::vector &args) -> ObjectPtr { + ObjectPtr val = args[0]; + ValueType::DoubleClass d = val->getNumericValue(); + return std::make_shared(trunc(d)); + }}, + }; + return builtinFunctions; + } +} \ No newline at end of file diff --git a/src/Module/builtins.hpp b/src/Module/builtins.hpp index c15e7d8..1fc4ec3 100644 --- a/src/Module/builtins.hpp +++ b/src/Module/builtins.hpp @@ -1,28 +1,28 @@ #pragma once -#include "Ast/Expressions/VarExpr.hpp" -#include "Ast/Statements/VarDef.hpp" -#include "Ast/astBase.hpp" -#include +#include + #include #include -#include +#include #include -#include -#include + #include #include #include -#include -#include -#include + namespace Fig { namespace Builtins { - const TypeInfo ErrorInterfaceTypeInfo(u8"Error", true); + + inline static const TypeInfo &getErrorInterfaceTypeInfo() + { + static const TypeInfo ErrorInterfaceTypeInfo(u8"Error", true); + return ErrorInterfaceTypeInfo; + } /* // error's interface like: interface Error @@ -33,379 +33,24 @@ namespace Fig } */ - const std::unordered_map builtinValues = { - {u8"null", Object::getNullInstance()}, - {u8"true", Object::getTrueInstance()}, - {u8"false", Object::getFalseInstance()}, - {u8"Error", - std::make_shared(InterfaceType(ErrorInterfaceTypeInfo, - {Ast::InterfaceMethod(u8"toString", - Ast::FunctionParameters({}, {}), - std::make_shared(u8"String"), - nullptr), - Ast::InterfaceMethod(u8"getErrorClass", - Ast::FunctionParameters({}, {}), - std::make_shared(u8"String"), - nullptr), - Ast::InterfaceMethod(u8"getErrorMessage", - Ast::FunctionParameters({}, {}), - std::make_shared(u8"String"), - nullptr)}))}, - - {u8"Any", std::make_shared(StructType(ValueType::Any, nullptr, {}, true))}, - {u8"Int", std::make_shared(StructType(ValueType::Int, nullptr, {}, true))}, - {u8"Null", std::make_shared(StructType(ValueType::Null, nullptr, {}, true))}, - {u8"String", std::make_shared(StructType(ValueType::String, nullptr, {}, true))}, - {u8"Bool", std::make_shared(StructType(ValueType::Bool, nullptr, {}, true))}, - {u8"Double", std::make_shared(StructType(ValueType::Double, nullptr, {}, true))}, - {u8"Function", std::make_shared(StructType(ValueType::Function, nullptr, {}, true))}, - {u8"List", std::make_shared(StructType(ValueType::List, nullptr, {}, true))}, - {u8"Map", std::make_shared(StructType(ValueType::Map, nullptr, {}, true))}, - // Type `StructType` `StructInstance` `Module` `InterfaceType` - // Not allowed to call constructor! - }; + const std::unordered_map &getBuiltinValues(); using BuiltinFunction = std::function &)>; - const std::unordered_map builtinFunctionArgCounts = { - {u8"__fstdout_print", -1}, // variadic - {u8"__fstdout_println", -1}, // variadic - {u8"__fstdin_read", 0}, - {u8"__fstdin_readln", 0}, - {u8"__fvalue_type", 1}, - {u8"__fvalue_int_parse", 1}, - {u8"__fvalue_int_from", 1}, - {u8"__fvalue_double_parse", 1}, - {u8"__fvalue_double_from", 1}, - {u8"__fvalue_string_from", 1}, - {u8"__ftime_now_ns", 0}, - /* math start */ - {u8"__fmath_acos", 1}, - {u8"__fmath_acosh", 1}, - {u8"__fmath_asin", 1}, - {u8"__fmath_asinh", 1}, - {u8"__fmath_atan", 1}, - {u8"__fmath_atan2", 2}, - {u8"__fmath_atanh", 1}, - {u8"__fmath_ceil", 1}, - {u8"__fmath_cos", 1}, - {u8"__fmath_cosh", 1}, - {u8"__fmath_exp", 1}, - {u8"__fmath_expm1", 1}, - {u8"__fmath_fabs", 1}, - {u8"__fmath_floor", 1}, - {u8"__fmath_fmod", 2}, - {u8"__fmath_frexp", 1}, - {u8"__fmath_gcd", 2}, - {u8"__fmath_hypot", 2}, - {u8"__fmath_isequal", 2}, - {u8"__fmath_log", 1}, - {u8"__fmath_log10", 1}, - {u8"__fmath_log1p", 1}, - {u8"__fmath_log2", 1}, - {u8"__fmath_sin", 1}, - {u8"__fmath_sinh", 1}, - {u8"__fmath_sqrt", 1}, - {u8"__fmath_tan", 1}, - {u8"__fmath_tanh", 1}, - {u8"__fmath_trunc", 1}, - }; - const std::unordered_map builtinFunctions{ - {u8"__fstdout_print", - [](const std::vector &args) -> ObjectPtr { - for (auto arg : args) { std::print("{}", arg->toStringIO().toBasicString()); } - return std::make_shared(ValueType::IntClass(args.size())); - }}, - {u8"__fstdout_println", - [](const std::vector &args) -> ObjectPtr { - for (auto arg : args) { std::print("{}", arg->toStringIO().toBasicString()); } - std::print("\n"); - return std::make_shared(ValueType::IntClass(args.size())); - }}, - {u8"__fstdin_read", - [](const std::vector &args) -> ObjectPtr { - std::string input; - std::cin >> input; - return std::make_shared(FString::fromBasicString(input)); - }}, - {u8"__fstdin_readln", - [](const std::vector &args) -> ObjectPtr { - std::string line; - std::getline(std::cin, line); - return std::make_shared(FString::fromBasicString(line)); - }}, - {u8"__fvalue_type", - [](const std::vector &args) -> ObjectPtr { - return std::make_shared(args[0]->getTypeInfo().toString()); - }}, - {u8"__fvalue_int_parse", - [](const std::vector &args) -> ObjectPtr { - FString str = args[0]->as(); - try - { - ValueType::IntClass val = std::stoi(str.toBasicString()); - return std::make_shared(val); - } - catch (...) - { - throw RuntimeError(FString(std::format("Invalid int string for parsing", str.toBasicString()))); - } - }}, - {u8"__fvalue_int_from", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - if (val->is()) - { - return std::make_shared( - static_cast(val->as())); - } - else if (val->is()) - { - return std::make_shared( - static_cast(val->as() ? 1 : 0)); - } - else - { - throw RuntimeError(FString(std::format("Type '{}' cannot be converted to int", - val->getTypeInfo().toString().toBasicString()))); - } - }}, - {u8"__fvalue_double_parse", - [](const std::vector &args) -> ObjectPtr { - FString str = args[0]->as(); - try - { - ValueType::DoubleClass val = std::stod(str.toBasicString()); - return std::make_shared(ValueType::DoubleClass(val)); - } - catch (...) - { - throw RuntimeError(FString(std::format("Invalid double string for parsing", str.toBasicString()))); - } - }}, - {u8"__fvalue_double_from", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - if (val->is()) - { - return std::make_shared( - static_cast(val->as())); - } - else if (val->is()) - { - return std::make_shared( - ValueType::DoubleClass(val->as() ? 1.0 : 0.0)); - } - else - { - throw RuntimeError(FString(std::format("Type '{}' cannot be converted to double", - val->getTypeInfo().toString().toBasicString()))); - } - }}, - {u8"__fvalue_string_from", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - return std::make_shared(val->toStringIO()); - }}, - {u8"__ftime_now_ns", - [](const std::vector &args) -> ObjectPtr { - // returns nanoseconds - using namespace Fig::Time; - auto now = Clock::now(); - return std::make_shared(static_cast( - std::chrono::duration_cast(now - start_time).count())); - }}, + const std::unordered_map &getBuiltinFunctionArgCounts(); + const std::unordered_map &getBuiltinFunctions(); - /* math start */ - {u8"__fmath_acos", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(acos(d)); - }}, - {u8"__fmath_acosh", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(acosh(d)); - }}, - {u8"__fmath_asin", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(asin(d)); - }}, - {u8"__fmath_asinh", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(asinh(d)); - }}, - {u8"__fmath_atan", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(atan(d)); - }}, - {u8"__fmath_atan2", - [](const std::vector &args) -> ObjectPtr { - ValueType::DoubleClass y = args[0]->getNumericValue(); - ValueType::DoubleClass x = args[1]->getNumericValue(); - return std::make_shared(atan2(y, x)); - }}, - {u8"__fmath_atanh", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(atanh(d)); - }}, - {u8"__fmath_ceil", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(ceil(d)); - }}, - {u8"__fmath_cos", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(cos(d)); - }}, - {u8"__fmath_cosh", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(cosh(d)); - }}, - {u8"__fmath_exp", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(exp(d)); - }}, - {u8"__fmath_expm1", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(expm1(d)); - }}, - {u8"__fmath_fabs", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(fabs(d)); - }}, - {u8"__fmath_floor", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(floor(d)); - }}, - {u8"__fmath_fmod", - [](const std::vector &args) -> ObjectPtr { - ValueType::DoubleClass x = args[0]->getNumericValue(); - ValueType::DoubleClass y = args[1]->getNumericValue(); - return std::make_shared(fmod(x, y)); - }}, - {u8"__fmath_frexp", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - int e; - return std::make_shared(List({std::make_shared(frexp(d, &e)), - std::make_shared(static_cast(e))})); - }}, - {u8"__fmath_gcd", - [](const std::vector &args) -> ObjectPtr { - ValueType::IntClass x = args[0]->as(); - ValueType::IntClass y = args[1]->as(); - return std::make_shared(std::gcd(x, y)); - }}, - {u8"__fmath_hypot", - [](const std::vector &args) -> ObjectPtr { - ValueType::DoubleClass x = args[0]->getNumericValue(); - ValueType::DoubleClass y = args[1]->getNumericValue(); - return std::make_shared(hypot(x, y)); - }}, - {u8"__fmath_isequal", - [](const std::vector &args) -> ObjectPtr { - ValueType::DoubleClass x = args[0]->getNumericValue(); - ValueType::DoubleClass y = args[1]->getNumericValue(); - static const double epsilon = 1e-9; - return std::make_shared(fabs(x - y) < epsilon); - }}, - {u8"__fmath_log", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(log(d)); - }}, - {u8"__fmath_log10", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(log10(d)); - }}, - {u8"__fmath_log1p", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(log1p(d)); - }}, - {u8"__fmath_log2", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(log2(d)); - }}, - {u8"__fmath_sin", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(sin(d)); - }}, - {u8"__fmath_sinh", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(sinh(d)); - }}, - {u8"__fmath_sqrt", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(sqrt(d)); - }}, - {u8"__fmath_tan", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(tan(d)); - }}, - {u8"__fmath_tanh", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(tanh(d)); - }}, - {u8"__fmath_trunc", - [](const std::vector &args) -> ObjectPtr { - ObjectPtr val = args[0]; - ValueType::DoubleClass d = val->getNumericValue(); - return std::make_shared(trunc(d)); - }}, - }; inline bool isBuiltinFunction(const FString &name) { - return builtinFunctions.find(name) != builtinFunctions.end(); + return getBuiltinFunctions().find(name) != getBuiltinFunctions().end(); } inline BuiltinFunction getBuiltinFunction(const FString &name) { - auto it = builtinFunctions.find(name); - if (it == builtinFunctions.end()) + auto it = getBuiltinFunctions().find(name); + if (it == getBuiltinFunctions().end()) { throw RuntimeError(FString(std::format("Builtin function '{}' not found", name.toBasicString()))); } @@ -414,8 +59,8 @@ namespace Fig inline int getBuiltinFunctionParamCount(const FString &name) { - auto it = builtinFunctionArgCounts.find(name); - if (it == builtinFunctionArgCounts.end()) + auto it = getBuiltinFunctionArgCounts().find(name); + if (it == getBuiltinFunctionArgCounts().end()) { throw RuntimeError(FString(std::format("Builtin function '{}' not found", name.toBasicString()))); } 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 aa6daef..6d3b9e7 100644 --- a/xmake.lua +++ b/xmake.lua @@ -3,75 +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/Evaluator/main.cpp") - add_files("src/Core/warning.cpp") - add_files("src/Core/runtimeTime.cpp") + add_files("src/Evaluator/Core/*.cpp") + add_files("src/VirtualMachine/VirtualMachine.cpp") add_files("src/Evaluator/evaluator.cpp") - add_files("src/Evaluator/Value/value.cpp") - add_files("src/Lexer/lexer.cpp") - add_files("src/Parser/parser.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