回档之后的重写。部分问题修复。添加了什么我也忘了
This commit is contained in:
510
src/Evaluator/Core/EvalStatement.cpp
Normal file
510
src/Evaluator/Core/EvalStatement.cpp
Normal file
@@ -0,0 +1,510 @@
|
||||
#include <Evaluator/Value/LvObject.hpp>
|
||||
#include <Evaluator/evaluator.hpp>
|
||||
#include <Evaluator/evaluator_error.hpp>
|
||||
|
||||
#include <Utils/utils.hpp>
|
||||
|
||||
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<Ast::ImportSt>(stmt);
|
||||
return evalImportSt(i, ctx);
|
||||
}
|
||||
case VarDefSt: {
|
||||
auto varDef = std::static_pointer_cast<Ast::VarDefAst>(stmt);
|
||||
|
||||
|
||||
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>(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<Ast::FunctionDefSt>(stmt);
|
||||
|
||||
|
||||
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<Object>(fn));
|
||||
return StatementResult::normal();
|
||||
}
|
||||
|
||||
case StructSt: {
|
||||
auto stDef = std::static_pointer_cast<Ast::StructDefSt>(stmt);
|
||||
|
||||
|
||||
if (ctx->containsInThisScope(stDef->name))
|
||||
{
|
||||
throw EvaluatorError(
|
||||
u8"RedeclarationError",
|
||||
std::format("Structure '{}' already defined in this scope", stDef->name.toBasicString()),
|
||||
stDef);
|
||||
}
|
||||
std::vector<Field> fields;
|
||||
std::vector<FString> _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<Context>(FString(std::format("<Struct {} at {}:{}>",
|
||||
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<Object>(StructType(type, defContext, fields)));
|
||||
return StatementResult::normal();
|
||||
}
|
||||
|
||||
case InterfaceDefSt: {
|
||||
auto ifd = std::static_pointer_cast<Ast::InterfaceDefAst>(stmt);
|
||||
|
||||
|
||||
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<Object>(InterfaceType(type, ifd->methods)));
|
||||
return StatementResult::normal();
|
||||
}
|
||||
|
||||
case ImplementSt: {
|
||||
auto ip = std::static_pointer_cast<Ast::ImplementAst>(stmt);
|
||||
|
||||
|
||||
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<InterfaceType>())
|
||||
{
|
||||
throw EvaluatorError(
|
||||
u8"NotAInterfaceError",
|
||||
std::format("Variable `{}` is not a interface", ip->interfaceName.toBasicString()),
|
||||
ip);
|
||||
}
|
||||
if (!structTypeObj->is<StructType>())
|
||||
{
|
||||
throw EvaluatorError(
|
||||
u8"NotAStructType",
|
||||
std::format("Variable `{}` is not a struct type", ip->structName.toBasicString()),
|
||||
ip);
|
||||
}
|
||||
auto &implementMethods = ip->methods;
|
||||
InterfaceType &interface = interfaceObj->as<InterfaceType>();
|
||||
|
||||
// ===== interface implementation validation =====
|
||||
ImplRecord record{interfaceType, structType, {}};
|
||||
|
||||
std::unordered_map<FString, Ast::InterfaceMethod> 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<FString> 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<Ast::IfSt>(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<ValueType::BoolClass>()) { 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<ValueType::BoolClass>()) { 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<Ast::WhileSt>(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<ValueType::BoolClass>()) { break; }
|
||||
ContextPtr loopContext = std::make_shared<Context>(
|
||||
FString(std::format("<While {}:{}>", 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<Ast::ForSt>(stmt);
|
||||
ContextPtr loopContext = std::make_shared<Context>(
|
||||
FString(std::format("<For {}:{}>", 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<ValueType::BoolClass>()) { break; }
|
||||
iteration++;
|
||||
ContextPtr iterationContext = std::make_shared<Context>(
|
||||
FString(std::format(
|
||||
"<For {}:{}, Iteration {}>", 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<Ast::TrySt>(stmt);
|
||||
|
||||
|
||||
ContextPtr tryCtx = std::make_shared<Context>(
|
||||
FString(std::format("<Try at {}:{}>", tryst->getAAI().line, tryst->getAAI().column)), ctx);
|
||||
StatementResult sr = StatementResult::normal();
|
||||
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<Context>(
|
||||
FString(
|
||||
std::format("<Catch at {}:{}>", 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<Ast::ThrowSt>(stmt);
|
||||
|
||||
|
||||
ObjectPtr value = eval(ts->value, ctx);
|
||||
if (value->is<ValueType::NullClass>())
|
||||
{
|
||||
throw EvaluatorError(u8"TypeError", u8"Why did you throw a null?", ts);
|
||||
}
|
||||
return StatementResult::errorFlow(value);
|
||||
}
|
||||
|
||||
case ReturnSt: {
|
||||
auto returnSt = std::static_pointer_cast<Ast::ReturnSt>(stmt);
|
||||
|
||||
|
||||
ObjectPtr returnValue = Object::getNullInstance(); // default is null
|
||||
if (returnSt->retValue) returnValue = eval(returnSt->retValue, ctx);
|
||||
return StatementResult::returnFlow(returnValue);
|
||||
}
|
||||
|
||||
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<Ast::ExpressionStmtAst>(stmt);
|
||||
return StatementResult::normal(eval(exprStmt->exp, ctx));
|
||||
}
|
||||
|
||||
case BlockStatement: {
|
||||
auto block = std::static_pointer_cast<Ast::BlockStatementAst>(stmt);
|
||||
|
||||
|
||||
ContextPtr blockCtx = std::make_shared<Context>(
|
||||
FString(std::format("<Block at {}:{}>", block->getAAI().line, block->getAAI().column)), ctx);
|
||||
return evalBlockStatement(block, blockCtx);
|
||||
}
|
||||
|
||||
default:
|
||||
throw RuntimeError(
|
||||
FString(std::format("Feature stmt {} unsupported yet", magic_enum::enum_name(stmt->getType()))));
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user