[Feat] 增加容器 List, Map, 以及对应Hash

[Impl] Addressable/Unaddressable Error现在内部存储FString而非View
This commit is contained in:
2025-12-25 17:10:12 +08:00
parent ab4024c2bf
commit f056b0ffbe
18 changed files with 583 additions and 179 deletions

View File

@@ -9,6 +9,8 @@
#include <cmath>
#include <string>
#include <format>
#include <functional>
#include <unordered_map>
namespace Fig
{
@@ -26,6 +28,22 @@ 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>;
using List = std::vector<ObjectPtr>;
struct ValueKey
{
ObjectPtr value;
ValueKey(ObjectPtr _value) :
value(_value) {}
};
struct ValueKeyHash
{
size_t operator()(const ValueKey &key) const;
};
using Map = std::unordered_map<ValueKey, ObjectPtr, ValueKeyHash>;
class Object
{
@@ -38,7 +56,104 @@ namespace Fig
ValueType::BoolClass,
Function,
StructType,
StructInstance>;
StructInstance,
List,
Map>;
std::unordered_map<TypeInfo,
std::unordered_map<FString,
std::function<ObjectPtr(std::vector<ObjectPtr>)>>,
TypeInfoHash>
memberTypeFunctions{
{ValueType::Null, {}},
{ValueType::Int, {}},
{ValueType::Double, {}},
{ValueType::String, {
{u8"length", [this](std::vector<ObjectPtr> args) -> ObjectPtr {
if (args.size() != 0)
throw RuntimeError(FString(
std::format("`length` expects 0 arguments, {} got", args.size())));
const FString &str = as<ValueType::StringClass>();
return std::make_shared<Object>(static_cast<ValueType::IntClass>(str.length()));
}},
}},
{ValueType::Function, {}},
{ValueType::StructType, {}},
{ValueType::StructInstance, {}},
{ValueType::List, {{u8"length", [this](std::vector<ObjectPtr> args) -> ObjectPtr {
if (args.size() != 0)
throw RuntimeError(FString(
std::format("`length` expects 0 arguments, {} got", args.size())));
const List &list = as<List>();
return std::make_shared<Object>(static_cast<ValueType::IntClass>(list.size()));
}},
{u8"get", [this](std::vector<ObjectPtr> args) -> ObjectPtr {
if (args.size() != 1)
throw RuntimeError(FString(
std::format("`get` expects 1 arguments, {} got", args.size())));
ObjectPtr arg = args[0];
if (arg->getTypeInfo() != ValueType::Int)
throw RuntimeError(FString(
std::format("`get` argument 1 expects Int, {} got", arg->getTypeInfo().toString().toBasicString())));
ValueType::IntClass i = arg->as<ValueType::IntClass>();
const List &list = as<List>();
if (i >= list.size())
return Object::getNullInstance();
return list[i];
}}}},
{ValueType::Map, {
{u8"get", [this](std::vector<ObjectPtr> args) -> ObjectPtr {
if (args.size() != 1)
throw RuntimeError(FString(
std::format("`get` expects 1 arguments, {} got", args.size())));
ObjectPtr index = args[0];
const Map &map = as<Map>();
if (!map.contains(index))
return Object::getNullInstance();
return map.at(index);
}},
{u8"contains", [this](std::vector<ObjectPtr> args) -> ObjectPtr {
if (args.size() != 1)
throw RuntimeError(FString(
std::format("`contains` expects 1 arguments, {} got", args.size())));
ObjectPtr index = args[0];
const Map &map = as<Map>();
return std::make_shared<Object>(
map.contains(index));
}},
}},
};
std::unordered_map<TypeInfo, std::unordered_map<FString, int>, TypeInfoHash> memberTypeFunctionsParas{
{ValueType::Null, {}},
{ValueType::Int, {}},
{ValueType::Double, {}},
{ValueType::String, {
{u8"length", 0},
}},
{ValueType::Function, {}},
{ValueType::StructType, {}},
{ValueType::StructInstance, {}},
{ValueType::List, {{u8"length", 0}, {u8"get", 1}}},
{ValueType::Map, {
{u8"get", 1},
{u8"contains", 1},
}},
};
bool hasMemberFunction(const FString &name) const
{
return memberTypeFunctions.at(getTypeInfo()).contains(name);
}
std::function<ObjectPtr(std::vector<ObjectPtr>)> getMemberFunction(const FString &name) const
{
return memberTypeFunctions.at(getTypeInfo()).at(name);
}
int getMemberFunctionParaCount(const FString &name) const
{
return memberTypeFunctionsParas.at(getTypeInfo()).at(name);
}
VariantType data;
@@ -49,9 +164,7 @@ namespace Fig
Object(const ValueType::IntClass &i) :
data(i) {}
explicit Object(const ValueType::DoubleClass &d) :
data(d)
{
}
data(d) {}
Object(const ValueType::StringClass &s) :
data(s) {}
Object(const ValueType::BoolClass &b) :
@@ -62,6 +175,10 @@ namespace Fig
data(s) {}
Object(const StructInstance &s) :
data(s) {}
Object(const List &l) :
data(l) {}
Object(const Map &m) :
data(m) {}
Object(const Object &) = default;
Object(Object &&) noexcept = default;
@@ -78,6 +195,10 @@ namespace Fig
return Object(ValueType::StringClass(u8""));
else if (ti == ValueType::Bool)
return Object(ValueType::BoolClass(false));
else if (ti == ValueType::List)
return Object(List{});
else if (ti == ValueType::Map)
return Object(Map{});
else
return *getNullInstance();
}
@@ -120,22 +241,37 @@ namespace Fig
{
return std::visit([](auto &&val) -> TypeInfo {
using T = std::decay_t<decltype(val)>;
if constexpr (std::is_same_v<T, ValueType::NullClass>)
return ValueType::Null;
else if constexpr (std::is_same_v<T, ValueType::IntClass>)
return ValueType::Int;
else if constexpr (std::is_same_v<T, ValueType::DoubleClass>)
return ValueType::Double;
else if constexpr (std::is_same_v<T, ValueType::StringClass>)
return ValueType::String;
else if constexpr (std::is_same_v<T, ValueType::BoolClass>)
return ValueType::Bool;
else if constexpr (std::is_same_v<T, Function>)
return ValueType::Function;
else if constexpr (std::is_same_v<T, StructType>)
return ValueType::StructType;
else if constexpr (std::is_same_v<T, StructInstance>)
return ValueType::StructInstance;
else if constexpr (std::is_same_v<T, List>)
return ValueType::List;
else if constexpr (std::is_same_v<T, Map>)
return ValueType::Map;
else
return ValueType::Any;
},
@@ -155,12 +291,18 @@ namespace Fig
throw RuntimeError(u8"getNumericValue: Not a numeric value");
}
FString toStringIO() const
{
if (is<ValueType::StringClass>()) return as<ValueType::StringClass>();
return toString();
}
FString toString() const
{
if (is<ValueType::NullClass>()) return FString(u8"null");
if (is<ValueType::IntClass>()) return FString(std::to_string(as<ValueType::IntClass>()));
if (is<ValueType::DoubleClass>()) return FString(std::format("{}", as<ValueType::DoubleClass>()));
if (is<ValueType::StringClass>()) return as<ValueType::StringClass>();
if (is<ValueType::StringClass>()) return FString(u8"<String \"") + as<ValueType::StringClass>() + FString(u8"\" >");
if (is<ValueType::BoolClass>()) return as<ValueType::BoolClass>() ? FString(u8"true") : FString(u8"false");
if (is<Function>())
return FString(std::format("<Function {} at {:p}>",
@@ -174,6 +316,36 @@ namespace Fig
return FString(std::format("<StructInstance '{}' at {:p}>",
as<StructInstance>().parentId,
static_cast<const void *>(&as<StructInstance>())));
if (is<List>())
{
FString output(u8"[");
const List &list = as<List>();
bool first_flag = true;
for (auto &ele : list)
{
if (!first_flag)
output += u8", ";
output += ele->toString();
first_flag = false;
}
output += u8"]";
return output;
}
if (is<Map>())
{
FString output(u8"{");
const Map &map = as<Map>();
bool first_flag = true;
for (auto &[key, value] : map)
{
if (!first_flag)
output += u8", ";
output += key.value->toString() + FString(u8" : ") + value->toString();
first_flag = false;
}
output += u8"}";
return output;
}
return FString(u8"<error>");
}
@@ -191,7 +363,7 @@ namespace Fig
friend Object operator+(const Object &lhs, const Object &rhs)
{
if (lhs.isNull() || rhs.isNull())
throw ValueError(FStringView(makeTypeErrorMessage("Cannot add", "+", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Cannot add", "+", lhs, rhs)));
if (lhs.isNumeric() && rhs.isNumeric())
{
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
@@ -202,13 +374,13 @@ namespace Fig
}
if (lhs.is<ValueType::StringClass>() && rhs.is<ValueType::StringClass>())
return Object(FString(lhs.as<ValueType::StringClass>() + rhs.as<ValueType::StringClass>()));
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "+", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "+", lhs, rhs)));
}
friend Object operator-(const Object &lhs, const Object &rhs)
{
if (lhs.isNull() || rhs.isNull())
throw ValueError(FStringView(makeTypeErrorMessage("Cannot subtract", "-", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Cannot subtract", "-", lhs, rhs)));
if (lhs.isNumeric() && rhs.isNumeric())
{
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
@@ -217,13 +389,13 @@ namespace Fig
return Object(static_cast<ValueType::IntClass>(result));
return Object(result);
}
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "-", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "-", lhs, rhs)));
}
friend Object operator*(const Object &lhs, const Object &rhs)
{
if (lhs.isNull() || rhs.isNull())
throw ValueError(FStringView(makeTypeErrorMessage("Cannot multiply", "*", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Cannot multiply", "*", lhs, rhs)));
if (lhs.isNumeric() && rhs.isNumeric())
{
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
@@ -232,82 +404,82 @@ namespace Fig
return Object(static_cast<ValueType::IntClass>(result));
return Object(result);
}
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "*", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "*", lhs, rhs)));
}
friend Object operator/(const Object &lhs, const Object &rhs)
{
if (lhs.isNull() || rhs.isNull())
throw ValueError(FStringView(makeTypeErrorMessage("Cannot divide", "/", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Cannot divide", "/", lhs, rhs)));
if (lhs.isNumeric() && rhs.isNumeric())
{
auto rnv = rhs.getNumericValue();
if (rnv == 0)
throw ValueError(FStringView(makeTypeErrorMessage("Division by zero", "/", lhs, rhs)));
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))
return Object(static_cast<ValueType::IntClass>(result));
return Object(result);
}
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "/", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "/", lhs, rhs)));
}
friend Object operator%(const Object &lhs, const Object &rhs)
{
if (lhs.isNull() || rhs.isNull())
throw ValueError(FStringView(makeTypeErrorMessage("Cannot modulo", "%", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Cannot modulo", "%", lhs, rhs)));
if (lhs.isNumeric() && rhs.isNumeric())
{
auto rnv = rhs.getNumericValue();
if (rnv == 0)
throw ValueError(FStringView(makeTypeErrorMessage("Modulo by zero", "/", lhs, rhs)));
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(FStringView(makeTypeErrorMessage("Unsupported operation", "%", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "%", lhs, rhs)));
}
// logic
friend Object operator&&(const Object &lhs, const Object &rhs)
{
if (!lhs.is<ValueType::BoolClass>() || !rhs.is<ValueType::BoolClass>())
throw ValueError(FStringView(makeTypeErrorMessage("Logical AND requires bool", "&&", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Logical AND requires bool", "&&", lhs, rhs)));
return Object(lhs.as<ValueType::BoolClass>() && rhs.as<ValueType::BoolClass>());
}
friend Object operator||(const Object &lhs, const Object &rhs)
{
if (!lhs.is<ValueType::BoolClass>() || !rhs.is<ValueType::BoolClass>())
throw ValueError(FStringView(makeTypeErrorMessage("Logical OR requires bool", "||", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Logical OR requires bool", "||", lhs, rhs)));
return Object(lhs.as<ValueType::BoolClass>() || rhs.as<ValueType::BoolClass>());
}
friend Object operator!(const Object &v)
{
if (!v.is<ValueType::BoolClass>())
throw ValueError(FStringView(std::format("Logical NOT requires bool: '{}'", v.getTypeInfo().name.toBasicString())));
throw ValueError(FString(std::format("Logical NOT requires bool: '{}'", v.getTypeInfo().name.toBasicString())));
return Object(!v.as<ValueType::BoolClass>());
}
friend Object operator-(const Object &v)
{
if (v.isNull())
throw ValueError(FStringView(u8"Unary minus cannot be applied to null"));
throw ValueError(FString(u8"Unary minus cannot be applied to null"));
if (v.is<ValueType::IntClass>())
return Object(-v.as<ValueType::IntClass>());
if (v.is<ValueType::DoubleClass>())
return Object(-v.as<ValueType::DoubleClass>());
throw ValueError(FStringView(std::format("Unary minus requires int or double: '{}'", v.getTypeInfo().name.toBasicString())));
throw ValueError(FString(std::format("Unary minus requires int or double: '{}'", v.getTypeInfo().name.toBasicString())));
}
friend Object operator~(const Object &v)
{
if (!v.is<ValueType::IntClass>())
throw ValueError(FStringView(std::format("Bitwise NOT requires int: '{}'", v.getTypeInfo().name.toBasicString())));
throw ValueError(FString(std::format("Bitwise NOT requires int: '{}'", v.getTypeInfo().name.toBasicString())));
return Object(~v.as<ValueType::IntClass>());
}
@@ -319,7 +491,7 @@ namespace Fig
if (lhs.isNumeric() && rhs.isNumeric()) return lhs.getNumericValue() < rhs.getNumericValue();
if (lhs.is<ValueType::StringClass>() && rhs.is<ValueType::StringClass>())
return lhs.as<ValueType::StringClass>() < rhs.as<ValueType::StringClass>();
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported comparison", "<", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Unsupported comparison", "<", lhs, rhs)));
}
friend bool operator<=(const Object &lhs, const Object &rhs) { return lhs == rhs || lhs < rhs; }
friend bool operator>(const Object &lhs, const Object &rhs)
@@ -327,7 +499,7 @@ namespace Fig
if (lhs.isNumeric() && rhs.isNumeric()) return lhs.getNumericValue() > rhs.getNumericValue();
if (lhs.is<ValueType::StringClass>() && rhs.is<ValueType::StringClass>())
return lhs.as<ValueType::StringClass>() > rhs.as<ValueType::StringClass>();
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported comparison", ">", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Unsupported comparison", ">", lhs, rhs)));
}
friend bool operator>=(const Object &lhs, const Object &rhs) { return lhs == rhs || lhs > rhs; }
@@ -335,49 +507,49 @@ namespace Fig
friend Object bit_and(const Object &lhs, const Object &rhs)
{
if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>())
throw ValueError(FStringView(makeTypeErrorMessage("Bitwise AND requires int", "&", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Bitwise AND requires int", "&", lhs, rhs)));
return Object(lhs.as<ValueType::IntClass>() & rhs.as<ValueType::IntClass>());
}
friend Object bit_or(const Object &lhs, const Object &rhs)
{
if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>())
throw ValueError(FStringView(makeTypeErrorMessage("Bitwise OR requires int", "|", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Bitwise OR requires int", "|", lhs, rhs)));
return Object(lhs.as<ValueType::IntClass>() | rhs.as<ValueType::IntClass>());
}
friend Object bit_xor(const Object &lhs, const Object &rhs)
{
if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>())
throw ValueError(FStringView(makeTypeErrorMessage("Bitwise XOR requires int", "^", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Bitwise XOR requires int", "^", lhs, rhs)));
return Object(lhs.as<ValueType::IntClass>() ^ rhs.as<ValueType::IntClass>());
}
friend Object bit_not(const Object &v)
{
if (!v.is<ValueType::IntClass>())
throw ValueError(FStringView(std::format("Bitwise NOT requires int: '{}'", v.getTypeInfo().name.toBasicString())));
throw ValueError(FString(std::format("Bitwise NOT requires int: '{}'", v.getTypeInfo().name.toBasicString())));
return Object(~v.as<ValueType::IntClass>());
}
friend Object shift_left(const Object &lhs, const Object &rhs)
{
if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>())
throw ValueError(FStringView(makeTypeErrorMessage("Shift left requires int", "<<", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Shift left requires int", "<<", lhs, rhs)));
return Object(lhs.as<ValueType::IntClass>() << rhs.as<ValueType::IntClass>());
}
friend Object shift_right(const Object &lhs, const Object &rhs)
{
if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>())
throw ValueError(FStringView(makeTypeErrorMessage("Shift right requires int", ">>", lhs, rhs)));
throw ValueError(FString(makeTypeErrorMessage("Shift right requires int", ">>", lhs, rhs)));
return Object(lhs.as<ValueType::IntClass>() >> rhs.as<ValueType::IntClass>());
}
friend Object power(const Object &base, const Object &exp)
{
if (base.isNull() || exp.isNull())
throw ValueError(FStringView(makeTypeErrorMessage("Cannot exponentiate", "**", base, exp)));
throw ValueError(FString(makeTypeErrorMessage("Cannot exponentiate", "**", base, exp)));
if (base.isNumeric() && exp.isNumeric())
{
bool bothInt = base.is<ValueType::IntClass>() && exp.is<ValueType::IntClass>();
@@ -386,68 +558,17 @@ namespace Fig
return Object(static_cast<ValueType::IntClass>(result));
return Object(result);
}
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "**", base, exp)));
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "**", base, exp)));
}
};
using ObjectPtr = std::shared_ptr<Object>;
using RvObject = ObjectPtr;
struct VariableSlot
inline bool operator==(const ValueKey &l, const ValueKey &r)
{
FString name;
ObjectPtr value;
TypeInfo declaredType;
AccessModifier am;
bool isRef = false;
std::shared_ptr<VariableSlot> refTarget;
};
struct LvObject
{
std::shared_ptr<VariableSlot> slot;
const ObjectPtr& get() const
{
auto s = resolve(slot);
return s->value;
}
void set(const ObjectPtr& v)
{
auto s = resolve(slot);
if (s->declaredType != ValueType::Any && s->declaredType != v->getTypeInfo())
{
throw RuntimeError(
FStringView(
std::format("Variable `{}` expects type `{}`, but got '{}'",
s->name.toBasicString(),
s->declaredType.toString().toBasicString(),
v->getTypeInfo().toString().toBasicString())
)
);
}
if (isAccessConst(s->am))
{
throw RuntimeError(FStringView(
std::format("Variable `{}` is immutable", s->name.toBasicString())
));
}
s->value = v;
}
FString name() const { return resolve(slot)->name; }
TypeInfo declaredType() const { return resolve(slot)->declaredType; }
AccessModifier access() const { return resolve(slot)->am; }
private:
std::shared_ptr<VariableSlot> resolve(std::shared_ptr<VariableSlot> s) const
{
while (s->isRef) s = s->refTarget;
return s;
}
};
return *l.value == *r.value;
}
} // namespace Fig