#pragma once #include #include #include #include #include #include #include #include #include #include namespace Fig { inline bool isDoubleInteger(ValueType::DoubleClass d) { return std::floor(d) == d; } inline bool isNumberExceededIntLimit(ValueType::DoubleClass d) { static constexpr ValueType::DoubleClass intMaxAsDouble = static_cast(std::numeric_limits::max()); static constexpr ValueType::DoubleClass intMinAsDouble = static_cast(std::numeric_limits::min()); return d > intMaxAsDouble || d < intMinAsDouble; } class Value { public: using VariantType = std::variant; VariantType data; Value() : data(Null{}) {} Value(const Null &n) : data(std::in_place_type, n) {} Value(const Int &i) : data(std::in_place_type, i) {} Value(const Double &d) : data(std::in_place_type, d) { ValueType::IntClass casted = static_cast(d.getValue()); if (casted == d.getValue()) { data.emplace(casted); } } Value(const String &s) : data(std::in_place_type, s) {} Value(const Bool &b) : data(std::in_place_type, b) {} Value(const Function &f) : data(std::in_place_type, f) {} Value(const StructType &s) : data(std::in_place_type, s) {} Value(const StructInstance &s) : data(std::in_place_type, s) {} template || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v>> Value(const T &val) { // 不可以用 data = 的形式 // __ValueWrapper 构造、拷贝有限制 if constexpr (std::is_same_v) data.emplace(val); else if constexpr (std::is_same_v) { ValueType::IntClass casted = static_cast(val); if (casted == val) { data.emplace(casted); } else { data.emplace(val); } } else if constexpr (std::is_same_v) data.emplace(val); else if constexpr (std::is_same_v) data.emplace(val); else if constexpr (std::is_same_v) data.emplace(val); else if constexpr (std::is_same_v) data.emplace(val); else if constexpr (std::is_same_v) data.emplace(val); } Value(const Value &) = default; Value(Value &&) noexcept = default; Value &operator=(const Value &) = default; Value &operator=(Value &&) noexcept = default; static Value defaultValue(TypeInfo ti) { if (ti == ValueType::Int) return Value(Int(0)); else if (ti == ValueType::Double) return Value(Double(0.0)); else if (ti == ValueType::String) return Value(String(u8"")); else if (ti == ValueType::Bool) return Value(Bool(false)); else if (ti == ValueType::Function) return getNullInstance(); else if (ti == ValueType::StructType) return getNullInstance(); else if (ti == ValueType::StructInstance) return getNullInstance(); else return getNullInstance(); } template bool is() const { return std::holds_alternative(data); } template T &as() { return std::get(data); } template const T &as() const { return std::get(data); } static Value getNullInstance() { static Value v(Null{}); return v; } TypeInfo getTypeInfo() const { return std::visit([](auto &&val) { return val.ti; }, data); } bool isNull() const { return is(); } bool isNumeric() const { return is() || is(); } ValueType::DoubleClass getNumericValue() const { if (is()) return static_cast(as().getValue()); else if (is()) return as().getValue(); else throw RuntimeError(u8"getNumericValue: Not a numeric value"); } FString toString() const { if (is()) return FString(u8"null"); if (is()) return FString(std::to_string(as().getValue())); if (is()) return FString(std::to_string(as().getValue())); if (is()) return as().getValue(); if (is()) return as().getValue() ? FString(u8"true") : FString(u8"false"); if (is()) { return FString(std::format("", as().getValue().id, static_cast(as().data.get()))); } if (is()) { return FString(std::format("", as().getValue().id, static_cast(as().data.get()))); } if (is()) { return FString(std::format("().getValue().structName.toBasicString(), static_cast(as().data.get()))); } return FString(u8""); } private: static std::string makeTypeErrorMessage(const char *prefix, const char *op, const Value &lhs, const Value &rhs) { auto lhs_type = std::visit([](auto &&v) { return v.ti.name.toBasicString(); }, lhs.data); auto rhs_type = std::visit([](auto &&v) { return v.ti.name.toBasicString(); }, rhs.data); return std::format("{}: {} '{}' {}", prefix, lhs_type, op, rhs_type); } public: // math friend Value operator+(const Value &lhs, const Value &rhs) { if (lhs.isNull() || rhs.isNull()) throw ValueError(FStringView(makeTypeErrorMessage("Cannot add", "+", lhs, rhs))); if (lhs.isNumeric() and rhs.isNumeric()) { ValueType::DoubleClass result = lhs.getNumericValue() + rhs.getNumericValue(); if (isDoubleInteger(result) and !isNumberExceededIntLimit(result)) { return Value(static_cast(result)); } return Value(ValueType::DoubleClass(result)); } if (lhs.is() && rhs.is()) return Value(ValueType::StringClass(lhs.as().getValue() + rhs.as().getValue())); throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "+", lhs, rhs))); } friend Value operator-(const Value &lhs, const Value &rhs) { if (lhs.isNull() || rhs.isNull()) throw ValueError(FStringView(makeTypeErrorMessage("Cannot subtract", "-", lhs, rhs))); if (lhs.isNumeric() and rhs.isNumeric()) { ValueType::DoubleClass result = lhs.getNumericValue() - rhs.getNumericValue(); if (isDoubleInteger(result) and !isNumberExceededIntLimit(result)) { return Value(static_cast(result)); } return Value(ValueType::DoubleClass(result)); } throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "-", lhs, rhs))); } friend Value operator*(const Value &lhs, const Value &rhs) { if (lhs.isNull() || rhs.isNull()) throw ValueError(FStringView(makeTypeErrorMessage("Cannot multiply", "*", lhs, rhs))); if (lhs.isNumeric() and rhs.isNumeric()) { ValueType::DoubleClass result = lhs.getNumericValue() * rhs.getNumericValue(); if (isDoubleInteger(result) and !isNumberExceededIntLimit(result)) { return Value(static_cast(result)); } return Value(ValueType::DoubleClass(result)); } throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "*", lhs, rhs))); } friend Value operator/(const Value &lhs, const Value &rhs) { if (lhs.isNull() || rhs.isNull()) throw ValueError(FStringView(makeTypeErrorMessage("Cannot divide", "/", lhs, rhs))); if (lhs.isNumeric() and rhs.isNumeric()) { ValueType::DoubleClass rnv = rhs.getNumericValue(); if (rnv == 0) throw ValueError(FStringView(makeTypeErrorMessage("Division by zero", "/", lhs, rhs))); ValueType::DoubleClass result = lhs.getNumericValue() / rnv; if (isDoubleInteger(result) and !isNumberExceededIntLimit(result)) { return Value(static_cast(result)); } return Value(ValueType::DoubleClass(result)); } throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "/", lhs, rhs))); } friend Value operator%(const Value &lhs, const Value &rhs) { if (lhs.isNull() || rhs.isNull()) throw ValueError(FStringView(makeTypeErrorMessage("Cannot modulo", "%", lhs, rhs))); if (lhs.isNumeric() and rhs.isNumeric()) { ValueType::DoubleClass rnv = rhs.getNumericValue(); if (rnv == 0) throw ValueError(FStringView(makeTypeErrorMessage("Modulo by zero", "/", lhs, rhs))); ValueType::DoubleClass result = fmod(lhs.getNumericValue(), rnv); if (isDoubleInteger(result) and !isNumberExceededIntLimit(result)) { return Value(static_cast(result)); } return Value(ValueType::DoubleClass(result)); } throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "%", lhs, rhs))); } // logic friend Value operator&&(const Value &lhs, const Value &rhs) { if (!lhs.is() || !rhs.is()) throw ValueError(FStringView(makeTypeErrorMessage("Logical AND requires bool", "&&", lhs, rhs))); return Value(lhs.as().getValue() && rhs.as().getValue()); } friend Value operator||(const Value &lhs, const Value &rhs) { if (!lhs.is() || !rhs.is()) throw ValueError(FStringView(makeTypeErrorMessage("Logical OR requires bool", "||", lhs, rhs))); return Value(lhs.as().getValue() || rhs.as().getValue()); } friend Value operator!(const Value &v) { if (!v.is()) throw ValueError(FStringView(std::format("Logical NOT requires bool: '{}'", std::visit([](auto &&val) { return val.ti.name.toBasicString(); }, v.data)))); return Value(!v.as().getValue()); } friend Value operator-(const Value &v) { if (v.isNull()) throw ValueError(FStringView(std::format("Unary minus cannot be applied to null"))); if (v.is()) return Value(-v.as().getValue()); if (v.is()) return Value(-v.as().getValue()); throw ValueError(FStringView(std::format("Unary minus requires int or double: '{}'", std::visit([](auto &&val) { return val.ti.name.toBasicString(); }, v.data)))); } friend Value operator~(const Value &v) { if (!v.is()) throw ValueError(FStringView(std::format("Bitwise NOT requires int: '{}'", std::visit([](auto &&val) { return val.ti.name.toBasicString(); }, v.data)))); return Value(~v.as().getValue()); } // compare → now returns bool friend bool operator==(const Value &lhs, const Value &rhs) { return lhs.data == rhs.data; } friend bool operator!=(const Value &lhs, const Value &rhs) { return !(lhs.data == rhs.data); } friend bool operator<(const Value &lhs, const Value &rhs) { if (lhs.isNumeric() and rhs.isNumeric()) return lhs.getNumericValue() < rhs.getNumericValue(); if (lhs.is() && rhs.is()) return lhs.as().getValue() < rhs.as().getValue(); throw ValueError(FStringView(makeTypeErrorMessage("Unsupported comparison", "<", lhs, rhs))); } friend bool operator<=(const Value &lhs, const Value &rhs) { return lhs == rhs or lhs < rhs; } friend bool operator>(const Value &lhs, const Value &rhs) { if (lhs.isNumeric() and rhs.isNumeric()) return lhs.getNumericValue() > rhs.getNumericValue(); if (lhs.is() && rhs.is()) return lhs.as().getValue() > rhs.as().getValue(); throw ValueError(FStringView(makeTypeErrorMessage("Unsupported comparison", ">", lhs, rhs))); } friend bool operator>=(const Value &lhs, const Value &rhs) { return lhs == rhs or lhs > rhs; } // bitwise friend Value bit_and(const Value &lhs, const Value &rhs) { if (!lhs.is() || !rhs.is()) throw ValueError(FStringView(makeTypeErrorMessage("Bitwise AND requires int", "&", lhs, rhs))); return Value(lhs.as().getValue() & rhs.as().getValue()); } friend Value bit_or(const Value &lhs, const Value &rhs) { if (!lhs.is() || !rhs.is()) throw ValueError(FStringView(makeTypeErrorMessage("Bitwise OR requires int", "|", lhs, rhs))); return Value(lhs.as().getValue() | rhs.as().getValue()); } friend Value bit_xor(const Value &lhs, const Value &rhs) { if (!lhs.is() || !rhs.is()) throw ValueError(FStringView(makeTypeErrorMessage("Bitwise XOR requires int", "^", lhs, rhs))); return Value(lhs.as().getValue() ^ rhs.as().getValue()); } friend Value bit_not(const Value &v) { if (!v.is()) throw ValueError(FStringView(std::format("Bitwise NOT requires int: '{}'", std::visit([](auto &&val) { return val.ti.name.toBasicString(); }, v.data)))); return Value(~v.as().getValue()); } friend Value shift_left(const Value &lhs, const Value &rhs) { if (!lhs.is() || !rhs.is()) throw ValueError(FStringView(makeTypeErrorMessage("Shift left requires int", "<<", lhs, rhs))); return Value(lhs.as().getValue() << rhs.as().getValue()); } friend Value shift_right(const Value &lhs, const Value &rhs) { if (!lhs.is() || !rhs.is()) throw ValueError(FStringView(makeTypeErrorMessage("Shift right requires int", ">>", lhs, rhs))); return Value(lhs.as().getValue() >> rhs.as().getValue()); } friend Value power(const Value &base, const Value &exp) { if (base.isNull() || exp.isNull()) throw ValueError(FStringView(makeTypeErrorMessage("Cannot exponentiate", "**", base, exp))); if (base.isNumeric() and exp.isNumeric()) { ValueType::DoubleClass result = std::pow(base.getNumericValue(), exp.getNumericValue()); if (isDoubleInteger(result) and !isNumberExceededIntLimit(result)) { return Value(static_cast(result)); } return Value(ValueType::DoubleClass(result)); } throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "**", base, exp))); } }; using Any = Value; } // namespace Fig