forked from PuqiAR/Fig-TreeWalker
挺大的改动。增加 as运算符,转换不了抛出 TypeError。import语法更新。修复try一点错误。现在表达式运算返回ExprResult。通过3个宏实现简便错误传播与解包 unwrap
This commit is contained in:
@@ -1,23 +1,27 @@
|
||||
#include <Ast/Expressions/BinaryExpr.hpp>
|
||||
#include <Evaluator/Value/value.hpp>
|
||||
#include <Evaluator/Value/Type.hpp>
|
||||
#include <Evaluator/Value/Type.hpp>
|
||||
#include <Evaluator/Value/LvObject.hpp>
|
||||
#include <Evaluator/Value/IntPool.hpp>
|
||||
#include <Evaluator/evaluator.hpp>
|
||||
#include <Evaluator/evaluator_error.hpp>
|
||||
#include <Evaluator/Core/ExprResult.hpp>
|
||||
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
RvObject Evaluator::evalBinary(Ast::BinaryExpr bin, ContextPtr ctx)
|
||||
ExprResult Evaluator::evalBinary(Ast::BinaryExpr bin, ContextPtr ctx)
|
||||
{
|
||||
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) {
|
||||
[ctx, op](const ObjectPtr &lhs, const ObjectPtr &rhs, const std::function<ExprResult()> &rollback) {
|
||||
if (lhs->is<StructInstance>() && lhs->getTypeInfo() == rhs->getTypeInfo())
|
||||
{
|
||||
// 运算符重载
|
||||
@@ -34,8 +38,8 @@ namespace Fig
|
||||
switch (op)
|
||||
{
|
||||
case Operator::Add: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
{
|
||||
@@ -46,8 +50,8 @@ namespace Fig
|
||||
});
|
||||
}
|
||||
case Operator::Subtract: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
{
|
||||
@@ -58,8 +62,8 @@ namespace Fig
|
||||
});
|
||||
}
|
||||
case Operator::Multiply: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
{
|
||||
@@ -70,13 +74,13 @@ namespace Fig
|
||||
});
|
||||
}
|
||||
case Operator::Divide: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
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);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
{
|
||||
@@ -93,8 +97,8 @@ namespace Fig
|
||||
}
|
||||
|
||||
case Operator::Is: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
|
||||
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs, ctx, bin]() {
|
||||
const TypeInfo &lhsType = lhs->getTypeInfo();
|
||||
@@ -143,9 +147,114 @@ namespace Fig
|
||||
});
|
||||
}
|
||||
|
||||
case Operator::As: {
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
|
||||
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs, ctx, bin, this]() -> ExprResult {
|
||||
if (!rhs->is<StructType>())
|
||||
{
|
||||
throw EvaluatorError(
|
||||
u8"OperatorError",
|
||||
std::format("Operator `as` requires right hand side operand a struct type, but got '{}'",
|
||||
prettyType(rhs).toBasicString()),
|
||||
bin->rexp);
|
||||
}
|
||||
const StructType &targetStructType = rhs->as<StructType>();
|
||||
const TypeInfo &targetType = targetStructType.type;
|
||||
const TypeInfo &sourceType = lhs->getTypeInfo();
|
||||
if (targetType == sourceType) { return lhs; }
|
||||
if (targetType == ValueType::String) { return std::make_shared<Object>(lhs->toStringIO()); }
|
||||
if (sourceType == ValueType::Int)
|
||||
{
|
||||
if (targetType == ValueType::Double)
|
||||
{
|
||||
return std::make_shared<Object>(
|
||||
static_cast<ValueType::DoubleClass>(lhs->as<ValueType::IntClass>()));
|
||||
}
|
||||
}
|
||||
else if (sourceType == ValueType::Double)
|
||||
{
|
||||
if (targetType == ValueType::Int)
|
||||
{
|
||||
return IntPool::getInstance().createInt(
|
||||
static_cast<ValueType::IntClass>(lhs->as<ValueType::DoubleClass>()));
|
||||
}
|
||||
}
|
||||
else if (sourceType == ValueType::String)
|
||||
{
|
||||
const FString &str = lhs->as<ValueType::StringClass>();
|
||||
if (targetType == ValueType::Int)
|
||||
{
|
||||
try
|
||||
{
|
||||
return IntPool::getInstance().createInt(
|
||||
static_cast<ValueType::IntClass>(std::stoll(str.toBasicString())));
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
return ExprResult::error(
|
||||
genTypeError(FString(std::format("Cannot cast type `{}` to `{}`, bad int string {}",
|
||||
prettyType(lhs).toBasicString(),
|
||||
prettyType(rhs).toBasicString(),
|
||||
str.toBasicString())),
|
||||
bin->rexp,
|
||||
ctx));
|
||||
}
|
||||
}
|
||||
if (targetType == ValueType::Double)
|
||||
{
|
||||
try
|
||||
{
|
||||
return std::make_shared<Object>(std::stod(str.toBasicString()));
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
return ExprResult::error(genTypeError(
|
||||
FString(std::format("Cannot cast type `{}` to `{}`, bad double string {}",
|
||||
prettyType(lhs).toBasicString(),
|
||||
prettyType(rhs).toBasicString(),
|
||||
str.toBasicString())),
|
||||
bin->rexp,
|
||||
ctx));
|
||||
}
|
||||
}
|
||||
if (targetType == ValueType::Bool)
|
||||
{
|
||||
if (str == u8"true") { return Object::getTrueInstance(); }
|
||||
else if (str == u8"false") { return Object::getFalseInstance(); }
|
||||
return ExprResult::error(
|
||||
genTypeError(FString(std::format("Cannot cast type `{}` to `{}`, bad bool string {}",
|
||||
prettyType(lhs).toBasicString(),
|
||||
prettyType(rhs).toBasicString(),
|
||||
str.toBasicString())),
|
||||
bin->rexp,
|
||||
ctx));
|
||||
}
|
||||
}
|
||||
else if (sourceType == ValueType::Bool)
|
||||
{
|
||||
if (targetType == ValueType::Int)
|
||||
{
|
||||
return IntPool::getInstance().createInt(static_cast<ValueType::IntClass>(lhs->as<ValueType::BoolClass>()));
|
||||
}
|
||||
if (targetType == ValueType::Double)
|
||||
{
|
||||
return std::make_shared<Object>(static_cast<ValueType::DoubleClass>(lhs->as<ValueType::BoolClass>()));
|
||||
}
|
||||
}
|
||||
|
||||
return ExprResult::error(genTypeError(FString(std::format("Cannot cast type `{}` to `{}`",
|
||||
prettyType(lhs).toBasicString(),
|
||||
prettyType(rhs).toBasicString())),
|
||||
bin->rexp,
|
||||
ctx));
|
||||
});
|
||||
}
|
||||
|
||||
case Operator::BitAnd: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
|
||||
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
@@ -156,9 +265,10 @@ namespace Fig
|
||||
return std::make_shared<Object>(bit_and(*lhs, *rhs));
|
||||
});
|
||||
}
|
||||
|
||||
case Operator::BitOr: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
|
||||
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
@@ -169,9 +279,10 @@ namespace Fig
|
||||
return std::make_shared<Object>(bit_or(*lhs, *rhs));
|
||||
});
|
||||
}
|
||||
|
||||
case Operator::BitXor: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
|
||||
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
@@ -182,9 +293,10 @@ namespace Fig
|
||||
return std::make_shared<Object>(bit_xor(*lhs, *rhs));
|
||||
});
|
||||
}
|
||||
|
||||
case Operator::ShiftLeft: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
|
||||
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
@@ -196,8 +308,8 @@ namespace Fig
|
||||
});
|
||||
}
|
||||
case Operator::ShiftRight: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
|
||||
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
@@ -210,99 +322,112 @@ namespace Fig
|
||||
}
|
||||
|
||||
case Operator::Assign: {
|
||||
LvObject lv = evalLv(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
LvObject lv = check_unwrap_lv(evalLv(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
lv.set(rhs);
|
||||
return rhs;
|
||||
}
|
||||
|
||||
case Operator::And: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
if (lhs->is<bool>() && !isBoolObjectTruthy(lhs)) { return Object::getFalseInstance(); }
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs && *rhs); });
|
||||
}
|
||||
|
||||
case Operator::Or: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
if (lhs->is<bool>() && isBoolObjectTruthy(lhs)) { return Object::getTrueInstance(); }
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
ObjectPtr rhs = check_unwrap(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);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(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);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(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);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(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);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(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);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(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);
|
||||
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs >= *rhs); });
|
||||
}
|
||||
|
||||
case Operator::PlusAssign: {
|
||||
LvObject lv = evalLv(lexp, ctx);
|
||||
LvObject lv = check_unwrap_lv(evalLv(lexp, ctx));
|
||||
const ObjectPtr &lhs = lv.get();
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
const ObjectPtr &result =
|
||||
tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs + *rhs); });
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
const ObjectPtr &result = check_unwrap(
|
||||
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);
|
||||
LvObject lv = check_unwrap_lv(evalLv(lexp, ctx));
|
||||
const ObjectPtr &lhs = lv.get();
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
const ObjectPtr &result =
|
||||
tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs - *rhs); });
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
const ObjectPtr &result = check_unwrap(
|
||||
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);
|
||||
LvObject lv = check_unwrap_lv(evalLv(lexp, ctx));
|
||||
const ObjectPtr &lhs = lv.get();
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
const ObjectPtr &result =
|
||||
tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs * *rhs); });
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
const ObjectPtr &result = check_unwrap(
|
||||
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);
|
||||
LvObject lv = check_unwrap_lv(evalLv(lexp, ctx));
|
||||
const ObjectPtr &lhs = lv.get();
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
const ObjectPtr &result =
|
||||
tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs / *rhs); });
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
const ObjectPtr &result = check_unwrap(
|
||||
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);
|
||||
LvObject lv = check_unwrap_lv(evalLv(lexp, ctx));
|
||||
const ObjectPtr &lhs = lv.get();
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
const ObjectPtr &result =
|
||||
tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs % *rhs); });
|
||||
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
|
||||
const ObjectPtr &result = check_unwrap(
|
||||
tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs % *rhs); }));
|
||||
lv.set(result);
|
||||
return rhs;
|
||||
}
|
||||
|
||||
default:
|
||||
throw EvaluatorError(u8"UnsupportedOp",
|
||||
std::format("Unsupport operator '{}' for binary", magic_enum::enum_name(op)),
|
||||
|
||||
Reference in New Issue
Block a user