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