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,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Ast/Statements/InterfaceDefSt.hpp"
|
||||
#include "Value/interface.hpp"
|
||||
#include <Value/Type.hpp>
|
||||
#include <algorithm>
|
||||
@@ -303,7 +304,7 @@ namespace Fig
|
||||
return false;
|
||||
}
|
||||
|
||||
Function getDefaultImplementedMethod(const TypeInfo &structType, const FString &functionName)
|
||||
Ast::InterfaceMethod getDefaultImplementedMethod(const TypeInfo &structType, const FString &functionName)
|
||||
{
|
||||
// O(N²)
|
||||
// SLOW
|
||||
@@ -336,15 +337,12 @@ namespace Fig
|
||||
if (method.name == functionName)
|
||||
{
|
||||
if (!method.hasDefaultBody()) assert(false);
|
||||
|
||||
return Function(
|
||||
method.paras, TypeInfo(method.returnType), method.defaultBody, shared_from_this());
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return Function(); // ignore warning
|
||||
}
|
||||
|
||||
const Function &getImplementedMethod(const TypeInfo &structType, const FString &functionName) const
|
||||
|
||||
40
src/Evaluator/Value/IntPool.hpp
Normal file
40
src/Evaluator/Value/IntPool.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "Value/value.hpp"
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Value/value_forward.hpp>
|
||||
#include <Value/Type.hpp>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class IntPool
|
||||
{
|
||||
private:
|
||||
static constexpr ValueType::IntClass CACHE_MIN = -128;
|
||||
static constexpr ValueType::IntClass CACHE_MAX = 127;
|
||||
|
||||
std::array<ObjectPtr, CACHE_MAX - CACHE_MIN + 1> cache;
|
||||
|
||||
public:
|
||||
IntPool()
|
||||
{
|
||||
for (ValueType::IntClass i = CACHE_MIN; i <= CACHE_MAX; ++i)
|
||||
{
|
||||
cache[i - CACHE_MIN] = std::make_shared<Object>(i);
|
||||
}
|
||||
}
|
||||
ObjectPtr createInt(ValueType::IntClass val) const
|
||||
{
|
||||
if (val >= CACHE_MIN && val <= CACHE_MAX) { return cache[val - CACHE_MIN]; }
|
||||
return std::make_shared<Object>(val);
|
||||
}
|
||||
|
||||
static const IntPool &getInstance()
|
||||
{
|
||||
static IntPool pool;
|
||||
return pool;
|
||||
}
|
||||
};
|
||||
}; // namespace Fig
|
||||
@@ -87,7 +87,7 @@ namespace Fig
|
||||
std::format("Variable `{}` expects type `{}`, but got '{}'",
|
||||
s->name.toBasicString(),
|
||||
s->declaredType.toString().toBasicString(),
|
||||
v->getTypeInfo().toString().toBasicString())));
|
||||
prettyType(v).toBasicString())));
|
||||
}
|
||||
if (isAccessConst(s->am))
|
||||
{
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
#include <Ast/AccessModifier.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Value/Type.hpp>
|
||||
#include <Value/value_forward.hpp>
|
||||
|
||||
#include <memory>
|
||||
namespace Fig
|
||||
{
|
||||
class Object;
|
||||
using ObjectPtr = std::shared_ptr<Object>;
|
||||
|
||||
struct VariableSlot
|
||||
{
|
||||
FString name;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "Value/structType.hpp"
|
||||
#include <Value/Type.hpp>
|
||||
#include <Value/value.hpp>
|
||||
#include <Context/context.hpp>
|
||||
@@ -26,9 +27,7 @@ namespace Fig
|
||||
{
|
||||
if (!typeMap.contains(_name))
|
||||
{
|
||||
throw RuntimeError(FString(std::format(
|
||||
"No type named '{}'",
|
||||
_name.toBasicString())));
|
||||
throw RuntimeError(FString(std::format("No type named '{}'", _name.toBasicString())));
|
||||
// *this = ValueType::String;
|
||||
}
|
||||
id = typeMap.at(name); // may throw
|
||||
@@ -41,10 +40,7 @@ namespace Fig
|
||||
ObjectPtr value = key.value;
|
||||
const TypeInfo &type = value->getTypeInfo();
|
||||
|
||||
if (type == ValueType::Int)
|
||||
{
|
||||
return std::hash<ValueType::IntClass>{}(value->as<ValueType::IntClass>());
|
||||
}
|
||||
if (type == ValueType::Int) { return std::hash<ValueType::IntClass>{}(value->as<ValueType::IntClass>()); }
|
||||
if (type == ValueType::Double)
|
||||
{
|
||||
return std::hash<ValueType::DoubleClass>{}(value->as<ValueType::DoubleClass>());
|
||||
@@ -61,10 +57,7 @@ namespace Fig
|
||||
{
|
||||
auto HashFields = [](std::vector<Field> fields) {
|
||||
size_t r = 0;
|
||||
for (auto &f : fields)
|
||||
{
|
||||
r += std::hash<Field>{}(f);
|
||||
}
|
||||
for (auto &f : fields) { r += std::hash<Field>{}(f); }
|
||||
return r;
|
||||
};
|
||||
const StructType &st = value->as<StructType>();
|
||||
@@ -73,19 +66,30 @@ namespace Fig
|
||||
if (type == ValueType::StructInstance)
|
||||
{
|
||||
const StructInstance &si = value->as<StructInstance>();
|
||||
return std::hash<TypeInfo>{}(si.parentType) + std::hash<uint64_t>{}(reinterpret_cast<uint64_t>(std::addressof(*si.localContext)));
|
||||
return std::hash<TypeInfo>{}(si.parentType)
|
||||
+ std::hash<uint64_t>{}(reinterpret_cast<uint64_t>(std::addressof(*si.localContext)));
|
||||
}
|
||||
assert(false);
|
||||
throw ""; // ignore warning
|
||||
}
|
||||
}
|
||||
|
||||
FString prettyType(std::shared_ptr<const Object> obj)
|
||||
TypeInfo actualType(std::shared_ptr<const Object> obj)
|
||||
{
|
||||
auto t = obj->getTypeInfo();
|
||||
if (t == ValueType::StructInstance)
|
||||
return obj->as<StructInstance>().parentType.toString();
|
||||
return t.toString();
|
||||
|
||||
// dispatch builtin struct types (like Int{}, List{} e.g...)
|
||||
if (t == ValueType::StructType)
|
||||
{
|
||||
return obj->as<StructType>().type;
|
||||
}
|
||||
|
||||
if (t == ValueType::StructInstance) return obj->as<StructInstance>().parentType;
|
||||
return t;
|
||||
}
|
||||
FString prettyType(std::shared_ptr<const Object> obj)
|
||||
{
|
||||
return actualType(obj).toString();
|
||||
}
|
||||
|
||||
const TypeInfo ValueType::Any(FString(u8"Any"), true); // id: 1
|
||||
@@ -102,8 +106,6 @@ namespace Fig
|
||||
const TypeInfo ValueType::Module(FString(u8"Module"), true); // id: 12
|
||||
const TypeInfo ValueType::InterfaceType(FString(u8"InterfaceType"), true); // id: 13
|
||||
|
||||
|
||||
|
||||
bool implements(const TypeInfo &structType, const TypeInfo &interfaceType, ContextPtr ctx)
|
||||
{
|
||||
return ctx->hasImplRegisted(structType, interfaceType);
|
||||
@@ -111,28 +113,25 @@ namespace Fig
|
||||
|
||||
bool isTypeMatch(const TypeInfo &expected, ObjectPtr obj, ContextPtr ctx)
|
||||
{
|
||||
if (expected == ValueType::Any)
|
||||
return true;
|
||||
if (expected == ValueType::Any) return true;
|
||||
|
||||
TypeInfo actual = obj->getTypeInfo();
|
||||
|
||||
if (obj->is<StructInstance>())
|
||||
if (obj->is<StructType>())
|
||||
{
|
||||
const StructType &t = obj->as<StructType>();
|
||||
if (expected == t.type) // the StructType typeinfo
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (obj->is<StructInstance>())
|
||||
{
|
||||
const StructInstance &si = obj->as<StructInstance>();
|
||||
if (si.parentType == expected)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (implements(si.parentType, expected, ctx))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return expected == actual;
|
||||
if (si.parentType == expected) { return true; }
|
||||
if (implements(si.parentType, expected, ctx)) { return true; }
|
||||
}
|
||||
return expected == actual;
|
||||
}
|
||||
|
||||
} // namespace Fig
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <Value/Type.hpp>
|
||||
#include <Value/valueError.hpp>
|
||||
#include <Value/module.hpp>
|
||||
#include <Value/value_forward.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <variant>
|
||||
@@ -31,10 +32,8 @@ namespace Fig
|
||||
static_cast<ValueType::DoubleClass>(std::numeric_limits<ValueType::IntClass>::min());
|
||||
return d > intMaxAsDouble || d < intMinAsDouble;
|
||||
}
|
||||
class Object;
|
||||
using ObjectPtr = std::shared_ptr<Object>;
|
||||
|
||||
|
||||
TypeInfo actualType(std::shared_ptr<const Object> obj);
|
||||
FString prettyType(std::shared_ptr<const Object> obj);
|
||||
|
||||
bool operator==(const Object &, const Object &);
|
||||
@@ -509,7 +508,7 @@ namespace Fig
|
||||
{
|
||||
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
|
||||
auto result = lhs.getNumericValue() + rhs.getNumericValue();
|
||||
if (bothInt && !isNumberExceededIntLimit(result))
|
||||
if (bothInt)
|
||||
return Object(static_cast<ValueType::IntClass>(result));
|
||||
return Object(result);
|
||||
}
|
||||
@@ -526,7 +525,7 @@ namespace Fig
|
||||
{
|
||||
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
|
||||
auto result = lhs.getNumericValue() - rhs.getNumericValue();
|
||||
if (bothInt && !isNumberExceededIntLimit(result))
|
||||
if (bothInt)
|
||||
return Object(static_cast<ValueType::IntClass>(result));
|
||||
return Object(result);
|
||||
}
|
||||
@@ -541,10 +540,20 @@ namespace Fig
|
||||
{
|
||||
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
|
||||
auto result = lhs.getNumericValue() * rhs.getNumericValue();
|
||||
if (bothInt && !isNumberExceededIntLimit(result))
|
||||
if (bothInt)
|
||||
return Object(static_cast<ValueType::IntClass>(result));
|
||||
return Object(result);
|
||||
}
|
||||
if (lhs.is<ValueType::StringClass>() && rhs.is<ValueType::IntClass>())
|
||||
{
|
||||
FString result;
|
||||
const FString &l = lhs.as<ValueType::StringClass>();
|
||||
for (size_t i=0; i < rhs.getNumericValue(); ++i)
|
||||
{
|
||||
result += l;
|
||||
}
|
||||
return Object(result);
|
||||
}
|
||||
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "*", lhs, rhs)));
|
||||
}
|
||||
|
||||
@@ -559,7 +568,7 @@ namespace Fig
|
||||
throw ValueError(FString(makeTypeErrorMessage("Division by zero", "/", lhs, rhs)));
|
||||
// bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
|
||||
auto result = lhs.getNumericValue() / rnv;
|
||||
// if (bothInt && !isNumberExceededIntLimit(result))
|
||||
// if (bothInt)
|
||||
// return Object(static_cast<ValueType::IntClass>(result));
|
||||
|
||||
// int / int maybe decimals
|
||||
@@ -573,15 +582,24 @@ namespace Fig
|
||||
{
|
||||
if (lhs.isNull() || rhs.isNull())
|
||||
throw ValueError(FString(makeTypeErrorMessage("Cannot modulo", "%", lhs, rhs)));
|
||||
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(makeTypeErrorMessage("Modulo by zero", "/", lhs, rhs)));
|
||||
|
||||
ValueType::IntClass q = lv / rv;
|
||||
ValueType::IntClass r = lv % rv;
|
||||
if (r != 0 && ((lv < 0) != (rv < 0))) { q -= 1; }
|
||||
return q;
|
||||
}
|
||||
|
||||
if (lhs.isNumeric() && rhs.isNumeric())
|
||||
{
|
||||
auto rnv = rhs.getNumericValue();
|
||||
if (rnv == 0)
|
||||
throw ValueError(FString(makeTypeErrorMessage("Modulo by zero", "/", lhs, rhs)));
|
||||
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
|
||||
auto result = std::fmod(lhs.getNumericValue(), rnv);
|
||||
if (bothInt && !isNumberExceededIntLimit(result))
|
||||
return Object(static_cast<ValueType::IntClass>(result));
|
||||
return Object(result);
|
||||
}
|
||||
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "%", lhs, rhs)));
|
||||
@@ -698,7 +716,7 @@ namespace Fig
|
||||
{
|
||||
bool bothInt = base.is<ValueType::IntClass>() && exp.is<ValueType::IntClass>();
|
||||
auto result = std::pow(base.getNumericValue(), exp.getNumericValue());
|
||||
if (bothInt && !isNumberExceededIntLimit(result))
|
||||
if (bothInt)
|
||||
return Object(static_cast<ValueType::IntClass>(result));
|
||||
return Object(result);
|
||||
}
|
||||
@@ -706,7 +724,6 @@ namespace Fig
|
||||
}
|
||||
};
|
||||
|
||||
using ObjectPtr = std::shared_ptr<Object>;
|
||||
using RvObject = ObjectPtr;
|
||||
|
||||
inline bool operator==(const ValueKey &l, const ValueKey &r)
|
||||
|
||||
10
src/Evaluator/Value/value_forward.hpp
Normal file
10
src/Evaluator/Value/value_forward.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class Object;
|
||||
|
||||
using ObjectPtr = std::shared_ptr<Object>;
|
||||
}; // namespace Fig
|
||||
@@ -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)));
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "Ast/Expressions/InitExpr.hpp"
|
||||
#include <Ast/Statements/ImplementSt.hpp>
|
||||
#include <Ast/Statements/InterfaceDefSt.hpp>
|
||||
#include <Value/Type.hpp>
|
||||
@@ -127,7 +128,7 @@ namespace Fig
|
||||
LvObject evalLv(Ast::Expression, ContextPtr); // for access: a.b / index a[b]
|
||||
|
||||
/* Right-value eval*/
|
||||
|
||||
RvObject evalInitExpr(Ast::InitExpr, ContextPtr); // only allows evalUnary to call
|
||||
RvObject evalBinary(Ast::BinaryExpr, ContextPtr); // normal binary expr: +, -, *....
|
||||
RvObject evalUnary(Ast::UnaryExpr, ContextPtr); // unary expr
|
||||
RvObject evalTernary(Ast::TernaryExpr, ContextPtr); // ternary expr
|
||||
|
||||
Reference in New Issue
Block a user