v0.4.2-alpha
All checks were successful
Release Build / build-windows-x64 (push) Successful in 43s
Release Build / build-linux-x64 (push) Successful in 50s

[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:
2026-01-22 08:24:14 +08:00
parent 21641f888e
commit ca4ae143b4
27 changed files with 947 additions and 670 deletions

View File

@@ -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

View 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

View File

@@ -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))
{

View File

@@ -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;

View File

@@ -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

View File

@@ -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)

View File

@@ -0,0 +1,10 @@
#pragma once
#include <memory>
namespace Fig
{
class Object;
using ObjectPtr = std::shared_ptr<Object>;
}; // namespace Fig

View File

@@ -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)));

View File

@@ -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