#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Fig { bool Evaluator::isInterfaceSignatureMatch(const Ast::ImplementMethod &implMethod, const Ast::InterfaceMethod &ifMethod) { 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)) { return LvObject(std::make_shared( member, std::make_shared(ctx->getDefaultImplementedMethod(si.parentType, member)), 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::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); return std::make_shared(*lhs + *rhs); } case Operator::Subtract: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); return std::make_shared(*lhs - *rhs); }; case Operator::Multiply: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); 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); return std::make_shared(*lhs % *rhs); }; case Operator::Power: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); 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 '{}' && '{}'", lhsType.toString().toBasicString(), rhsType.toString().toBasicString()), bin->lexp); } case Operator::BitAnd: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); return std::make_shared(bit_and(*lhs, *rhs)); } case Operator::BitOr: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); return std::make_shared(bit_or(*lhs, *rhs)); } case Operator::BitXor: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); return std::make_shared(bit_xor(*lhs, *rhs)); } case Operator::ShiftLeft: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); return std::make_shared(shift_left(*lhs, *rhs)); } case Operator::ShiftRight: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); 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); 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; 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) { 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 { // named / shorthand init 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); } } } instanceCtx->merge(*structT.defContext); for (auto &[id, fn] : instanceCtx->getFunctions()) { instanceCtx->_update(*instanceCtx->getFunctionName(id), std::make_shared(Function(fn.paras, fn.retType, fn.body, instanceCtx) // change its closureContext to // struct instance's context )); } return std::make_shared(StructInstance(structT.type, instanceCtx)); } 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(); for (const Ast::Statement &stmt : block->stmts) { sr = evalStatement(stmt, ctx); if (!sr.isNormal()) { return sr; } } 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 FString &declaredTypeName = varDef->typeName; if (declaredTypeName == Parser::varDefTypeFollowed) { declaredType = value->getTypeInfo(); } else if (!declaredTypeName.empty()) { declaredType = TypeInfo(declaredTypeName); if (value != nullptr && !isTypeMatch(declaredType, value, ctx)) { throw EvaluatorError(u8"TypeError", std::format("Variable `{}` expects init-value type `{}`, but got '{}'", varDef->name.toBasicString(), declaredTypeName.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); } Function fn(fnDef->paras, TypeInfo(fnDef->retType), 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); } fields.push_back(Field(field.am, field.fieldName, TypeInfo(field.tiName), 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); record.implMethods[name] = Function(implMethod.paras, TypeInfo(ifMethod.returnType), 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)); } 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) { std::ifstream file(path); assert(file.is_open()); std::string source((std::istreambuf_iterator(file)), std::istreambuf_iterator()); file.close(); Lexer lexer((FString(source))); Parser parser(lexer); std::vector asts; std::vector sourceLines = Utils::splitSource(FString(source)); asts = parser.parseAll(); Evaluator evaluator; evaluator.SetSourcePath(FString(path.string())); ContextPtr modctx = std::make_shared(FString(std::format("", path.string())), nullptr); evaluator.SetGlobalContext(modctx); evaluator.RegisterBuiltinsValue(); evaluator.Run(asts); return evaluator.global; } StatementResult Evaluator::evalImportSt(Ast::Import i, ContextPtr ctx) { const std::vector &pathVec = i->path; const FString &modName = pathVec.at(pathVec.size() - 1); // pathVec at least has 1 element if (modName == u8"_builtins") { RegisterBuiltins(); return StatementResult::normal(); } auto path = resolveModulePath(pathVec); ContextPtr modCtx = loadModule(path); // std::cerr << modName.toBasicString() << '\n'; DEBUG if (ctx->containsInThisScope(modName)) { throw EvaluatorError( u8"RedeclarationError", std::format("{} has already been declared.", modName.toBasicString()), i); } ctx->def( modName, ValueType::Module, AccessModifier::PublicConst, std::make_shared(Module(modName, modCtx))); return StatementResult::normal(); } StatementResult Evaluator::Run(std::vector asts) { using Ast::AstType; StatementResult sr = StatementResult::normal(); for (auto &ast : asts) { Ast::Expression exp; if ((exp = std::dynamic_pointer_cast(ast))) // 保持 dynamic_pointer_cast ! { sr = StatementResult::normal(eval(exp, global)); } else { // statement Ast::Statement stmt = std::static_pointer_cast(ast); assert(stmt != nullptr); sr = evalStatement(stmt, global); if (sr.isError()) { throw EvaluatorError(u8"UncaughtExceptionError", std::format("Uncaught exception: {}", sr.result->toString().toBasicString()), stmt); } if (!sr.isNormal()) { return sr; } } } return sr; } void Evaluator::printStackTrace() { if (global) global->printStackTrace(); } }; // namespace Fig