#pragma once #include "helperFunctions/ErrorUtils.h" #include #include #include #include #include #include #include // Forward declarations class Environment; class Function; class BuiltinFunction; class Thunk; // Type tags for the Value union enum ValueType { VAL_NONE, VAL_NUMBER, VAL_BOOLEAN, VAL_STRING, VAL_FUNCTION, VAL_BUILTIN_FUNCTION, VAL_THUNK, VAL_ARRAY }; // Tagged value system (like Lua) - no heap allocation for simple values struct Value { union { double number; bool boolean; Function* function; BuiltinFunction* builtin_function; Thunk* thunk; }; ValueType type; std::string string_value; // Store strings outside the union for safety std::shared_ptr > array_value; // Store arrays as shared_ptr for mutability // Constructors Value() : number(0.0), type(ValueType::VAL_NONE) {} Value(double n) : number(n), type(ValueType::VAL_NUMBER) {} Value(bool b) : boolean(b), type(ValueType::VAL_BOOLEAN) {} Value(const char* s) : type(ValueType::VAL_STRING), string_value(s ? s : "") {} Value(const std::string& s) : type(ValueType::VAL_STRING), string_value(s) {} Value(std::string&& s) : type(ValueType::VAL_STRING), string_value(std::move(s)) {} Value(Function* f) : function(f), type(ValueType::VAL_FUNCTION) {} Value(BuiltinFunction* bf) : builtin_function(bf), type(ValueType::VAL_BUILTIN_FUNCTION) {} Value(Thunk* t) : thunk(t), type(ValueType::VAL_THUNK) {} Value(const std::vector& arr) : type(ValueType::VAL_ARRAY), array_value(std::make_shared >(arr)) {} Value(std::vector&& arr) : type(ValueType::VAL_ARRAY), array_value(std::make_shared >(std::move(arr))) {} // Move constructor Value(Value&& other) noexcept : type(other.type), string_value(std::move(other.string_value)), array_value(std::move(other.array_value)) { if (type != ValueType::VAL_STRING && type != ValueType::VAL_ARRAY) { number = other.number; // Copy the union } other.type = ValueType::VAL_NONE; } // Move assignment Value& operator=(Value&& other) noexcept { if (this != &other) { type = other.type; if (type == ValueType::VAL_STRING) { string_value = std::move(other.string_value); } else if (type == ValueType::VAL_ARRAY) { array_value = std::move(other.array_value); // shared_ptr automatically handles moving } else { number = other.number; // Copy the union } other.type = ValueType::VAL_NONE; } return *this; } // Copy constructor (only when needed) Value(const Value& other) : type(other.type) { if (type == ValueType::VAL_STRING) { string_value = other.string_value; } else if (type == ValueType::VAL_ARRAY) { array_value = other.array_value; // shared_ptr automatically handles sharing } else { number = other.number; // Copy the union } } // Copy assignment (only when needed) Value& operator=(const Value& other) { if (this != &other) { type = other.type; if (type == ValueType::VAL_STRING) { string_value = other.string_value; } else if (type == ValueType::VAL_ARRAY) { array_value = other.array_value; // shared_ptr automatically handles sharing } else { number = other.number; // Copy the union } } return *this; } // Type checking (fast, no dynamic casting) - inline for performance inline bool isNumber() const { return type == ValueType::VAL_NUMBER; } inline bool isBoolean() const { return type == ValueType::VAL_BOOLEAN; } inline bool isString() const { return type == ValueType::VAL_STRING; } inline bool isFunction() const { return type == ValueType::VAL_FUNCTION; } inline bool isBuiltinFunction() const { return type == ValueType::VAL_BUILTIN_FUNCTION; } inline bool isArray() const { return type == ValueType::VAL_ARRAY; } inline bool isThunk() const { return type == ValueType::VAL_THUNK; } inline bool isNone() const { return type == ValueType::VAL_NONE; } // Get type name as string for error messages inline std::string getType() const { switch (type) { case ValueType::VAL_NONE: return "none"; case ValueType::VAL_NUMBER: return "number"; case ValueType::VAL_BOOLEAN: return "boolean"; case ValueType::VAL_STRING: return "string"; case ValueType::VAL_FUNCTION: return "function"; case ValueType::VAL_BUILTIN_FUNCTION: return "builtin_function"; case ValueType::VAL_THUNK: return "thunk"; case ValueType::VAL_ARRAY: return "array"; default: return "unknown"; } } // Value extraction (safe, with type checking) - inline for performance inline double asNumber() const { return isNumber() ? number : 0.0; } inline bool asBoolean() const { return isBoolean() ? boolean : false; } inline const std::string& asString() const { return string_value; } inline const std::vector& asArray() const { return *array_value; } inline std::vector& asArray() { return *array_value; } inline Function* asFunction() const { return isFunction() ? function : nullptr; } inline BuiltinFunction* asBuiltinFunction() const { return isBuiltinFunction() ? builtin_function : nullptr; } inline Thunk* asThunk() const { return isThunk() ? thunk : nullptr; } // Truthiness check - inline for performance inline bool isTruthy() const { switch (type) { case ValueType::VAL_NONE: return false; case ValueType::VAL_BOOLEAN: return boolean; case ValueType::VAL_NUMBER: return number != 0.0; case ValueType::VAL_STRING: return !string_value.empty(); case ValueType::VAL_FUNCTION: return function != nullptr; case ValueType::VAL_BUILTIN_FUNCTION: return builtin_function != nullptr; case ValueType::VAL_THUNK: return thunk != nullptr; default: return false; } } // Equality comparison - inline for performance inline bool equals(const Value& other) const { if (type != other.type) return false; switch (type) { case ValueType::VAL_NONE: return true; case ValueType::VAL_BOOLEAN: return boolean == other.boolean; case ValueType::VAL_NUMBER: return number == other.number; case ValueType::VAL_STRING: return string_value == other.string_value; case ValueType::VAL_FUNCTION: return function == other.function; case ValueType::VAL_BUILTIN_FUNCTION: return builtin_function == other.builtin_function; case ValueType::VAL_THUNK: return thunk == other.thunk; default: return false; } } // String representation std::string toString() const { switch (type) { case ValueType::VAL_NONE: return "none"; case ValueType::VAL_BOOLEAN: return boolean ? "true" : "false"; case ValueType::VAL_NUMBER: { // Format numbers like the original stringify function if (number == std::floor(number)) { return std::to_string(static_cast(number)); } else { std::string str = std::to_string(number); // Remove trailing zeros str.erase(str.find_last_not_of('0') + 1, std::string::npos); if (str.back() == '.') str.pop_back(); return str; } } case ValueType::VAL_STRING: return string_value; case ValueType::VAL_FUNCTION: return ""; case ValueType::VAL_BUILTIN_FUNCTION: return ""; case ValueType::VAL_THUNK: return ""; case ValueType::VAL_ARRAY: { const std::vector& arr = *array_value; std::string result = "["; for (size_t i = 0; i < arr.size(); i++) { if (i > 0) result += ", "; result += arr[i].toString(); } result += "]"; return result; } default: return "unknown"; } } // Arithmetic operators Value operator+(const Value& other) const { if (isNumber() && other.isNumber()) { return Value(number + other.number); } if (isString() && other.isString()) { return Value(string_value + other.string_value); } if (isString() && other.isNumber()) { return Value(string_value + other.toString()); } if (isNumber() && other.isString()) { return Value(toString() + other.string_value); } // Handle none values by converting to string if (isString() && other.isNone()) { return Value(string_value + "none"); } if (isNone() && other.isString()) { return Value("none" + other.string_value); } if (isString() && !other.isString() && !other.isNumber()) { return Value(string_value + other.toString()); } if (!isString() && !isNumber() && other.isString()) { return Value(toString() + other.string_value); } throw std::runtime_error(ErrorUtils::makeOperatorError("+", getType(), other.getType())); } Value operator-(const Value& other) const { if (isNumber() && other.isNumber()) { return Value(number - other.number); } throw std::runtime_error(ErrorUtils::makeOperatorError("-", getType(), other.getType())); } Value operator*(const Value& other) const { if (isNumber() && other.isNumber()) { return Value(number * other.number); } if (isString() && other.isNumber()) { std::string result; for (int i = 0; i < static_cast(other.number); ++i) { result += string_value; } return Value(result); } if (isNumber() && other.isString()) { std::string result; for (int i = 0; i < static_cast(number); ++i) { result += other.string_value; } return Value(result); } throw std::runtime_error(ErrorUtils::makeOperatorError("*", getType(), other.getType())); } Value operator/(const Value& other) const { if (isNumber() && other.isNumber()) { if (other.number == 0) { throw std::runtime_error("Division by zero"); } return Value(number / other.number); } throw std::runtime_error(ErrorUtils::makeOperatorError("/", getType(), other.getType())); } Value operator%(const Value& other) const { if (isNumber() && other.isNumber()) { return Value(fmod(number, other.number)); } throw std::runtime_error(ErrorUtils::makeOperatorError("%", getType(), other.getType())); } Value operator&(const Value& other) const { if (isNumber() && other.isNumber()) { return Value(static_cast(static_cast(number) & static_cast(other.number))); } throw std::runtime_error(ErrorUtils::makeOperatorError("&", getType(), other.getType())); } Value operator|(const Value& other) const { if (isNumber() && other.isNumber()) { return Value(static_cast(static_cast(number) | static_cast(other.number))); } throw std::runtime_error(ErrorUtils::makeOperatorError("|", getType(), other.getType())); } Value operator^(const Value& other) const { if (isNumber() && other.isNumber()) { return Value(static_cast(static_cast(number) ^ static_cast(other.number))); } throw std::runtime_error(ErrorUtils::makeOperatorError("^", getType(), other.getType())); } Value operator<<(const Value& other) const { if (isNumber() && other.isNumber()) { return Value(static_cast(static_cast(number) << static_cast(other.number))); } throw std::runtime_error(ErrorUtils::makeOperatorError("<<", getType(), other.getType())); } Value operator>>(const Value& other) const { if (isNumber() && other.isNumber()) { return Value(static_cast(static_cast(number) >> static_cast(other.number))); } throw std::runtime_error(ErrorUtils::makeOperatorError(">>", getType(), other.getType())); } }; // Global constants for common values extern const Value NONE_VALUE; extern const Value TRUE_VALUE; extern const Value FALSE_VALUE; extern const Value ZERO_VALUE; extern const Value ONE_VALUE;