From 01c16dee3fe90f688d058f27706b5b7c634c3a5c Mon Sep 17 00:00:00 2001 From: PuqiAR Date: Mon, 2 Feb 2026 16:11:08 +0800 Subject: [PATCH] =?UTF-8?q?[Feat]=20=E6=94=AF=E6=8C=81=E8=BF=90=E7=AE=97?= =?UTF-8?q?=E7=AC=A6=E9=87=8D=E8=BD=BD=EF=BC=81=E8=AF=A6=E8=A7=81=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E6=88=96=20Library/lang/lang.fig=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E3=80=82=E9=80=9A=E8=BF=87=20impl=20Operatio?= =?UTF-8?q?n=20for=20xxx=E5=AE=9E=E7=8E=B0=E9=87=8D=E8=BD=BD=20[Impl]=20?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E5=8F=82=E6=95=B0=E6=8C=87=E5=AE=9A=E7=8E=B0?= =?UTF-8?q?=E5=9C=A8=E4=B9=9F=E6=8E=A5=E5=8F=97=E4=B8=80=E4=B8=AA=20exp?= =?UTF-8?q?=EF=BC=8C=E9=80=90=E6=B8=90=E6=94=B9=E5=8A=A8=E5=85=B6=E4=BB=96?= =?UTF-8?q?=E4=B8=AD...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Ast/functionParameters.hpp | 14 +- src/Core/fig_string.hpp | 1 + src/Evaluator/Context/context.hpp | 108 ++++++- src/Evaluator/Core/Eval.cpp | 24 +- src/Evaluator/Core/EvalBinary.cpp | 387 +++++++++++++----------- src/Evaluator/Core/EvalFunctionCall.cpp | 106 ++++--- src/Evaluator/Core/EvalInitExpr.cpp | 12 +- src/Evaluator/Core/EvalLvObject.cpp | 10 +- src/Evaluator/Core/EvalStatement.cpp | 164 +++++++++- src/Evaluator/Core/EvalUnary.cpp | 24 +- src/Evaluator/Value/function.hpp | 24 +- src/Evaluator/Value/value.cpp | 6 + src/Evaluator/Value/value.hpp | 31 +- src/Evaluator/evaluator.hpp | 39 +-- src/Evaluator/evaluator_error.hpp | 13 +- src/Module/Library/lang/lang.fig | 38 +++ src/Module/builtins.cpp | 1 + src/Module/builtins.hpp | 29 +- src/Parser/parser.cpp | 19 +- src/Parser/parser.hpp | 3 +- 20 files changed, 698 insertions(+), 355 deletions(-) diff --git a/src/Ast/functionParameters.hpp b/src/Ast/functionParameters.hpp index 095c9c3..18299cf 100644 --- a/src/Ast/functionParameters.hpp +++ b/src/Ast/functionParameters.hpp @@ -17,8 +17,10 @@ namespace Fig::Ast func test2(dp1 = 10, dp2:String = "default parameter 2") */ - using PosParasType = std::vector>; - using DefParasType = std::vector>>; + using PosParasType = std::vector>; + // name type exp + using DefParasType = std::vector>>; + // name type exp default value PosParasType posParas; DefParasType defParas; // default parameters @@ -61,9 +63,9 @@ namespace Fig::Ast for (auto &p : posParas) { out += p.first; - if (!p.second.empty()) + if (p.second != nullptr) { - out += FString(u8":" + p.second); + out += FString(u8":" + p.second->toString()); } out += u8","; } @@ -75,9 +77,9 @@ namespace Fig::Ast for (auto &p : defParas) { out += p.first; - if (!p.second.first.empty()) + if (p.second.first != nullptr) { - out += FString(u8":" + p.second.first); + out += FString(u8":" + p.second.first->toString()); } if (p.second.second != nullptr) { diff --git a/src/Core/fig_string.hpp b/src/Core/fig_string.hpp index a676436..07fc1d1 100644 --- a/src/Core/fig_string.hpp +++ b/src/Core/fig_string.hpp @@ -38,6 +38,7 @@ namespace Fig { public: using std::u8string::u8string; + using std::u8string::operator=; FString operator+(const FString &x) { diff --git a/src/Evaluator/Context/context.hpp b/src/Evaluator/Context/context.hpp index ef202e4..8c4a907 100644 --- a/src/Evaluator/Context/context.hpp +++ b/src/Evaluator/Context/context.hpp @@ -1,15 +1,17 @@ #pragma once -#include "Evaluator/Value/function.hpp" -#include -#include -#include #include #include +#include #include #include #include +#include +#include +#include +#include +#include #include #include #include @@ -26,6 +28,35 @@ namespace Fig std::unordered_map implMethods; }; + struct OperationRecord + { + using UnaryOpFn = std::function; + using BinaryOpFn = std::function; + + std::unordered_map unOpRec; + std::unordered_map binOpRec; + + bool hasUnaryOp(Ast::Operator op) const + { + return unOpRec.contains(op); + } + + bool hasBinaryOp(Ast::Operator op) const + { + return binOpRec.contains(op); + } + + const UnaryOpFn &getUnaryOpFn(Ast::Operator op) const + { + return unOpRec.at(op); + } + + const BinaryOpFn &getBinaryOpFn(Ast::Operator op) const + { + return binOpRec.at(op); + } + }; + class Context : public std::enable_shared_from_this { private: @@ -37,7 +68,7 @@ namespace Fig // implRegistry std::unordered_map, TypeInfoHash> implRegistry; - + std::unordered_map opRegistry; public: ContextPtr parent; @@ -54,6 +85,7 @@ namespace Fig { variables.insert(c.variables.begin(), c.variables.end()); implRegistry.insert(c.implRegistry.begin(), c.implRegistry.end()); + opRegistry.insert(c.opRegistry.begin(), c.opRegistry.end()); // structTypeNames.insert(c.structTypeNames.begin(), // c.structTypeNames.end()); } @@ -362,6 +394,72 @@ namespace Fig throw ""; // ignore warning } + bool hasOperatorImplemented(const TypeInfo &type, Ast::Operator op, bool isUnary = false) const + { + auto it = opRegistry.find(type); + if (it != opRegistry.end()) + { + const OperationRecord &rec = it->second; + if (isUnary) + return rec.hasUnaryOp(op); + else + return rec.hasBinaryOp(op); + } + + if (parent) return parent->hasOperatorImplemented(type, op, isUnary); + return false; + } + + std::optional getUnaryOperatorFn(const TypeInfo &type, Ast::Operator op) const + { + auto it = opRegistry.find(type); + if (it != opRegistry.end()) + { + const OperationRecord &rec = it->second; + if (rec.hasUnaryOp(op)) return rec.getUnaryOpFn(op); + } + + if (parent) return parent->getUnaryOperatorFn(type, op); + return std::nullopt; + } + + std::optional getBinaryOperatorFn(const TypeInfo &type, Ast::Operator op) const + { + auto it = opRegistry.find(type); + if (it != opRegistry.end()) + { + const OperationRecord &rec = it->second; + if (rec.hasBinaryOp(op)) return rec.getBinaryOpFn(op); + } + + if (parent) return parent->getBinaryOperatorFn(type, op); + return std::nullopt; + } + + void registerUnaryOperator(const TypeInfo &type, Ast::Operator op, OperationRecord::UnaryOpFn fn) + { + opRegistry[type].unOpRec[op] = std::move(fn); + } + + void registerBinaryOperator(const TypeInfo &type, Ast::Operator op, OperationRecord::BinaryOpFn fn) + { + opRegistry[type].binOpRec[op] = std::move(fn); + } + + bool removeUnaryOperator(const TypeInfo &type, Ast::Operator op) + { + auto it = opRegistry.find(type); + if (it != opRegistry.end()) return it->second.unOpRec.erase(op) > 0; + return false; + } + + bool removeBinaryOperator(const TypeInfo &type, Ast::Operator op) + { + auto it = opRegistry.find(type); + if (it != opRegistry.end()) return it->second.binOpRec.erase(op) > 0; + return false; + } + void printStackTrace(std::ostream &os = std::cerr, int indent = 0) const { const Context *ctx = this; diff --git a/src/Evaluator/Core/Eval.cpp b/src/Evaluator/Core/Eval.cpp index a36c1d0..5044dbe 100644 --- a/src/Evaluator/Core/Eval.cpp +++ b/src/Evaluator/Core/Eval.cpp @@ -39,27 +39,7 @@ namespace Fig 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); + return evalFunctionCall(fnCall, ctx); } case AstType::FunctionLiteralExpr: { auto fnLiteral = std::static_pointer_cast(exp); @@ -84,7 +64,7 @@ namespace Fig body = fnLiteral->getBlockBody(); } - Function fn(fnLiteral->paras, ValueType::Any, body, ctx + Function fn(FString(std::format("")),fnLiteral->paras, ValueType::Any, body, ctx /* pass the ctx(fnLiteral eval context) as closure context */ diff --git a/src/Evaluator/Core/EvalBinary.cpp b/src/Evaluator/Core/EvalBinary.cpp index a51247d..7a303dc 100644 --- a/src/Evaluator/Core/EvalBinary.cpp +++ b/src/Evaluator/Core/EvalBinary.cpp @@ -1,9 +1,13 @@ +#include "Ast/Expressions/BinaryExpr.hpp" #include #include #include #include #include +#include +#include + namespace Fig { RvObject Evaluator::evalBinary(Ast::BinaryExpr bin, ContextPtr ctx) @@ -11,229 +15,198 @@ namespace Fig using Ast::Operator; Operator op = bin->op; Ast::Expression lexp = bin->lexp, rexp = bin->rexp; + + const auto &tryInvokeOverloadFn = + [ctx, op](const ObjectPtr &lhs, const ObjectPtr &rhs, const std::function &rollback) { + if (lhs->is() && lhs->getTypeInfo() == rhs->getTypeInfo()) + { + // 运算符重载 + const TypeInfo &type = actualType(lhs); + if (ctx->hasOperatorImplemented(type, op)) + { + const auto &fnOpt = ctx->getBinaryOperatorFn(type, op); + return (*fnOpt)(lhs, rhs); + } + } + return rollback(); + }; + 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); + return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { + 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); - }; + return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { + 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)); - }; + return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { + 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); - }; + return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared(*lhs / *rhs); }); + } case Operator::Modulo: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); + return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { + if (lhs->is() && rhs->is()) + { + ValueType::IntClass lv = lhs->as(); + ValueType::IntClass rv = rhs->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); + }); + } - 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(); + return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs, ctx, bin]() { + 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 (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 - 依次我们可以判断内置类型 + 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 + 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(); - } + 其中 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); + 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)); + return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { + 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)); + return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { + 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)); + return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { + 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)); + return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { + 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)); + return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { + 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: { @@ -242,46 +215,98 @@ namespace Fig lv.set(rhs); return rhs; } + + case Operator::And: { + ObjectPtr lhs = eval(lexp, ctx); + if (lhs->is() && !isBoolObjectTruthy(lhs)) { return Object::getFalseInstance(); } + ObjectPtr rhs = eval(rexp, ctx); + return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { 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 tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared(*lhs || *rhs); }); + } + case Operator::Equal: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared(*lhs == *rhs); }); + } + case Operator::NotEqual: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared(*lhs != *rhs); }); + } + case Operator::Less: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared(*lhs < *rhs); }); + } + case Operator::LessEqual: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared(*lhs <= *rhs); }); + } + case Operator::Greater: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared(*lhs > *rhs); }); + } + case Operator::GreaterEqual: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared(*lhs >= *rhs); }); + } case Operator::PlusAssign: { LvObject lv = evalLv(lexp, ctx); + const ObjectPtr &lhs = lv.get(); ObjectPtr rhs = eval(rexp, ctx); - lv.set(std::make_shared(*(lv.get()) + *rhs)); + const ObjectPtr &result = + tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared(*lhs + *rhs); }); + lv.set(result); return rhs; } case Operator::MinusAssign: { LvObject lv = evalLv(lexp, ctx); + const ObjectPtr &lhs = lv.get(); ObjectPtr rhs = eval(rexp, ctx); - lv.set(std::make_shared(*(lv.get()) - *rhs)); + const ObjectPtr &result = + tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared(*lhs - *rhs); }); + lv.set(result); return rhs; } case Operator::AsteriskAssign: { LvObject lv = evalLv(lexp, ctx); + const ObjectPtr &lhs = lv.get(); ObjectPtr rhs = eval(rexp, ctx); - lv.set(std::make_shared(*(lv.get()) * (*rhs))); + const ObjectPtr &result = + tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared(*lhs * *rhs); }); + lv.set(result); return rhs; } case Operator::SlashAssign: { LvObject lv = evalLv(lexp, ctx); + const ObjectPtr &lhs = lv.get(); ObjectPtr rhs = eval(rexp, ctx); - lv.set(std::make_shared(*(lv.get()) / *rhs)); + const ObjectPtr &result = + tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared(*lhs / *rhs); }); + lv.set(result); return rhs; } case Operator::PercentAssign: { LvObject lv = evalLv(lexp, ctx); + const ObjectPtr &lhs = lv.get(); ObjectPtr rhs = eval(rexp, ctx); - lv.set(std::make_shared(*(lv.get()) / *rhs)); + const ObjectPtr &result = + tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared(*lhs % *rhs); }); + lv.set(result); 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 +}; // namespace Fig \ No newline at end of file diff --git a/src/Evaluator/Core/EvalFunctionCall.cpp b/src/Evaluator/Core/EvalFunctionCall.cpp index 2de0594..f53f5ef 100644 --- a/src/Evaluator/Core/EvalFunctionCall.cpp +++ b/src/Evaluator/Core/EvalFunctionCall.cpp @@ -1,3 +1,6 @@ +#include "Ast/functionParameters.hpp" +#include "Evaluator/Value/value.hpp" +#include #include #include #include @@ -5,41 +8,74 @@ namespace Fig { - RvObject Evaluator::evalFunctionCall(const Function &fn, - const Ast::FunctionArguments &fnArgs, - const FString &fnName, - ContextPtr ctx) + RvObject Evaluator::executeFunction(const Function &fn, + const Ast::FunctionCallArgs &args, + ContextPtr fnCtx) // new context for fn, already filled paras { - const Function &fnStruct = fn; + // const FString &fnName = fn.name; + if (fn.type == Function::Builtin || fn.type == Function::MemberType) + { + if (fn.type == Function::Builtin) { return fn.builtin(args.argv); } + else + { + return fn.mtFn(nullptr, + args.argv); // wrapped member type function (`this` provided by evalMemberExpr) + } + } + // else: normal fn, args is needless + for (const auto &stmt : fn.body->stmts) + { + StatementResult sr = evalStatement(stmt, fnCtx); + if (sr.isError()) + { + throw EvaluatorError(u8"UncaughtExceptionError", + std::format("Uncaught exception: {}", sr.result->toString().toBasicString()), + stmt); + } + if (!sr.isNormal()) + { + return sr.result; + } + } + return Object::getNullInstance(); + } + RvObject Evaluator::evalFunctionCall(const Ast::FunctionCall &call, ContextPtr ctx) + { + RvObject fnObj = eval(call->callee, ctx); + if (fnObj->getTypeInfo() != ValueType::Function) + { + throw EvaluatorError(u8"ObjectNotCallable", + std::format("Object `{}` isn't callable", fnObj->toString().toBasicString()), + call->callee); + } + + const Function &fn = fnObj->as(); + + const FString &fnName = fn.name; + const Ast::FunctionArguments &fnArgs = call->arg; + Ast::FunctionCallArgs evaluatedArgs; - if (fnStruct.type == Function::Builtin || fnStruct.type == Function::MemberType) + if (fn.type == Function::Builtin || fn.type == Function::MemberType) { for (const auto &argExpr : fnArgs.argv) { evaluatedArgs.argv.push_back(eval(argExpr, ctx)); } - if (fnStruct.builtinParamCount != -1 && fnStruct.builtinParamCount != evaluatedArgs.getLength()) + if (fn.builtinParamCount != -1 && fn.builtinParamCount != evaluatedArgs.getLength()) { throw EvaluatorError(u8"BuiltinArgumentMismatchError", std::format("Builtin function '{}' expects {} arguments, but {} were provided", fnName.toBasicString(), - fnStruct.builtinParamCount, + fn.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) - } + return executeFunction(fn, evaluatedArgs, nullptr); } // check argument, all types of parameters - Ast::FunctionParameters fnParas = fnStruct.paras; + Ast::FunctionParameters fnParas = fn.paras; // create new context for function call auto newContext = std::make_shared(FString(std::format("", fnName.toBasicString())), - fnStruct.closureContext); + fn.closureContext); if (fnParas.variadic) goto VariadicFilling; @@ -60,8 +96,8 @@ namespace Fig 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 + const TypeInfo &expectedType = actualType(eval(fnParas.posParas[i].second, ctx)); // 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)) @@ -80,7 +116,7 @@ namespace Fig for (; i < fnArgs.getLength(); i++) { size_t defParamIndex = i - fnParas.posParas.size(); - TypeInfo expectedType(fnParas.defParas[defParamIndex].second.first); + const TypeInfo &expectedType = actualType(eval(fnParas.defParas[defParamIndex].second.first, ctx)); ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second, ctx); if (!isTypeMatch(expectedType, defaultVal, ctx)) @@ -126,13 +162,13 @@ namespace Fig if (j < fnParas.posParas.size()) { paramName = fnParas.posParas[j].first; - paramType = TypeInfo(fnParas.posParas[j].second); + paramType = actualType(eval(fnParas.posParas[j].second, ctx)); } else { size_t defParamIndex = j - fnParas.posParas.size(); paramName = fnParas.defParas[defParamIndex].first; - paramType = TypeInfo(fnParas.defParas[defParamIndex].second.first); + paramType = actualType(eval(fnParas.defParas[defParamIndex].second.first, ctx)); } AccessModifier argAm = AccessModifier::Normal; newContext->def(paramName, paramType, argAm, evaluatedArgs.argv[j]); @@ -152,30 +188,16 @@ namespace Fig 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)) + ObjectPtr retVal = executeFunction(fn, evaluatedArgs, newContext); + + if (!isTypeMatch(fn.retType, retVal, ctx)) { throw EvaluatorError(u8"ReturnTypeMismatchError", std::format("Function '{}' expects return type '{}', but got type '{}'", fnName.toBasicString(), - fnStruct.retType.toString().toBasicString(), + fn.retType.toString().toBasicString(), prettyType(retVal).toBasicString()), - fnStruct.body); + fn.body); } return retVal; } diff --git a/src/Evaluator/Core/EvalInitExpr.cpp b/src/Evaluator/Core/EvalInitExpr.cpp index 37e748b..24a5136 100644 --- a/src/Evaluator/Core/EvalInitExpr.cpp +++ b/src/Evaluator/Core/EvalInitExpr.cpp @@ -1,3 +1,4 @@ +#include "Evaluator/Value/value.hpp" #include #include #include @@ -347,16 +348,13 @@ namespace Fig // 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); - + const FString &funcName = fn.name; + const auto &funcSlot = stDefCtx->get(funcName); + instanceCtx->def(funcName, ValueType::Function, funcSlot->am, - std::make_shared(Function(fn.paras, fn.retType, fn.body, instanceCtx))); + std::make_shared(Function(funcName, fn.paras, fn.retType, fn.body, instanceCtx))); } return std::make_shared(StructInstance(structT.type, instanceCtx)); diff --git a/src/Evaluator/Core/EvalLvObject.cpp b/src/Evaluator/Core/EvalLvObject.cpp index fd93001..c4e18a8 100644 --- a/src/Evaluator/Core/EvalLvObject.cpp +++ b/src/Evaluator/Core/EvalLvObject.cpp @@ -1,3 +1,4 @@ +#include "Evaluator/Value/value.hpp" #include #include #include @@ -36,6 +37,7 @@ namespace Fig return LvObject(std::make_shared( member, std::make_shared(Function( + member, [baseVal, member](ObjectPtr self, std::vector args) -> ObjectPtr { if (self) { return baseVal->getMemberFunction(member)(self, args); } return baseVal->getMemberFunction(member)(baseVal, args); @@ -52,7 +54,8 @@ namespace Fig // e.g. impl xxx for Int auto &fn = ctx->getImplementedMethod(baseVal->getTypeInfo(), member); - Function boundFn(fn.paras, + Function boundFn(member, + fn.paras, fn.retType, fn.body, ctx // current context @@ -74,7 +77,8 @@ namespace Fig if (ctx->hasMethodImplemented(si.parentType, member)) { auto &fn = ctx->getImplementedMethod(si.parentType, member); - Function boundFn(fn.paras, + Function boundFn(member, + fn.paras, fn.retType, fn.body, si.localContext // create a new function and set closure context @@ -92,7 +96,7 @@ namespace Fig 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); + Function fn(member, ifm.paras, actualType(eval(ifm.returnType, ctx)), ifm.defaultBody, ctx); return LvObject(std::make_shared( member, std::make_shared(fn), ValueType::Function, AccessModifier::PublicConst), diff --git a/src/Evaluator/Core/EvalStatement.cpp b/src/Evaluator/Core/EvalStatement.cpp index 1eb1db6..9f34ce9 100644 --- a/src/Evaluator/Core/EvalStatement.cpp +++ b/src/Evaluator/Core/EvalStatement.cpp @@ -1,8 +1,16 @@ +#include "Ast/AccessModifier.hpp" +#include "Ast/Expressions/FunctionCall.hpp" +#include "Ast/astBase.hpp" +#include "Ast/functionParameters.hpp" +#include "Core/fig_string.hpp" +#include "Evaluator/Core/StatementResult.hpp" +#include "Evaluator/Value/value.hpp" #include #include #include #include +#include namespace Fig { @@ -17,7 +25,6 @@ namespace Fig } case VarDefSt: { auto varDef = std::static_pointer_cast(stmt); - if (ctx->containsInThisScope(varDef->name)) { @@ -62,7 +69,6 @@ namespace Fig case FunctionDefSt: { auto fnDef = std::static_pointer_cast(stmt); - const FString &fnName = fnDef->name; if (ctx->containsInThisScope(fnName)) @@ -79,7 +85,7 @@ namespace Fig returnType = actualType(returnTypeValue); } - Function fn(fnDef->paras, returnType, fnDef->body, ctx); + Function fn(fnName, fnDef->paras, returnType, fnDef->body, ctx); ctx->def(fnName, ValueType::Function, (fnDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const), @@ -89,7 +95,6 @@ namespace Fig case StructSt: { auto stDef = std::static_pointer_cast(stmt); - if (ctx->containsInThisScope(stDef->name)) { @@ -148,7 +153,6 @@ namespace Fig case InterfaceDefSt: { auto ifd = std::static_pointer_cast(stmt); - const FString &interfaceName = ifd->name; @@ -169,7 +173,6 @@ namespace Fig case ImplementSt: { auto ip = std::static_pointer_cast(stmt); - TypeInfo structType(ip->structName); TypeInfo interfaceType(ip->interfaceName); @@ -216,7 +219,148 @@ namespace Fig std::format("Variable `{}` is not a struct type", ip->structName.toBasicString()), ip); } + auto &implementMethods = ip->methods; + + if (ip->interfaceName == u8"Operation") + { + // 运算符重载 + /* + impl Operation for xxx + { + add(l, r) {...} + } + */ + using enum Ast::Operator; + static const std::unordered_map> magic_name_to_op = { + // 算术 + {u8"Add", {Ast::Operator::Add, 2}}, + {u8"Sub", {Ast::Operator::Subtract, 2}}, + {u8"Mul", {Ast::Operator::Multiply, 2}}, + {u8"Div", {Ast::Operator::Divide, 2}}, + {u8"Mod", {Ast::Operator::Modulo, 2}}, + {u8"Pow", {Ast::Operator::Power, 2}}, + + // 逻辑(一元) + {u8"Neg", {Ast::Operator::Subtract, 1}}, // 一元负号 + {u8"Not", {Ast::Operator::Not, 1}}, + + // 逻辑(二元) + {u8"And", {Ast::Operator::And, 2}}, + {u8"Or", {Ast::Operator::Or, 2}}, + + // 比较 + {u8"Equal", {Ast::Operator::Equal, 2}}, + {u8"NotEqual", {Ast::Operator::NotEqual, 2}}, + {u8"LessThan", {Ast::Operator::Less, 2}}, + {u8"LessEqual", {Ast::Operator::LessEqual, 2}}, + {u8"GreaterThan", {Ast::Operator::Greater, 2}}, + {u8"GreaterEqual", {Ast::Operator::GreaterEqual, 2}}, + {u8"Is", {Ast::Operator::Is, 2}}, + + // 位运算(一元) + {u8"BitNot", {Ast::Operator::BitNot, 1}}, + + // 位运算(二元) + {u8"BitAnd", {Ast::Operator::BitAnd, 2}}, + {u8"BitOr", {Ast::Operator::BitOr, 2}}, + {u8"BitXor", {Ast::Operator::BitXor, 2}}, + {u8"ShiftLeft", {Ast::Operator::ShiftLeft, 2}}, + {u8"ShiftRight", {Ast::Operator::ShiftRight, 2}}, + }; + for (auto &implMethod : implementMethods) + { + const FString &opName = implMethod.name; + if (!magic_name_to_op.contains(opName)) + { + // ... 现在忽略 + // 未来可能报错 + + continue; + } + + auto [op, expectArgCnt] = magic_name_to_op.at(opName); + + // type op isUnary(1-->true, 2-->false) + if (ctx->hasOperatorImplemented(structType, op, (expectArgCnt == 1 ? true : false))) + { + throw EvaluatorError( + u8"DuplicateImplementError", + std::format("{} has already implement by another interface", opName.toBasicString()), + ip); + } + + size_t paraCnt = implMethod.paras.posParas.size(); // 必须为位置参数! + if (paraCnt != expectArgCnt || implMethod.paras.size() != expectArgCnt) + { + // 特化报错,更详细易读 + throw EvaluatorError(u8"InterfaceSignatureMismatch", + std::format("Operator {} for {} arg count must be {}, got {}", + opName.toBasicString(), + structLv.name().toBasicString(), + expectArgCnt, + paraCnt), + ip); + } + + FString opFnName(u8"Operation." + prettyType(structTypeObj) + u8"." + opName); + + ContextPtr fnCtx = std::make_shared( + FString(std::format("", opFnName.toBasicString())), ctx); + + const auto &fillOpFnParas = [this, structType, implMethod, opFnName, fnCtx, ctx, paraCnt]( + const std::vector &args) { + const Ast::FunctionParameters ¶s = implMethod.paras; + for (size_t i = 0; i < paraCnt; ++i) + { + const TypeInfo ¶Type = actualType(eval(paras.posParas[i].second, ctx)); + if (paraType != ValueType::Any && paraType != structType) + { + throw EvaluatorError( + u8"ParameterTypeError", + std::format("Invalid op fn parameter type '{}' of `{}`, must be `{}`", + paraType.toString().toBasicString(), + paras.posParas[i].first.toBasicString(), + structType.toString().toBasicString()), + paras.posParas[i].second); + } + fnCtx->def(paras.posParas[i].first, paraType, AccessModifier::Normal, args[i]); + } + }; + + if (paraCnt == 1) + { + ctx->registerUnaryOperator(structType, op, [=, this](const ObjectPtr &value) -> ObjectPtr { + fillOpFnParas({value}); + return executeFunction(Function(opFnName, + implMethod.paras, // parameters + structType, // return type --> struct type + implMethod.body, // body + ctx // closure context + ), + Ast::FunctionCallArgs{.argv = {value}}, + fnCtx); + }); + } + else + { + ctx->registerBinaryOperator( + structType, op, [=, this](const ObjectPtr &lhs, const ObjectPtr &rhs) { + fillOpFnParas({lhs, rhs}); + return executeFunction(Function(opFnName, + implMethod.paras, // parameters + structType, // return type --> struct type + implMethod.body, // body + ctx // closure context + ), + Ast::FunctionCallArgs{.argv = {lhs, rhs}}, + fnCtx); + }); + } + } + return StatementResult::normal(); + } + InterfaceType &interface = interfaceObj->as(); // ===== interface implementation validation ===== @@ -291,7 +435,7 @@ namespace Fig ObjectPtr returnTypeValue = eval(ifMethod.returnType, ctx); record.implMethods[name] = - Function(implMethod.paras, actualType(returnTypeValue), implMethod.body, ctx); + Function(implMethod.name, implMethod.paras, actualType(returnTypeValue), implMethod.body, ctx); } for (auto &m : interface.methods) @@ -406,7 +550,6 @@ namespace Fig case TrySt: { auto tryst = std::static_pointer_cast(stmt); - ContextPtr tryCtx = std::make_shared( FString(std::format("", tryst->getAAI().line, tryst->getAAI().column)), ctx); @@ -445,7 +588,6 @@ namespace Fig case ThrowSt: { auto ts = std::static_pointer_cast(stmt); - ObjectPtr value = eval(ts->value, ctx); if (value->is()) @@ -457,7 +599,6 @@ namespace Fig case ReturnSt: { auto returnSt = std::static_pointer_cast(stmt); - ObjectPtr returnValue = Object::getNullInstance(); // default is null if (returnSt->retValue) returnValue = eval(returnSt->retValue, ctx); @@ -495,7 +636,6 @@ namespace Fig case BlockStatement: { auto block = std::static_pointer_cast(stmt); - ContextPtr blockCtx = std::make_shared( FString(std::format("", block->getAAI().line, block->getAAI().column)), ctx); @@ -507,4 +647,4 @@ namespace Fig FString(std::format("Feature stmt {} unsupported yet", magic_enum::enum_name(stmt->getType())))); } } -}; \ No newline at end of file +}; // namespace Fig \ No newline at end of file diff --git a/src/Evaluator/Core/EvalUnary.cpp b/src/Evaluator/Core/EvalUnary.cpp index a2e1d44..eda404a 100644 --- a/src/Evaluator/Core/EvalUnary.cpp +++ b/src/Evaluator/Core/EvalUnary.cpp @@ -1,3 +1,4 @@ +#include "Evaluator/Value/value.hpp" #include #include #include @@ -10,16 +11,31 @@ namespace Fig Operator op = un->op; Ast::Expression exp = un->exp; ObjectPtr value = eval(exp, ctx); + + const auto &tryInvokeOverloadFn = [ctx, op](const ObjectPtr &rhs, const std::function &rollback) { + if (rhs->is()) + { + // 运算符重载 + const TypeInfo &type = actualType(rhs); + if (ctx->hasOperatorImplemented(type, op)) + { + const auto &fnOpt = ctx->getUnaryOperatorFn(type, op); + return (*fnOpt)(rhs); + } + } + return rollback(); + }; + switch (op) { case Operator::Not: { - return std::make_shared(!(*value)); + return tryInvokeOverloadFn(value, [value]() { return std::make_shared(!(*value)); }); } case Operator::Subtract: { - return std::make_shared(-(*value)); + return tryInvokeOverloadFn(value, [value]() { return std::make_shared(-(*value)); }); } case Operator::BitNot: { - return std::make_shared(bit_not((*value))); + return tryInvokeOverloadFn(value, [value]() { return std::make_shared(bit_not(*value)); }); } default: { throw EvaluatorError(u8"UnsupportedOpError", @@ -28,4 +44,4 @@ namespace Fig } } } -}; \ No newline at end of file +}; // namespace Fig \ No newline at end of file diff --git a/src/Evaluator/Value/function.hpp b/src/Evaluator/Value/function.hpp index 8e1d368..a2ff208 100644 --- a/src/Evaluator/Value/function.hpp +++ b/src/Evaluator/Value/function.hpp @@ -16,6 +16,7 @@ namespace Fig { public: std::size_t id; + FString name; enum FnType { @@ -52,11 +53,13 @@ namespace Fig new (&body) Ast::BlockStatement(); } - Function(Ast::FunctionParameters _paras, + Function(const FString &_name, + Ast::FunctionParameters _paras, TypeInfo _retType, Ast::BlockStatement _body, ContextPtr _closureContext) : id(nextId()), // 分配唯一 ID + name(_name), paras(std::move(_paras)), retType(std::move(_retType)), body(std::move(_body)), @@ -65,25 +68,22 @@ namespace Fig type = Normal; } - Function(std::function(const std::vector> &)> fn, int argc) : - id(nextId()), type(Builtin), builtin(fn), builtinParamCount(argc) + Function(const FString &_name, std::function(const std::vector> &)> fn, int argc) : + id(nextId()), name(_name), type(Builtin), builtin(fn), builtinParamCount(argc) { type = Builtin; } - Function(std::function(std::shared_ptr, + Function(const FString &_name, std::function(std::shared_ptr, const std::vector> &)> fn, int argc) : - id(nextId()), type(MemberType), mtFn(fn), builtinParamCount(argc) + id(nextId()), name(_name), type(MemberType), mtFn(fn), builtinParamCount(argc) { type = MemberType; } // ===== Copy / Move ===== - Function(const Function &other) - { - copyFrom(other); - } + Function(const Function &other) { copyFrom(other); } Function &operator=(const Function &other) { if (this != &other) @@ -94,10 +94,7 @@ namespace Fig return *this; }; - ~Function() - { - destroy(); - } + ~Function() { destroy(); } // ===== Comparison ===== bool operator==(const Function &other) const noexcept { return id == other.id; } @@ -125,6 +122,7 @@ namespace Fig void copyFrom(const Function &other) { + name = other.name; type = other.type; id = nextId(); // 每个复制都生成新的ID builtinParamCount = other.builtinParamCount; diff --git a/src/Evaluator/Value/value.cpp b/src/Evaluator/Value/value.cpp index 0fd58a2..71ef674 100644 --- a/src/Evaluator/Value/value.cpp +++ b/src/Evaluator/Value/value.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -84,6 +85,11 @@ namespace Fig return obj->as().type; } + if (t == ValueType::InterfaceType) + { + return obj->as().type; + } + if (t == ValueType::StructInstance) return obj->as().parentType; return t; } diff --git a/src/Evaluator/Value/value.hpp b/src/Evaluator/Value/value.hpp index 0f6819b..2ac8280 100644 --- a/src/Evaluator/Value/value.hpp +++ b/src/Evaluator/Value/value.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -87,9 +88,8 @@ namespace Fig Module, InterfaceType>; - - - static std::unordered_map, TypeInfoHash> getMemberTypeFunctions() + static std::unordered_map, TypeInfoHash> + getMemberTypeFunctions() { static const std::unordered_map, TypeInfoHash> memberTypeFunctions{ @@ -241,9 +241,8 @@ namespace Fig return memberTypeFunctions; } - - - static std::unordered_map, TypeInfoHash> getMemberTypeFunctionsParas() + static std::unordered_map, TypeInfoHash> + getMemberTypeFunctionsParas() { static const std::unordered_map, TypeInfoHash> memberTypeFunctionsParas{ @@ -425,7 +424,7 @@ namespace Fig return toString(); } - FString toString() const + FString toString(std::unordered_set &visited) const { if (is()) return FString(u8"null"); if (is()) return FString(std::to_string(as())); @@ -445,13 +444,16 @@ namespace Fig static_cast(&as()))); if (is()) { + if (visited.contains(this)) { return u8"[...]"; } + visited.insert(this); + FString output(u8"["); const List &list = as(); bool first_flag = true; for (auto &ele : list) { if (!first_flag) output += u8", "; - output += ele.value->toString(); + output += ele.value->toString(visited); first_flag = false; } output += u8"]"; @@ -459,13 +461,16 @@ namespace Fig } if (is()) { + if (visited.contains(this)) { return u8"{...}"; } + visited.insert(this); + FString output(u8"{"); const Map &map = as(); bool first_flag = true; for (auto &[key, value] : map) { if (!first_flag) output += u8", "; - output += key.value->toString() + FString(u8" : ") + value->toString(); + output += key.value->toString(visited) + FString(u8" : ") + value->toString(visited); first_flag = false; } output += u8"}"; @@ -486,6 +491,12 @@ namespace Fig return FString(u8""); } + FString toString() const + { + std::unordered_set visited{}; + return toString(visited); + } + private: static std::string makeTypeErrorMessage(const char *prefix, const char *op, const Object &lhs, const Object &rhs) @@ -612,7 +623,7 @@ namespace Fig friend Object operator!(const Object &v) { if (!v.is()) - throw ValueError( + throw ValueError( FString(std::format("Logical NOT requires bool: '{}'", v.getTypeInfo().name.toBasicString()))); return Object(!v.as()); } diff --git a/src/Evaluator/evaluator.hpp b/src/Evaluator/evaluator.hpp index 835554a..b982fc4 100644 --- a/src/Evaluator/evaluator.hpp +++ b/src/Evaluator/evaluator.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -24,15 +25,9 @@ namespace Fig FString sourcePath; std::vector sourceLines; - void SetSourcePath(const FString &sp) - { - sourcePath = sp; - } + void SetSourcePath(const FString &sp) { sourcePath = sp; } - void SetSourceLines(const std::vector &sl) - { - sourceLines = sl; - } + void SetSourceLines(const std::vector &sl) { sourceLines = sl; } void SetGlobalContext(ContextPtr ctx) { @@ -40,11 +35,7 @@ namespace Fig global = ctx; } - void CreateGlobalContext() - { - global = std::make_shared( - FString(u8"")); - } + void CreateGlobalContext() { global = std::make_shared(FString(u8"")); } void RegisterBuiltins() // only function { @@ -53,12 +44,8 @@ namespace Fig for (auto &[name, fn] : Builtins::getBuiltinFunctions()) { int argc = Builtins::getBuiltinFunctionParamCount(name); - Function f(fn, argc); - global->def( - name, - ValueType::Function, - AccessModifier::Const, - std::make_shared(f)); + Function f(name, fn, argc); + global->def(name, ValueType::Function, AccessModifier::Const, std::make_shared(f)); } } @@ -68,11 +55,7 @@ namespace Fig for (auto &[name, val] : Builtins::getBuiltinValues()) { - global->def( - name, - val->getTypeInfo(), - AccessModifier::Const, - val); + global->def(name, val->getTypeInfo(), AccessModifier::Const, val); } } @@ -86,12 +69,16 @@ namespace Fig LvObject evalLv(Ast::Expression, ContextPtr); // for access: a.b / index a[b] /* Right-value eval*/ - RvObject evalInitExpr(Ast::InitExpr, ContextPtr); // only allows evalUnary to call + RvObject evalInitExpr(Ast::InitExpr, ContextPtr); // only allows evalUnary to call RvObject evalBinary(Ast::BinaryExpr, ContextPtr); // normal binary expr: +, -, *.... RvObject evalUnary(Ast::UnaryExpr, ContextPtr); // unary expr RvObject evalTernary(Ast::TernaryExpr, ContextPtr); // ternary expr - RvObject evalFunctionCall(const Function &, const Ast::FunctionArguments &, const FString &, ContextPtr); // function call + RvObject executeFunction(const Function &fn, const Ast::FunctionCallArgs &, ContextPtr); // fn, fn context + + RvObject evalFunctionCall(const Ast::FunctionCall &, + ContextPtr); // function call + RvObject eval(Ast::Expression, ContextPtr); StatementResult evalBlockStatement(Ast::BlockStatement, ContextPtr); // block diff --git a/src/Evaluator/evaluator_error.hpp b/src/Evaluator/evaluator_error.hpp index a9b7f5f..11163b6 100644 --- a/src/Evaluator/evaluator_error.hpp +++ b/src/Evaluator/evaluator_error.hpp @@ -16,15 +16,20 @@ namespace Fig std::source_location loc = std::source_location::current()) { message = msg; - line = ast->getAAI().line; - column = ast->getAAI().column; + src_loc = std::move(loc); typeName = std::move(_typeName); - sourcePath = *ast->getAAI().sourcePath; - sourceLines = *ast->getAAI().sourceLines; + if (ast != nullptr) + { + line = ast->getAAI().line; + column = ast->getAAI().column; + sourcePath = *ast->getAAI().sourcePath; + sourceLines = *ast->getAAI().sourceLines; + } + } EvaluatorError(FString _typeName, std::string_view msg, diff --git a/src/Module/Library/lang/lang.fig b/src/Module/Library/lang/lang.fig index 344888e..33575bf 100644 --- a/src/Module/Library/lang/lang.fig +++ b/src/Module/Library/lang/lang.fig @@ -31,6 +31,44 @@ public interface Error getErrorMessage() -> String; } +// Operation interface + +public interface Operation +{ +// math + Add(T, T) -> T; + Sub(T, T) -> T; + Mul(T, T) -> T; + Div(T, T) -> T; + Mod(T, T) -> T; + Pow(T, T) -> T; + +// logic + Neg(T) -> T; + Not(T) -> T; + + And(T, T) -> T; + Or(T, T) -> T; + +// comparision + Equal(T, T) -> T; + NotEqual(T, T) -> T; + LessThan(T, T) -> T; + LessEqual(T, T) -> T; + GreaterThan(T, T) -> T; + GreaterEqual(T, T) -> T; + Is(T, T) -> T; + +// Bit + BitNot(T) -> T; + + BitAnd(T, T) -> T; + BitOr(T, T) -> T; + BitXor(T, T) -> T; + ShiftLeft(T, T) -> T; + ShiftRight(T, T) -> T: +} + public struct Any {} public struct Int {} public struct Null {} diff --git a/src/Module/builtins.cpp b/src/Module/builtins.cpp index 83ba1c4..a524e0b 100644 --- a/src/Module/builtins.cpp +++ b/src/Module/builtins.cpp @@ -28,6 +28,7 @@ namespace Fig::Builtins Ast::FunctionParameters({}, {}), std::make_shared(u8"String"), nullptr)}))}, + {u8"Operation", std::make_shared(InterfaceType(getOperationInterfaceTypeInfo(), {}))}, {u8"Any", std::make_shared(StructType(ValueType::Any, nullptr, {}, true))}, {u8"Int", std::make_shared(StructType(ValueType::Int, nullptr, {}, true))}, diff --git a/src/Module/builtins.hpp b/src/Module/builtins.hpp index 1fc4ec3..e8035cf 100644 --- a/src/Module/builtins.hpp +++ b/src/Module/builtins.hpp @@ -7,22 +7,15 @@ #include #include - #include #include #include - namespace Fig { namespace Builtins { - inline static const TypeInfo &getErrorInterfaceTypeInfo() - { - static const TypeInfo ErrorInterfaceTypeInfo(u8"Error", true); - return ErrorInterfaceTypeInfo; - } /* // error's interface like: interface Error @@ -33,15 +26,33 @@ namespace Fig } */ + inline static const TypeInfo &getErrorInterfaceTypeInfo() + { + static const TypeInfo ErrorInterfaceTypeInfo(u8"Error", true); + return ErrorInterfaceTypeInfo; + } + + /* + interface Operation + { + add(..., ...) -> ...; + } + + */ + + inline static const TypeInfo &getOperationInterfaceTypeInfo() + { + static const TypeInfo OperationInterfaceTypeInfo(u8"Operation", true); + return OperationInterfaceTypeInfo; + } + const std::unordered_map &getBuiltinValues(); using BuiltinFunction = std::function &)>; - const std::unordered_map &getBuiltinFunctionArgCounts(); const std::unordered_map &getBuiltinFunctions(); - inline bool isBuiltinFunction(const FString &name) { return getBuiltinFunctions().find(name) != getBuiltinFunctions().end(); diff --git a/src/Parser/parser.cpp b/src/Parser/parser.cpp index cbed366..51df3ab 100644 --- a/src/Parser/parser.cpp +++ b/src/Parser/parser.cpp @@ -1,3 +1,4 @@ +#include "Ast/Expressions/VarExpr.hpp" #include #include #include @@ -184,7 +185,7 @@ namespace Fig if (isThis(TokenType::Assign)) // = { next(); - dp.push_back({pname, {ValueType::Any.name, parseExpression(0, TokenType::Comma)}}); + dp.push_back({pname, {makeAst(u8"Any"), parseExpression(0, TokenType::Comma)}}); if (isThis(TokenType::Comma)) { next(); // only skip `,` when it's there @@ -193,13 +194,11 @@ namespace Fig else if (isThis(TokenType::Colon)) // : { next(); // skip `:` - expect(TokenType::Identifier, FString(u8"Type name")); - FString ti(currentToken().getValue()); - next(); // skip type name + Ast::Expression type_exp = parseExpression(0, TokenType::Comma, TokenType::RightParen, TokenType::Assign); if (isThis(TokenType::Assign)) // = { next(); // skip `=` - dp.push_back({pname, {ti, parseExpression(0, TokenType::Comma)}}); + dp.push_back({pname, {type_exp, parseExpression(0, TokenType::Comma)}}); if (isThis(TokenType::Comma)) { next(); // only skip `,` when it's there @@ -207,7 +206,7 @@ namespace Fig } else { - pp.push_back({pname, ti}); + pp.push_back({pname, type_exp}); if (isThis(TokenType::Comma)) { next(); // only skip `,` when it's there @@ -228,7 +227,7 @@ namespace Fig } else { - pp.push_back({pname, ValueType::Any.name}); + pp.push_back({pname, makeAst(u8"Any")}); if (isThis(TokenType::Comma)) { next(); // only skip `,` when it's there @@ -1020,14 +1019,14 @@ namespace Fig return makeAst(path); } - Ast::Expression Parser::parseExpression(Precedence bp, TokenType stop, TokenType stop2) + Ast::Expression Parser::parseExpression(Precedence bp, TokenType stop, TokenType stop2, TokenType stop3) { Ast::Expression lhs; Ast::Operator op; Token tok = currentToken(); if (tok == EOFTok) throwAddressableError(FString(u8"Unexpected end of expression")); - if (tok.getType() == stop || tok.getType() == stop2) + if (tok.getType() == stop || tok.getType() == stop2 || tok.getType() == stop3) { if (lhs == nullptr) throwAddressableError(FString(u8"Expected expression")); return lhs; @@ -1089,7 +1088,7 @@ namespace Fig while (true) { tok = currentToken(); - if (tok.getType() == stop || tok.getType() == stop2 || tok == EOFTok) break; + if (tok.getType() == stop || tok.getType() == stop2 || tok.getType() == stop3 || tok == EOFTok) break; /* Postfix */ diff --git a/src/Parser/parser.hpp b/src/Parser/parser.hpp index 5d42a5d..5df84e0 100644 --- a/src/Parser/parser.hpp +++ b/src/Parser/parser.hpp @@ -1,5 +1,6 @@ #pragma once +#include "Token/token.hpp" #include #include #include @@ -322,7 +323,7 @@ namespace Fig Ast::Import __parseImport(); // entry: current is Token::Import Ast::Statement __parseStatement(bool = true); // entry: (idk) - Ast::Expression parseExpression(Precedence, TokenType = TokenType::Semicolon, TokenType = TokenType::Semicolon); + Ast::Expression parseExpression(Precedence, TokenType = TokenType::Semicolon, TokenType = TokenType::Semicolon, TokenType = TokenType::Semicolon); std::vector parseAll(); }; }; // namespace Fig \ No newline at end of file