forked from PuqiAR/Fig-TreeWalker
v0.4.2-alpha
[Fix][Impl] 为了消除类构造带来的语法歧义,同时保持实现简洁和括号省略的语法,自此版本,引入了 `new` 操作符
造成歧义的原方法:
if a == A{}
条件是 a == A,还是 a == A{} ?
因此,现在使用 new a_struct{}来构造类
[Opti] 相较于 Fig v0.4.1-alpha版本,release O3同编译条件下
Fib普通递归法性能提升 ~50%
具体方式:
增加了小整数优化,-128~127的整数现在会直接从IntPool获取而不是新构造
...忘了
[Fix] 类构造 shorthand模式忘写了,现在补上了
[Feat][Impl] 类型声明现在接受一个表达式,原为Identifier。实现 var start: time.Time = time.now() 的效果
这是符合语法和语言特性的支持,类型为一等公民。类似Python的 <class 'type'>
[Impl] 修改了部分错误输出的细节
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
#include <Ast/Expressions/VarExpr.hpp>
|
||||
#include <Value/Type.hpp>
|
||||
#include <Value/value.hpp>
|
||||
#include <Value/IntPool.hpp>
|
||||
#include <Ast/Statements/ErrorFlow.hpp>
|
||||
#include <Value/VariableSlot.hpp>
|
||||
#include <Value/value.hpp>
|
||||
@@ -120,11 +124,11 @@ namespace Fig
|
||||
}
|
||||
else if (ctx->hasDefaultImplementedMethod(si.parentType, member))
|
||||
{
|
||||
const auto &ifm = ctx->getDefaultImplementedMethod(si.parentType, member);
|
||||
Function fn(ifm.paras, actualType(eval(ifm.returnType, ctx)), ifm.defaultBody, ctx);
|
||||
|
||||
return LvObject(std::make_shared<VariableSlot>(
|
||||
member,
|
||||
std::make_shared<Object>(ctx->getDefaultImplementedMethod(si.parentType, member)),
|
||||
ValueType::Function,
|
||||
AccessModifier::PublicConst),
|
||||
member, std::make_shared<Object>(fn), ValueType::Function, AccessModifier::PublicConst),
|
||||
ctx);
|
||||
}
|
||||
else
|
||||
@@ -222,6 +226,362 @@ 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<StructType>())
|
||||
{
|
||||
throw EvaluatorError(u8"NotAStructTypeError",
|
||||
std::format("'{}' is not a structure type", structName.toBasicString()),
|
||||
initExpr);
|
||||
}
|
||||
const StructType &structT = structTypeVal->as<StructType>();
|
||||
|
||||
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>(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<ValueType::IntClass>()) err("expects Int");
|
||||
return std::make_shared<Object>(val->as<ValueType::IntClass>());
|
||||
}
|
||||
|
||||
// ===================== Double =====================
|
||||
if (type == ValueType::Double)
|
||||
{
|
||||
if (!val->is<ValueType::DoubleClass>()) err("expects Double");
|
||||
return std::make_shared<Object>(val->as<ValueType::DoubleClass>());
|
||||
}
|
||||
|
||||
// ===================== Bool =====================
|
||||
if (type == ValueType::Bool)
|
||||
{
|
||||
if (!val->is<ValueType::BoolClass>()) err("expects Bool");
|
||||
return std::make_shared<Object>(val->as<ValueType::BoolClass>());
|
||||
}
|
||||
|
||||
// ===================== String =====================
|
||||
if (type == ValueType::String)
|
||||
{
|
||||
if (!val->is<ValueType::StringClass>()) err("expects String");
|
||||
return std::make_shared<Object>(val->as<ValueType::StringClass>());
|
||||
}
|
||||
|
||||
// ===================== Null =====================
|
||||
if (type == ValueType::Null)
|
||||
{
|
||||
// Null basically ignores input but keep invariant strict:
|
||||
if (!val->is<ValueType::NullClass>()) err("expects Null");
|
||||
return Object::getNullInstance();
|
||||
}
|
||||
|
||||
// ===================== List =====================
|
||||
if (type == ValueType::List)
|
||||
{
|
||||
if (!val->is<List>()) err("expects List");
|
||||
|
||||
const auto &src = val->as<List>();
|
||||
auto copied = std::make_shared<Object>(List{});
|
||||
|
||||
auto &dst = copied->as<List>();
|
||||
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<Map>()) err("expects Map");
|
||||
|
||||
const auto &src = val->as<Map>();
|
||||
auto copied = std::make_shared<Object>(Map{});
|
||||
|
||||
auto &dst = copied->as<Map>();
|
||||
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<std::pair<FString, ObjectPtr>> 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<Context>(FString(std::format("<StructInstance {}>", 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<Object>(Function(fn.paras, fn.retType, fn.body, instanceCtx)));
|
||||
}
|
||||
|
||||
return std::make_shared<Object>(StructInstance(structT.type, instanceCtx));
|
||||
}
|
||||
|
||||
RvObject Evaluator::evalBinary(Ast::BinaryExpr bin, ContextPtr ctx)
|
||||
{
|
||||
using Ast::Operator;
|
||||
@@ -232,16 +592,37 @@ namespace Fig
|
||||
case Operator::Add: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
{
|
||||
ValueType::IntClass result = lhs->as<ValueType::IntClass>() + rhs->as<ValueType::IntClass>();
|
||||
return IntPool::getInstance().createInt(result);
|
||||
}
|
||||
|
||||
return std::make_shared<Object>(*lhs + *rhs);
|
||||
}
|
||||
case Operator::Subtract: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
{
|
||||
ValueType::IntClass result = lhs->as<ValueType::IntClass>() - rhs->as<ValueType::IntClass>();
|
||||
return IntPool::getInstance().createInt(result);
|
||||
}
|
||||
|
||||
return std::make_shared<Object>(*lhs - *rhs);
|
||||
};
|
||||
case Operator::Multiply: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
{
|
||||
ValueType::IntClass result = lhs->as<ValueType::IntClass>() * rhs->as<ValueType::IntClass>();
|
||||
return IntPool::getInstance().createInt(result);
|
||||
}
|
||||
|
||||
return std::make_shared<Object>((*lhs) * (*rhs));
|
||||
};
|
||||
case Operator::Divide: {
|
||||
@@ -252,11 +633,40 @@ namespace Fig
|
||||
case Operator::Modulo: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
{
|
||||
ValueType::IntClass lv = lhs->as<ValueType::IntClass>();
|
||||
ValueType::IntClass rv = lhs->as<ValueType::IntClass>();
|
||||
if (rv == 0)
|
||||
{
|
||||
throw ValueError(
|
||||
FString(
|
||||
std::format(
|
||||
"Modulo by zero: {} % {}",
|
||||
lv,
|
||||
rv
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
ValueType::IntClass result = lv / rv;
|
||||
ValueType::IntClass r = lv % rv;
|
||||
if (r != 0 && ((lv < 0) != (rv < 0))) { result -= 1; }
|
||||
return IntPool::getInstance().createInt(result);
|
||||
}
|
||||
|
||||
return std::make_shared<Object>(*lhs % *rhs);
|
||||
};
|
||||
case Operator::Power: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
{
|
||||
ValueType::IntClass result = std::pow(lhs->as<ValueType::IntClass>(),rhs->as<ValueType::IntClass>());
|
||||
return IntPool::getInstance().createInt(result);
|
||||
}
|
||||
return std::make_shared<Object>(power(*lhs, *rhs));
|
||||
}
|
||||
case Operator::And: {
|
||||
@@ -343,34 +753,64 @@ namespace Fig
|
||||
|
||||
throw EvaluatorError(u8"TypeError",
|
||||
std::format("Unsupported operator `is` for '{}' && '{}'",
|
||||
lhsType.toString().toBasicString(),
|
||||
rhsType.toString().toBasicString()),
|
||||
prettyType(lhs).toBasicString(),
|
||||
prettyType(rhs).toBasicString()),
|
||||
bin->lexp);
|
||||
}
|
||||
|
||||
case Operator::BitAnd: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
{
|
||||
ValueType::IntClass result = lhs->as<ValueType::IntClass>() & rhs->as<ValueType::IntClass>();
|
||||
return IntPool::getInstance().createInt(result);
|
||||
}
|
||||
return std::make_shared<Object>(bit_and(*lhs, *rhs));
|
||||
}
|
||||
case Operator::BitOr: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
{
|
||||
ValueType::IntClass result = lhs->as<ValueType::IntClass>() | rhs->as<ValueType::IntClass>();
|
||||
return IntPool::getInstance().createInt(result);
|
||||
}
|
||||
return std::make_shared<Object>(bit_or(*lhs, *rhs));
|
||||
}
|
||||
case Operator::BitXor: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
{
|
||||
ValueType::IntClass result = lhs->as<ValueType::IntClass>() ^ rhs->as<ValueType::IntClass>();
|
||||
return IntPool::getInstance().createInt(result);
|
||||
}
|
||||
return std::make_shared<Object>(bit_xor(*lhs, *rhs));
|
||||
}
|
||||
case Operator::ShiftLeft: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
{
|
||||
ValueType::IntClass result = lhs->as<ValueType::IntClass>() << rhs->as<ValueType::IntClass>();
|
||||
return IntPool::getInstance().createInt(result);
|
||||
}
|
||||
return std::make_shared<Object>(shift_left(*lhs, *rhs));
|
||||
}
|
||||
case Operator::ShiftRight: {
|
||||
ObjectPtr lhs = eval(lexp, ctx);
|
||||
ObjectPtr rhs = eval(rexp, ctx);
|
||||
|
||||
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
|
||||
{
|
||||
ValueType::IntClass result = lhs->as<ValueType::IntClass>() >> rhs->as<ValueType::IntClass>();
|
||||
return IntPool::getInstance().createInt(result);
|
||||
}
|
||||
return std::make_shared<Object>(shift_right(*lhs, *rhs));
|
||||
}
|
||||
|
||||
@@ -439,7 +879,6 @@ namespace Fig
|
||||
case Operator::BitNot: {
|
||||
return std::make_shared<Object>(bit_not((*value)));
|
||||
}
|
||||
|
||||
default: {
|
||||
throw EvaluatorError(u8"UnsupportedOpError",
|
||||
std::format("Unsupported op '{}' for unary expression", magic_enum::enum_name(op)),
|
||||
@@ -721,306 +1160,8 @@ namespace Fig
|
||||
}
|
||||
case AstType::InitExpr: {
|
||||
auto initExpr = std::static_pointer_cast<Ast::InitExprAst>(exp);
|
||||
LvObject structeLv = evalLv(initExpr->structe, ctx);
|
||||
ObjectPtr structTypeVal = structeLv.get();
|
||||
const FString &structName = structeLv.name();
|
||||
if (!structTypeVal->is<StructType>())
|
||||
{
|
||||
throw EvaluatorError(u8"NotAStructTypeError",
|
||||
std::format("'{}' is not a structure type", structName.toBasicString()),
|
||||
initExpr);
|
||||
}
|
||||
const StructType &structT = structTypeVal->as<StructType>();
|
||||
|
||||
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>(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<ValueType::IntClass>()) err("expects Int");
|
||||
return std::make_shared<Object>(val->as<ValueType::IntClass>());
|
||||
}
|
||||
|
||||
// ===================== Double =====================
|
||||
if (type == ValueType::Double)
|
||||
{
|
||||
if (!val->is<ValueType::DoubleClass>()) err("expects Double");
|
||||
return std::make_shared<Object>(val->as<ValueType::DoubleClass>());
|
||||
}
|
||||
|
||||
// ===================== Bool =====================
|
||||
if (type == ValueType::Bool)
|
||||
{
|
||||
if (!val->is<ValueType::BoolClass>()) err("expects Bool");
|
||||
return std::make_shared<Object>(val->as<ValueType::BoolClass>());
|
||||
}
|
||||
|
||||
// ===================== String =====================
|
||||
if (type == ValueType::String)
|
||||
{
|
||||
if (!val->is<ValueType::StringClass>()) err("expects String");
|
||||
return std::make_shared<Object>(val->as<ValueType::StringClass>());
|
||||
}
|
||||
|
||||
// ===================== Null =====================
|
||||
if (type == ValueType::Null)
|
||||
{
|
||||
// Null basically ignores input but keep invariant strict:
|
||||
if (!val->is<ValueType::NullClass>()) err("expects Null");
|
||||
return Object::getNullInstance();
|
||||
}
|
||||
|
||||
// ===================== List =====================
|
||||
if (type == ValueType::List)
|
||||
{
|
||||
if (!val->is<List>()) err("expects List");
|
||||
|
||||
const auto &src = val->as<List>();
|
||||
auto copied = std::make_shared<Object>(List{});
|
||||
|
||||
auto &dst = copied->as<List>();
|
||||
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<Map>()) err("expects Map");
|
||||
|
||||
const auto &src = val->as<Map>();
|
||||
auto copied = std::make_shared<Object>(Map{});
|
||||
|
||||
auto &dst = copied->as<Map>();
|
||||
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<std::pair<FString, ObjectPtr>> evaluatedArgs;
|
||||
for (const auto &[argName, argExpr] : initExpr->args)
|
||||
{
|
||||
evaluatedArgs.push_back({argName, eval(argExpr, ctx)});
|
||||
}
|
||||
ContextPtr instanceCtx = std::make_shared<Context>(
|
||||
FString(std::format("<StructInstance {}>", 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<Object>(Function(fn.paras,
|
||||
// fn.retType,
|
||||
// fn.body,
|
||||
// instanceCtx) // change its closureContext to
|
||||
// // struct instance's context
|
||||
// ));
|
||||
// }
|
||||
|
||||
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<Object>(Function(
|
||||
fn.paras,
|
||||
fn.retType,
|
||||
fn.body,
|
||||
instanceCtx
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
return std::make_shared<Object>(StructInstance(structT.type, instanceCtx));
|
||||
assert(initExpr != nullptr);
|
||||
return evalInitExpr(initExpr, ctx);
|
||||
}
|
||||
|
||||
case AstType::ListExpr: {
|
||||
@@ -1081,18 +1222,22 @@ namespace Fig
|
||||
|
||||
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())
|
||||
const Ast::Expression &declaredTypeExp = varDef->declaredType;
|
||||
|
||||
if (varDef->followupType) { declaredType = actualType(value); }
|
||||
else if (declaredTypeExp)
|
||||
{
|
||||
declaredType = TypeInfo(declaredTypeName);
|
||||
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(),
|
||||
declaredTypeName.toBasicString(),
|
||||
prettyType(declaredTypeValue).toBasicString(),
|
||||
prettyType(value).toBasicString()),
|
||||
varDef->expr);
|
||||
}
|
||||
@@ -1120,7 +1265,14 @@ namespace Fig
|
||||
std::format("Function `{}` already declared in this scope", fnName.toBasicString()),
|
||||
fnDef);
|
||||
}
|
||||
Function fn(fnDef->paras, TypeInfo(fnDef->retType), fnDef->body, ctx);
|
||||
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),
|
||||
@@ -1151,7 +1303,14 @@ namespace Fig
|
||||
stDef->name.toBasicString()),
|
||||
stDef);
|
||||
}
|
||||
fields.push_back(Field(field.am, field.fieldName, TypeInfo(field.tiName), field.defaultValueExpr));
|
||||
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(),
|
||||
@@ -1322,8 +1481,10 @@ namespace Fig
|
||||
|
||||
implemented.insert(name);
|
||||
|
||||
ObjectPtr returnTypeValue = eval(ifMethod.returnType, ctx);
|
||||
|
||||
record.implMethods[name] =
|
||||
Function(implMethod.paras, TypeInfo(ifMethod.returnType), implMethod.body, ctx);
|
||||
Function(implMethod.paras, actualType(returnTypeValue), implMethod.body, ctx);
|
||||
}
|
||||
|
||||
for (auto &m : interface.methods)
|
||||
@@ -1499,13 +1660,11 @@ namespace Fig
|
||||
case BreakSt: {
|
||||
if (!ctx->parent)
|
||||
{
|
||||
throw EvaluatorError(
|
||||
u8"BreakOutsideLoopError", u8"`break` statement outside loop", stmt);
|
||||
throw EvaluatorError(u8"BreakOutsideLoopError", u8"`break` statement outside loop", stmt);
|
||||
}
|
||||
if (!ctx->isInLoopContext())
|
||||
{
|
||||
throw EvaluatorError(
|
||||
u8"BreakOutsideLoopError", u8"`break` statement outside loop", stmt);
|
||||
throw EvaluatorError(u8"BreakOutsideLoopError", u8"`break` statement outside loop", stmt);
|
||||
}
|
||||
return StatementResult::breakFlow();
|
||||
}
|
||||
@@ -1513,13 +1672,11 @@ namespace Fig
|
||||
case ContinueSt: {
|
||||
if (!ctx->parent)
|
||||
{
|
||||
throw EvaluatorError(
|
||||
u8"ContinueOutsideLoopError", u8"`continue` statement outside loop", stmt);
|
||||
throw EvaluatorError(u8"ContinueOutsideLoopError", u8"`continue` statement outside loop", stmt);
|
||||
}
|
||||
if (!ctx->isInLoopContext())
|
||||
{
|
||||
throw EvaluatorError(
|
||||
u8"ContinueOutsideLoopError", u8"`continue` statement outside loop", stmt);
|
||||
throw EvaluatorError(u8"ContinueOutsideLoopError", u8"`continue` statement outside loop", stmt);
|
||||
}
|
||||
return StatementResult::continueFlow();
|
||||
}
|
||||
@@ -1531,6 +1688,15 @@ namespace Fig
|
||||
return StatementResult::normal(eval(exprStmt->exp, ctx));
|
||||
}
|
||||
|
||||
case BlockStatement: {
|
||||
auto block = std::static_pointer_cast<Ast::BlockStatementAst>(stmt);
|
||||
assert(block != nullptr);
|
||||
|
||||
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()))));
|
||||
@@ -1690,9 +1856,8 @@ namespace Fig
|
||||
|
||||
if (ctx->containsInThisScope(modName))
|
||||
{
|
||||
throw EvaluatorError(u8"RedeclarationError",
|
||||
std::format("{} has already been declared.", modName.toBasicString()),
|
||||
i);
|
||||
throw EvaluatorError(
|
||||
u8"RedeclarationError", std::format("{} has already been declared.", modName.toBasicString()), i);
|
||||
}
|
||||
ctx->def(
|
||||
modName, ValueType::Module, AccessModifier::PublicConst, std::make_shared<Object>(Module(modName, modCtx)));
|
||||
|
||||
Reference in New Issue
Block a user