[Feat] 支持运算符重载!详见文档或 Library/lang/lang.fig中的定义。通过 impl Operation for xxx实现重载

[Impl] 函数参数指定现在也接受一个 exp,逐渐改动其他中...
This commit is contained in:
2026-02-02 16:11:08 +08:00
parent 41bff72d44
commit 01c16dee3f
20 changed files with 698 additions and 355 deletions

View File

@@ -17,8 +17,10 @@ namespace Fig::Ast
func test2(dp1 = 10, dp2:String = "default parameter 2")
*/
using PosParasType = std::vector<std::pair<FString, FString>>;
using DefParasType = std::vector<std::pair<FString, std::pair<FString, Expression>>>;
using PosParasType = std::vector<std::pair<FString, Expression>>;
// name type exp
using DefParasType = std::vector<std::pair<FString, std::pair<Expression, Expression>>>;
// 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)
{

View File

@@ -38,6 +38,7 @@ namespace Fig
{
public:
using std::u8string::u8string;
using std::u8string::operator=;
FString operator+(const FString &x)
{

View File

@@ -1,15 +1,17 @@
#pragma once
#include "Evaluator/Value/function.hpp"
#include <Ast/Statements/InterfaceDefSt.hpp>
#include <Evaluator/Value/interface.hpp>
#include <Evaluator/Value/Type.hpp>
#include <algorithm>
#include <cstddef>
#include <functional>
#include <unordered_map>
#include <iostream>
#include <memory>
#include <Ast/astBase.hpp>
#include <Ast/Statements/InterfaceDefSt.hpp>
#include <Evaluator/Value/function.hpp>
#include <Evaluator/Value/interface.hpp>
#include <Evaluator/Value/Type.hpp>
#include <Evaluator/Context/context_forward.hpp>
#include <Core/fig_string.hpp>
#include <Evaluator/Value/value.hpp>
@@ -26,6 +28,35 @@ namespace Fig
std::unordered_map<FString, Function> implMethods;
};
struct OperationRecord
{
using UnaryOpFn = std::function<ObjectPtr(const ObjectPtr &)>;
using BinaryOpFn = std::function<ObjectPtr(const ObjectPtr &, const ObjectPtr &)>;
std::unordered_map<Ast::Operator, UnaryOpFn> unOpRec;
std::unordered_map<Ast::Operator, BinaryOpFn> 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<Context>
{
private:
@@ -37,7 +68,7 @@ namespace Fig
// implRegistry <Struct, ordered list of ImplRecord>
std::unordered_map<TypeInfo, std::vector<ImplRecord>, TypeInfoHash> implRegistry;
std::unordered_map<TypeInfo, OperationRecord, TypeInfoHash> 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<OperationRecord::UnaryOpFn> 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<OperationRecord::BinaryOpFn> 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;

View File

@@ -39,27 +39,7 @@ namespace Fig
case AstType::FunctionCall: {
auto fnCall = std::static_pointer_cast<Ast::FunctionCallExpr>(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<Function>();
size_t fnId = fn.id;
// const auto &fnNameOpt = ctx->getFunctionName(fnId);
// const FString &fnName = (fnNameOpt ? *fnNameOpt :
// u8"<anonymous>");
auto fnNameOpt = ctx->getFunctionName(fnId);
if (!fnNameOpt && fn.closureContext) fnNameOpt = fn.closureContext->getFunctionName(fnId);
const FString &fnName = (fnNameOpt ? *fnNameOpt : u8"<anonymous> 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<Ast::FunctionLiteralExprAst>(exp);
@@ -84,7 +64,7 @@ namespace Fig
body = fnLiteral->getBlockBody();
}
Function fn(fnLiteral->paras, ValueType::Any, body, ctx
Function fn(FString(std::format("<LambdaFn>")),fnLiteral->paras, ValueType::Any, body, ctx
/*
pass the ctx(fnLiteral eval context) as closure context
*/

View File

@@ -1,9 +1,13 @@
#include "Ast/Expressions/BinaryExpr.hpp"
#include <Evaluator/Value/value.hpp>
#include <Evaluator/Value/LvObject.hpp>
#include <Evaluator/Value/IntPool.hpp>
#include <Evaluator/evaluator.hpp>
#include <Evaluator/evaluator_error.hpp>
#include <memory>
#include <functional>
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<ObjectPtr()> &rollback) {
if (lhs->is<StructInstance>() && 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<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() + rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(*lhs + *rhs);
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() + rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(*lhs + *rhs);
});
}
case Operator::Subtract: {
ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx);
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() - rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(*lhs - *rhs);
};
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() - rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(*lhs - *rhs);
});
}
case Operator::Multiply: {
ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx);
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() * rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>((*lhs) * (*rhs));
};
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() * rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(*lhs * *rhs);
});
}
case Operator::Divide: {
ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx);
return std::make_shared<Object>(*lhs / *rhs);
};
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs / *rhs); });
}
case Operator::Modulo: {
ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx);
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass lv = lhs->as<ValueType::IntClass>();
ValueType::IntClass rv = rhs->as<ValueType::IntClass>();
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<Object>(*lhs % *rhs);
});
}
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass lv = lhs->as<ValueType::IntClass>();
ValueType::IntClass rv = lhs->as<ValueType::IntClass>();
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<Object>(*lhs % *rhs);
};
case Operator::Power: {
ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx);
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result =
std::pow(lhs->as<ValueType::IntClass>(), rhs->as<ValueType::IntClass>());
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(power(*lhs, *rhs));
}
case Operator::And: {
ObjectPtr lhs = eval(lexp, ctx);
if (lhs->is<bool>() && !isBoolObjectTruthy(lhs))
{
return Object::getFalseInstance(); // short-circuit
}
ObjectPtr rhs = eval(rexp, ctx);
return std::make_shared<Object>(*lhs && *rhs);
};
case Operator::Or: {
ObjectPtr lhs = eval(lexp, ctx);
if (lhs->is<bool>() && isBoolObjectTruthy(lhs))
{
return Object::getTrueInstance();
}
ObjectPtr rhs = eval(rexp, ctx);
return std::make_shared<Object>(*lhs || *rhs);
};
case Operator::Equal: {
ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx);
return std::make_shared<Object>(*lhs == *rhs);
}
case Operator::NotEqual: {
ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx);
return std::make_shared<Object>(*lhs != *rhs);
}
case Operator::Less: {
ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx);
return std::make_shared<Object>(*lhs < *rhs);
}
case Operator::LessEqual: {
ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx);
return std::make_shared<Object>(*lhs <= *rhs);
}
case Operator::Greater: {
ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx);
return std::make_shared<Object>(*lhs > *rhs);
}
case Operator::GreaterEqual: {
ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx);
return std::make_shared<Object>(*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<StructInstance>() && rhs->is<StructType>())
{
const StructInstance &si = lhs->as<StructInstance>();
const StructType &st = rhs->as<StructType>();
return std::make_shared<Object>(si.parentType == st.type);
}
if (lhs->is<StructInstance>() && rhs->is<InterfaceType>())
{
const StructInstance &si = lhs->as<StructInstance>();
const InterfaceType &it = rhs->as<InterfaceType>();
return std::make_shared<Object>(implements(si.parentType, it.type, ctx));
}
if (lhs->is<StructInstance>() && rhs->is<StructType>())
{
const StructInstance &si = lhs->as<StructInstance>();
const StructType &st = rhs->as<StructType>();
return std::make_shared<Object>(si.parentType == st.type);
}
if (lhs->is<StructInstance>() && rhs->is<InterfaceType>())
{
const StructInstance &si = lhs->as<StructInstance>();
const InterfaceType &it = rhs->as<InterfaceType>();
return std::make_shared<Object>(implements(si.parentType, it.type, ctx));
}
if (ValueType::isTypeBuiltin(lhsType) && rhsType == ValueType::StructType)
{
const StructType &st = rhs->as<StructType>();
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<StructType>();
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<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() & rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(bit_and(*lhs, *rhs));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() & rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(bit_and(*lhs, *rhs));
});
}
case Operator::BitOr: {
ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx);
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() | rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(bit_or(*lhs, *rhs));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() | rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(bit_or(*lhs, *rhs));
});
}
case Operator::BitXor: {
ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx);
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() ^ rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(bit_xor(*lhs, *rhs));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() ^ rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(bit_xor(*lhs, *rhs));
});
}
case Operator::ShiftLeft: {
ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx);
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() << rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(shift_left(*lhs, *rhs));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() << rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(shift_left(*lhs, *rhs));
});
}
case Operator::ShiftRight: {
ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx);
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() >> rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(shift_right(*lhs, *rhs));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() >> rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(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<bool>() && !isBoolObjectTruthy(lhs)) { return Object::getFalseInstance(); }
ObjectPtr rhs = eval(rexp, ctx);
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs && *rhs); });
}
case Operator::Or: {
ObjectPtr lhs = eval(lexp, ctx);
if (lhs->is<bool>() && isBoolObjectTruthy(lhs)) { return Object::getTrueInstance(); }
ObjectPtr rhs = eval(rexp, ctx);
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*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<Object>(*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<Object>(*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<Object>(*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<Object>(*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<Object>(*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<Object>(*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<Object>(*(lv.get()) + *rhs));
const ObjectPtr &result =
tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*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<Object>(*(lv.get()) - *rhs));
const ObjectPtr &result =
tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*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<Object>(*(lv.get()) * (*rhs)));
const ObjectPtr &result =
tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*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<Object>(*(lv.get()) / *rhs));
const ObjectPtr &result =
tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*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<Object>(*(lv.get()) / *rhs));
const ObjectPtr &result =
tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*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<Object>(
// *(lv.get()) ^ *rhs));
// }
default:
throw EvaluatorError(u8"UnsupportedOp",
std::format("Unsupport operator '{}' for binary", magic_enum::enum_name(op)),
bin);
}
}
};
}; // namespace Fig

View File

@@ -1,3 +1,6 @@
#include "Ast/functionParameters.hpp"
#include "Evaluator/Value/value.hpp"
#include <Ast/Expressions/FunctionCall.hpp>
#include <Evaluator/Value/function.hpp>
#include <Evaluator/Value/LvObject.hpp>
#include <Evaluator/evaluator.hpp>
@@ -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<Function>();
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<Context>(FString(std::format("<Function {}()>", 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;
}

View File

@@ -1,3 +1,4 @@
#include "Evaluator/Value/value.hpp"
#include <Evaluator/Value/LvObject.hpp>
#include <Evaluator/evaluator.hpp>
#include <Evaluator/evaluator_error.hpp>
@@ -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<Object>(Function(fn.paras, fn.retType, fn.body, instanceCtx)));
std::make_shared<Object>(Function(funcName, fn.paras, fn.retType, fn.body, instanceCtx)));
}
return std::make_shared<Object>(StructInstance(structT.type, instanceCtx));

View File

@@ -1,3 +1,4 @@
#include "Evaluator/Value/value.hpp"
#include <Evaluator/Value/LvObject.hpp>
#include <Evaluator/evaluator.hpp>
#include <Evaluator/evaluator_error.hpp>
@@ -36,6 +37,7 @@ namespace Fig
return LvObject(std::make_shared<VariableSlot>(
member,
std::make_shared<Object>(Function(
member,
[baseVal, member](ObjectPtr self, std::vector<ObjectPtr> 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<VariableSlot>(
member, std::make_shared<Object>(fn), ValueType::Function, AccessModifier::PublicConst),

View File

@@ -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 <Evaluator/Value/LvObject.hpp>
#include <Evaluator/evaluator.hpp>
#include <Evaluator/evaluator_error.hpp>
#include <Utils/utils.hpp>
#include <unordered_map>
namespace Fig
{
@@ -18,7 +26,6 @@ namespace Fig
case VarDefSt: {
auto varDef = std::static_pointer_cast<Ast::VarDefAst>(stmt);
if (ctx->containsInThisScope(varDef->name))
{
throw EvaluatorError(
@@ -63,7 +70,6 @@ namespace Fig
case FunctionDefSt: {
auto fnDef = std::static_pointer_cast<Ast::FunctionDefSt>(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),
@@ -90,7 +96,6 @@ namespace Fig
case StructSt: {
auto stDef = std::static_pointer_cast<Ast::StructDefSt>(stmt);
if (ctx->containsInThisScope(stDef->name))
{
throw EvaluatorError(
@@ -149,7 +154,6 @@ namespace Fig
case InterfaceDefSt: {
auto ifd = std::static_pointer_cast<Ast::InterfaceDefAst>(stmt);
const FString &interfaceName = ifd->name;
if (ctx->containsInThisScope(interfaceName))
@@ -170,7 +174,6 @@ namespace Fig
case ImplementSt: {
auto ip = std::static_pointer_cast<Ast::ImplementAst>(stmt);
TypeInfo structType(ip->structName);
TypeInfo interfaceType(ip->interfaceName);
if (ctx->hasImplRegisted(structType, interfaceType))
@@ -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<FString, std::pair<Ast::Operator, size_t>> 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<Context>(
FString(std::format("<Function {}>", opFnName.toBasicString())), ctx);
const auto &fillOpFnParas = [this, structType, implMethod, opFnName, fnCtx, ctx, paraCnt](
const std::vector<ObjectPtr> &args) {
const Ast::FunctionParameters &paras = implMethod.paras;
for (size_t i = 0; i < paraCnt; ++i)
{
const TypeInfo &paraType = 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<InterfaceType>();
// ===== 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)
@@ -407,7 +551,6 @@ namespace Fig
case TrySt: {
auto tryst = std::static_pointer_cast<Ast::TrySt>(stmt);
ContextPtr tryCtx = std::make_shared<Context>(
FString(std::format("<Try at {}:{}>", tryst->getAAI().line, tryst->getAAI().column)), ctx);
StatementResult sr = StatementResult::normal();
@@ -446,7 +589,6 @@ namespace Fig
case ThrowSt: {
auto ts = std::static_pointer_cast<Ast::ThrowSt>(stmt);
ObjectPtr value = eval(ts->value, ctx);
if (value->is<ValueType::NullClass>())
{
@@ -458,7 +600,6 @@ namespace Fig
case ReturnSt: {
auto returnSt = std::static_pointer_cast<Ast::ReturnSt>(stmt);
ObjectPtr returnValue = Object::getNullInstance(); // default is null
if (returnSt->retValue) returnValue = eval(returnSt->retValue, ctx);
return StatementResult::returnFlow(returnValue);
@@ -496,7 +637,6 @@ namespace Fig
case BlockStatement: {
auto block = std::static_pointer_cast<Ast::BlockStatementAst>(stmt);
ContextPtr blockCtx = std::make_shared<Context>(
FString(std::format("<Block at {}:{}>", block->getAAI().line, block->getAAI().column)), ctx);
return evalBlockStatement(block, blockCtx);
@@ -507,4 +647,4 @@ namespace Fig
FString(std::format("Feature stmt {} unsupported yet", magic_enum::enum_name(stmt->getType()))));
}
}
};
}; // namespace Fig

View File

@@ -1,3 +1,4 @@
#include "Evaluator/Value/value.hpp"
#include <Evaluator/Value/LvObject.hpp>
#include <Evaluator/evaluator.hpp>
#include <Evaluator/evaluator_error.hpp>
@@ -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<ObjectPtr()> &rollback) {
if (rhs->is<StructInstance>())
{
// 运算符重载
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<Object>(!(*value));
return tryInvokeOverloadFn(value, [value]() { return std::make_shared<Object>(!(*value)); });
}
case Operator::Subtract: {
return std::make_shared<Object>(-(*value));
return tryInvokeOverloadFn(value, [value]() { return std::make_shared<Object>(-(*value)); });
}
case Operator::BitNot: {
return std::make_shared<Object>(bit_not((*value)));
return tryInvokeOverloadFn(value, [value]() { return std::make_shared<Object>(bit_not(*value)); });
}
default: {
throw EvaluatorError(u8"UnsupportedOpError",
@@ -28,4 +44,4 @@ namespace Fig
}
}
}
};
}; // namespace Fig

View File

@@ -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<std::shared_ptr<Object>(const std::vector<std::shared_ptr<Object>> &)> fn, int argc) :
id(nextId()), type(Builtin), builtin(fn), builtinParamCount(argc)
Function(const FString &_name, std::function<std::shared_ptr<Object>(const std::vector<std::shared_ptr<Object>> &)> fn, int argc) :
id(nextId()), name(_name), type(Builtin), builtin(fn), builtinParamCount(argc)
{
type = Builtin;
}
Function(std::function<std::shared_ptr<Object>(std::shared_ptr<Object>,
Function(const FString &_name, std::function<std::shared_ptr<Object>(std::shared_ptr<Object>,
const std::vector<std::shared_ptr<Object>> &)> 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;

View File

@@ -1,3 +1,4 @@
#include <Evaluator/Value/interface.hpp>
#include <Evaluator/Value/structType.hpp>
#include <Evaluator/Value/value_forward.hpp>
#include <Evaluator/Value/Type.hpp>
@@ -84,6 +85,11 @@ namespace Fig
return obj->as<StructType>().type;
}
if (t == ValueType::InterfaceType)
{
return obj->as<InterfaceType>().type;
}
if (t == ValueType::StructInstance) return obj->as<StructInstance>().parentType;
return t;
}

View File

@@ -12,6 +12,7 @@
#include <cassert>
#include <iostream>
#include <memory>
#include <unordered_set>
#include <variant>
#include <cmath>
#include <string>
@@ -87,9 +88,8 @@ namespace Fig
Module,
InterfaceType>;
static std::unordered_map<TypeInfo, std::unordered_map<FString, BuiltinTypeMemberFn>, TypeInfoHash> getMemberTypeFunctions()
static std::unordered_map<TypeInfo, std::unordered_map<FString, BuiltinTypeMemberFn>, TypeInfoHash>
getMemberTypeFunctions()
{
static const std::unordered_map<TypeInfo, std::unordered_map<FString, BuiltinTypeMemberFn>, TypeInfoHash>
memberTypeFunctions{
@@ -241,9 +241,8 @@ namespace Fig
return memberTypeFunctions;
}
static std::unordered_map<TypeInfo, std::unordered_map<FString, int>, TypeInfoHash> getMemberTypeFunctionsParas()
static std::unordered_map<TypeInfo, std::unordered_map<FString, int>, TypeInfoHash>
getMemberTypeFunctionsParas()
{
static const std::unordered_map<TypeInfo, std::unordered_map<FString, int>, TypeInfoHash>
memberTypeFunctionsParas{
@@ -425,7 +424,7 @@ namespace Fig
return toString();
}
FString toString() const
FString toString(std::unordered_set<const Object *> &visited) const
{
if (is<ValueType::NullClass>()) return FString(u8"null");
if (is<ValueType::IntClass>()) return FString(std::to_string(as<ValueType::IntClass>()));
@@ -445,13 +444,16 @@ namespace Fig
static_cast<const void *>(&as<StructInstance>())));
if (is<List>())
{
if (visited.contains(this)) { return u8"[...]"; }
visited.insert(this);
FString output(u8"[");
const List &list = as<List>();
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<Map>())
{
if (visited.contains(this)) { return u8"{...}"; }
visited.insert(this);
FString output(u8"{");
const Map &map = as<Map>();
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"<error>");
}
FString toString() const
{
std::unordered_set<const Object *> 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<ValueType::BoolClass>())
throw ValueError(
throw ValueError(
FString(std::format("Logical NOT requires bool: '{}'", v.getTypeInfo().name.toBasicString())));
return Object(!v.as<ValueType::BoolClass>());
}

View File

@@ -1,4 +1,5 @@
#pragma once
#include <Ast/Expressions/FunctionCall.hpp>
#include <Ast/Expressions/InitExpr.hpp>
#include <Ast/Statements/ImplementSt.hpp>
#include <Ast/Statements/InterfaceDefSt.hpp>
@@ -24,15 +25,9 @@ namespace Fig
FString sourcePath;
std::vector<FString> sourceLines;
void SetSourcePath(const FString &sp)
{
sourcePath = sp;
}
void SetSourcePath(const FString &sp) { sourcePath = sp; }
void SetSourceLines(const std::vector<FString> &sl)
{
sourceLines = sl;
}
void SetSourceLines(const std::vector<FString> &sl) { sourceLines = sl; }
void SetGlobalContext(ContextPtr ctx)
{
@@ -40,11 +35,7 @@ namespace Fig
global = ctx;
}
void CreateGlobalContext()
{
global = std::make_shared<Context>(
FString(u8"<Global>"));
}
void CreateGlobalContext() { global = std::make_shared<Context>(FString(u8"<Global>")); }
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<Object>(f));
Function f(name, fn, argc);
global->def(name, ValueType::Function, AccessModifier::Const, std::make_shared<Object>(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

View File

@@ -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,

View File

@@ -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 {}

View File

@@ -28,6 +28,7 @@ namespace Fig::Builtins
Ast::FunctionParameters({}, {}),
std::make_shared<Ast::VarExprAst>(u8"String"),
nullptr)}))},
{u8"Operation", std::make_shared<Object>(InterfaceType(getOperationInterfaceTypeInfo(), {}))},
{u8"Any", std::make_shared<Object>(StructType(ValueType::Any, nullptr, {}, true))},
{u8"Int", std::make_shared<Object>(StructType(ValueType::Int, nullptr, {}, true))},

View File

@@ -7,22 +7,15 @@
#include <Evaluator/Value/value.hpp>
#include <Core/runtimeTime.hpp>
#include <unordered_map>
#include <functional>
#include <vector>
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<FString, ObjectPtr> &getBuiltinValues();
using BuiltinFunction = std::function<ObjectPtr(const std::vector<ObjectPtr> &)>;
const std::unordered_map<FString, int> &getBuiltinFunctionArgCounts();
const std::unordered_map<FString, BuiltinFunction> &getBuiltinFunctions();
inline bool isBuiltinFunction(const FString &name)
{
return getBuiltinFunctions().find(name) != getBuiltinFunctions().end();

View File

@@ -1,3 +1,4 @@
#include "Ast/Expressions/VarExpr.hpp"
#include <Ast/Statements/ErrorFlow.hpp>
#include <Ast/Statements/ImplementSt.hpp>
#include <Ast/astBase.hpp>
@@ -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<Ast::VarExprAst>(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<Ast::VarExprAst>(u8"Any")});
if (isThis(TokenType::Comma))
{
next(); // only skip `,` when it's there
@@ -1020,14 +1019,14 @@ namespace Fig
return makeAst<Ast::ImportSt>(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<SyntaxError>(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<SyntaxError>(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 */

View File

@@ -1,5 +1,6 @@
#pragma once
#include "Token/token.hpp"
#include <Ast/astBase.hpp>
#include <Ast/ast.hpp>
#include <Lexer/lexer.hpp>
@@ -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<Ast::AstBase> parseAll();
};
}; // namespace Fig