#pragma once #include #include #include #include #include "arena.hpp" #include "pod.hpp" #include "utf8.hpp" // Forward declarations class Value; class String; class Nil; class Int64; class Float; class Bool; class String; class Symbol; class Syntax; class Pair; class Array; class ByteArray; class Writer; short cmp_tag(Tag lhs, Tag rhs); class Object { public: virtual Tag tag() = 0; virtual Result copy(Arena& arena) = 0; virtual PodObject* pod() = 0; virtual void move(Object*) = 0; virtual ~Object() = default; virtual Result cmp(Arena&, Object&) = 0; virtual Result cmp(Arena&, Nil&) { return cmp_tag(tag(), Tag::Nil); } virtual Result cmp(Arena&, Int64&) { return cmp_tag(tag(), Tag::Int64); } virtual Result cmp(Arena&, Float&) { return cmp_tag(tag(), Tag::Float); } virtual Result cmp(Arena&, Bool&) { return cmp_tag(tag(), Tag::Bool); } virtual Result cmp(Arena&, String&) { return cmp_tag(tag(), Tag::String); } virtual Result cmp(Arena&, Symbol&) { return cmp_tag(tag(), Tag::Symbol); } virtual Result cmp(Arena&, Syntax&) { return cmp_tag(tag(), Tag::Syntax); } virtual Result cmp(Arena&, Pair&) { return cmp_tag(tag(), Tag::Pair); } virtual Result cmp(Arena&, Array&) { return cmp_tag(tag(), Tag::Array); } virtual Result cmp(Arena&, ByteArray&) { return cmp_tag(tag(), Tag::ByteArray); } Object() = default; Object(const Object&) = delete; }; class Nil : public Object { public: Nil() {} Nil(Nil&& rhs) : _value(std::move(rhs._value)) {} Nil(GcRoot&& val) : _value(std::move(val)) {} virtual Tag tag() final { return Tag::Nil; } virtual PodObject* pod() final { return _value.get(); } virtual Result cmp(Arena& arena, Object& rhs) final { return -TRY(rhs.cmp(arena, *this)); } virtual Result cmp(Arena&, Nil& rhs) final { return 0; } static Result create(Arena& arena, PodNil* obj) { return Nil(TRY(MkGcRoot(obj, arena))); } static Result create(Arena& arena) { auto pod = TRY(arena.alloc()); pod->header.tag = Tag::Nil; return Nil(TRY(MkGcRoot(pod, arena))); } virtual Result copy(Arena& arena) final; virtual void move(Object* obj) final { new (obj) Nil(std::move(_value)); } private: GcRoot _value; }; class Array : public Object { public: Array() {} Array(Array&& rhs) : _value(std::move(rhs._value)) {} Array(GcRoot&& val) : _value(std::move(val)) {} Array& operator=(Array&& rhs) { _value = std::move(rhs._value); return *this; } virtual Tag tag() final { return Tag::Array; } virtual PodObject* pod() final { return _value.get(); } virtual Result cmp(Arena& arena, Object& rhs) final { return -TRY(rhs.cmp(arena, *this)); } virtual Result cmp(Arena&, Array& rhs) final { return 0; } virtual void move(Object* obj) final { new (obj) Array(std::move(_value)); } static Result create(Arena& arena, PodArray* obj) { return Array(TRY(MkGcRoot(obj, arena))); } static Result create(Arena& arena) { auto pod = TRY(arena.alloc()); pod->header.tag = Tag::Array; pod->size = 0; return Array(TRY(MkGcRoot(pod, arena))); } uint64_t size() { return _value->size; } virtual Result copy(Arena& arena) final; Result get(Arena& arena, uint64_t idx); Result append(Arena& arena, Value& rhs); Result concat(Arena& arena, Array& rhs) { uint64_t rhs_size = rhs.size(); uint64_t lhs_size = size(); uint64_t res_size = lhs_size + rhs_size; auto pod = TRY(arena.alloc(res_size * sizeof(PodObject*))); pod->size = res_size; memcpy(pod->data, _value->data, sizeof(PodObject*) * lhs_size); memcpy(pod->data + lhs_size, rhs._value->data, sizeof(char) * rhs_size); return Array(TRY(MkGcRoot(pod, arena))); } Result sub(Arena& arena, uint64_t start, uint64_t end) { if (start > end) return ErrorCode::IndexOutOfRange; uint64_t res_size = end - start; auto pod = TRY(arena.alloc(res_size * sizeof(PodObject*))); pod->size = res_size; memcpy(pod->data, _value->data + start, sizeof(PodObject*) * res_size); return Array(TRY(MkGcRoot(pod, arena))); } private: GcRoot _value; }; class ByteArray : public Object { public: ByteArray() {} ByteArray(ByteArray&& rhs) : _value(std::move(rhs._value)) {} ByteArray(GcRoot&& val) : _value(std::move(val)) {} ByteArray& operator=(ByteArray&& rhs) { _value = std::move(rhs._value); return *this; } virtual Tag tag() final { return Tag::ByteArray; } virtual PodObject* pod() final { return _value.get(); } virtual Result cmp(Arena& arena, Object& rhs) final { return -TRY(rhs.cmp(arena, *this)); } virtual Result cmp(Arena&, ByteArray& rhs) final { return 0; } virtual void move(Object* obj) final { new (obj) ByteArray(std::move(_value)); } static Result create(Arena& arena, PodByteArray* obj) { return ByteArray(TRY(MkGcRoot(obj, arena))); } static Result create(Arena& arena, char* chars, int64_t size) { auto pod = TRY(arena.alloc(size * sizeof(char))); pod->header.tag = Tag::ByteArray; memcpy(pod->data, chars, size * sizeof(char32_t)); pod->size = size; return ByteArray(TRY(MkGcRoot(pod, arena))); } static Result create(Arena& arena, const char* str) { uint64_t size = strlen(str); auto pod = TRY(arena.alloc(size * sizeof(char32_t))); pod->header.tag = Tag::ByteArray; memcpy(pod->data, str, size); pod->size = size; return ByteArray(TRY(MkGcRoot(pod, arena))); } static Result create(Arena& arena, String& str); uint64_t size() { return _value->size; } virtual Result copy(Arena& arena) final; Result operator[](uint64_t idx) { if (idx >= _value->size) return ErrorCode::IndexOutOfRange; return _value->data[idx]; } Result concat(Arena& arena, const char* rhs) { uint64_t rhs_size = strlen(rhs); uint64_t lhs_size = size(); uint64_t res_size = lhs_size + rhs_size; auto pod = TRY(arena.alloc(res_size * sizeof(char))); pod->size = res_size; memcpy(pod->data, _value->data, sizeof(char) * lhs_size); memcpy(pod->data, rhs + lhs_size, sizeof(char) * rhs_size); return ByteArray(TRY(MkGcRoot(pod, arena))); } Result concat(Arena& arena, const char* rhs, uint64_t rhs_size) { uint64_t lhs_size = size(); uint64_t res_size = lhs_size + rhs_size; auto pod = TRY(arena.alloc(res_size * sizeof(char))); pod->size = res_size; memcpy(pod->data, _value->data, sizeof(char) * lhs_size); memcpy(pod->data, rhs + lhs_size, sizeof(char) * rhs_size); return ByteArray(TRY(MkGcRoot(pod, arena))); } Result concat(Arena& arena, ByteArray& rhs) { uint64_t rhs_size = rhs.size(); uint64_t lhs_size = size(); uint64_t res_size = lhs_size + rhs_size; auto pod = TRY(arena.alloc(res_size * sizeof(char))); pod->size = res_size; memcpy(pod->data, _value->data, sizeof(char) * lhs_size); memcpy(pod->data + lhs_size, rhs._value->data, sizeof(char) * rhs_size); return ByteArray(TRY(MkGcRoot(pod, arena))); } Result sub(Arena& arena, uint64_t start, uint64_t end) { if (start > end) return ErrorCode::IndexOutOfRange; uint64_t res_size = end - start; auto pod = TRY(arena.alloc(res_size * sizeof(char))); pod->size = res_size; memcpy(pod->data, _value->data + start, sizeof(char) * res_size); return ByteArray(TRY(MkGcRoot(pod, arena))); } private: GcRoot _value; }; class Dict : public Object { public: friend class Writer; Dict() {} Dict(Dict&& rhs) : _value(std::move(rhs._value)) {} Dict(GcRoot&& val) : _value(std::move(val)) {} Dict& operator=(Dict&& rhs) { _value = std::move(rhs._value); return *this; } virtual Tag tag() final { return Tag::Dict; } virtual PodObject* pod() final { return _value.get(); } virtual Result cmp(Arena& arena, Object& rhs) final { return -TRY(rhs.cmp(arena, *this)); } virtual Result cmp(Arena&, Dict& rhs) final; virtual void move(Object* obj) final { new (obj) Dict(std::move(_value)); } static Result create(Arena& arena, PodDict* obj) { return Dict(TRY(MkGcRoot(obj, arena))); } static Result create(Arena& arena) { auto pod = TRY(arena.alloc()); pod->header.tag = Tag::Dict; pod->size = 0; return Dict(TRY(MkGcRoot(pod, arena))); } virtual Result copy(Arena& arena) final; Result get(Arena& arena, Value& key); Result insert(Arena& arena, Value& key, Value& value); template Result insert(Arena& arena, const K& key, const V& value); Result concat(Arena& arena, Dict& rhs) { uint64_t rhs_size = rhs.size(); uint64_t lhs_size = size(); uint64_t res_size = lhs_size + rhs_size; auto pod = TRY(arena.alloc(res_size * sizeof(PodObject*))); pod->size = res_size; memcpy(pod->data, _value->data, sizeof(PodObject*) * lhs_size); memcpy(pod->data + lhs_size, rhs._value->data, sizeof(char) * rhs_size); return Dict(TRY(MkGcRoot(pod, arena))); } private: Result find(Arena& arena, Value& key); uint64_t size() { return _value->size; } GcRoot _value; }; class String : public Object { public: String() {} String(String&& rhs) : _value(std::move(rhs._value)) {} String(GcRoot&& val) : _value(std::move(val)) {} String& operator=(String&& rhs) { _value = std::move(rhs._value); return *this; } virtual Tag tag() final { return Tag::String; } virtual PodObject* pod() final { return _value.get(); } virtual Result cmp(Arena& arena, Object& rhs) final { return -TRY(rhs.cmp(arena, *this)); } virtual Result cmp(Arena&, String& rhs) final { auto lsize = size(); auto rsize = rhs.size(); uint64_t i = 0; uint64_t j = 0; while (1) { if (i == lsize && j == lsize) return 0; short cmp = short(i == lsize) - short(j == rsize); if (cmp != 0) return cmp; char32_t lc = _value->data[i]; char32_t rc = rhs._value->data[j]; cmp = (lc > rc) - (lc < rc); if (cmp != 0) return cmp; i++; j++; } return 0; } virtual void move(Object* obj) final { new (obj) String(std::move(_value)); } static Result create(Arena& arena, PodString* obj) { return String(TRY(MkGcRoot(obj, arena))); } static Result create(Arena& arena, char32_t* chars, int64_t size) { auto pod = TRY(arena.alloc(size * sizeof(char32_t))); pod->header.tag = Tag::String; memcpy(pod->data, chars, size * sizeof(char32_t)); pod->size = size; return String(TRY(MkGcRoot(pod, arena))); } static Result create(Arena& arena, const char* str) { uint64_t size = strlen(str); auto pod = TRY(arena.alloc(size * sizeof(char32_t))); pod->header.tag = Tag::String; for (uint64_t i = 0; i < size; i++) pod->data[i] = str[i]; pod->size = size; return String(TRY(MkGcRoot(pod, arena))); } uint64_t size() { return _value->size; } virtual Result copy(Arena& arena) final; Result operator[](uint64_t idx) { if (idx >= _value->size) return ErrorCode::IndexOutOfRange; return _value->data[idx]; } Result concat(Arena& arena, const char* rhs) { uint64_t rhs_size = strlen(rhs); uint64_t lhs_size = size(); uint64_t res_size = lhs_size + rhs_size; auto pod = TRY(arena.alloc(res_size * sizeof(char32_t))); pod->header.tag = Tag::String; pod->size = res_size; memcpy(pod->data, _value->data, sizeof(char32_t) * lhs_size); for (uint64_t i = 0; i < rhs_size; i++) pod->data[lhs_size + i] = rhs[i]; return String(TRY(MkGcRoot(pod, arena))); } Result concat(Arena& arena, const char32_t* rhs, uint64_t rhs_size) { uint64_t lhs_size = size(); uint64_t res_size = lhs_size + rhs_size; auto pod = TRY(arena.alloc(res_size * sizeof(char32_t))); pod->header.tag = Tag::String; pod->size = res_size; memcpy(pod->data, _value->data, sizeof(char32_t) * lhs_size); for (uint64_t i = 0; i < rhs_size; i++) pod->data[lhs_size + i] = rhs[i]; return String(TRY(MkGcRoot(pod, arena))); } Result concat(Arena& arena, String& rhs) { uint64_t rhs_size = rhs.size(); uint64_t lhs_size = size(); uint64_t res_size = lhs_size + rhs_size; auto pod = TRY(arena.alloc(res_size * sizeof(char32_t))); pod->header.tag = Tag::String; pod->size = res_size; memcpy(pod->data, _value->data, sizeof(char32_t) * lhs_size); memcpy(pod->data + lhs_size, rhs._value->data, sizeof(char32_t) * rhs_size); return String(TRY(MkGcRoot(pod, arena))); } Result sub(Arena& arena, uint64_t start, uint64_t end) { if (start > end) return ErrorCode::IndexOutOfRange; uint64_t res_size = end - start; auto pod = TRY(arena.alloc(res_size * sizeof(char32_t))); pod->header.tag = Tag::String; pod->size = res_size; memcpy(pod->data, _value->data + start, sizeof(char32_t) * res_size); return String(TRY(MkGcRoot(pod, arena))); } friend class Symbol; private: GcRoot _value; }; class Symbol : public Object { public: Symbol() {} Symbol(Symbol&& rhs) : _value(std::move(rhs._value)) {} Symbol(GcRoot&& val) : _value(std::move(val)) {} virtual Tag tag() final { return Tag::Symbol; } virtual PodObject* pod() final { return _value.get(); } virtual Result cmp(Arena& arena, Object& rhs) final { return -TRY(rhs.cmp(arena, *this)); } virtual Result cmp(Arena&, Symbol& rhs) final { return 0; } virtual void move(Object* obj) final { new (obj) Symbol(std::move(_value)); } static Result create(Arena& arena, PodSymbol* obj) { return Symbol(TRY(MkGcRoot(obj, arena))); } static Result create(Arena& arena, char32_t* chars, int64_t size) { auto pod = TRY(arena.alloc(size * sizeof(char32_t))); pod->header.tag = Tag::Symbol; memcpy(pod->data, chars, size * sizeof(char32_t)); return Symbol(TRY(MkGcRoot(pod, arena))); } static Result create(Arena& arena, String& rhs); virtual Result copy(Arena& arena) final; uint64_t size() { return _value->size; } Result operator[](uint64_t idx) { if (idx >= _value->size) return ErrorCode::IndexOutOfRange; return _value->data[idx]; } private: GcRoot _value; }; class Syntax : public Object { public: Syntax() {} Syntax(Syntax&& rhs) : _value(std::move(rhs._value)) {} Syntax(GcRoot&& val) : _value(std::move(val)) {} Syntax(String filename, String modulename, Value expression); virtual Tag tag() final { return Tag::Syntax; } virtual PodObject* pod() final { return _value.get(); } virtual Result cmp(Arena& arena, Object& rhs) final { return -TRY(rhs.cmp(arena, *this)); } virtual Result cmp(Arena&, Syntax& rhs) final { return 0; } virtual void move(Object* obj) final { new (obj) Syntax(std::move(_value)); } static Result create(Arena& arena, PodSyntax* obj) { return Syntax(TRY(MkGcRoot(obj, arena))); } Result get_value(Arena& arena); virtual Result copy(Arena& arena) final; private: GcRoot _value; }; class Pair : public Object { public: Pair() {} Pair(Pair&& rhs) : _value(std::move(rhs._value)) {} Pair(GcRoot&& val) : _value(std::move(val)) {} virtual Tag tag() final { return Tag::Pair; } virtual PodObject* pod() final { return _value.get(); } virtual Result cmp(Arena& arena, Object& rhs) final { return -TRY(rhs.cmp(arena, *this)); } virtual Result cmp(Arena&, Pair& rhs) final { return 0; } virtual void move(Object* obj) final { new (obj) Pair(std::move(_value)); } static Result create(Arena& arena, PodPair* obj) { return Pair(TRY(MkGcRoot(obj, arena))); } static Result create(Arena& arena, Value& first, Value& rest); Result first(Arena& arena); Result rest(Arena& arena); virtual Result copy(Arena& arena) final; private: GcRoot _value; }; class Int64 : public Object { public: friend class Float; Int64() {} Int64(Int64&& rhs) : _value(std::move(rhs._value)) {} Int64(GcRoot&& val) : _value(std::move(val)) {} virtual Tag tag() final { return Tag::Int64; } virtual PodObject* pod() final { return _value.get(); } virtual Result cmp(Arena& arena, Object& rhs) final { return -TRY(rhs.cmp(arena, *this)); } virtual Result cmp(Arena&, Int64& rhs) final { return (_value->value > rhs._value->value) - (_value->value < rhs._value->value); } virtual Result cmp(Arena&, Float& rhs) final; virtual void move(Object* obj) final { new (obj) Int64(std::move(_value)); } static Result create(Arena& arena, PodInt64* obj) { return Int64(TRY(MkGcRoot(obj, arena))); } static Result create(Arena& arena, double val) { auto pod = TRY(arena.alloc()); pod->header.tag = Tag::Int64; pod->value = val; return Int64(TRY(MkGcRoot(pod, arena))); } int64_t value() { return _value->value; } virtual Result copy(Arena& arena) final; private: GcRoot _value; }; class Float : public Object { public: friend class Int64; Float() {} Float(Float&& rhs) : _value(std::move(rhs._value)) {} Float(GcRoot&& val) : _value(std::move(val)) {} virtual Tag tag() final { return Tag::Float; } virtual PodObject* pod() final { return _value.get(); } virtual Result cmp(Arena& arena, Object& rhs) final { return -TRY(rhs.cmp(arena, *this)); } virtual Result cmp(Arena&, Float& rhs) final { return (_value->value > rhs._value->value) - (_value->value < rhs._value->value); } virtual Result cmp(Arena&, Int64& rhs) final; virtual void move(Object* obj) final { new (obj) Float(std::move(_value)); } static Result create(Arena& arena, PodFloat* obj) { return Float(TRY(MkGcRoot(obj, arena))); } static Result create(Arena& arena, double val) { auto pod = TRY(arena.alloc()); pod->header.tag = Tag::Float; pod->value = val; return Float(TRY(MkGcRoot(pod, arena))); } double value() { return _value->value; } virtual Result copy(Arena& arena) final; private: GcRoot _value; }; class Bool : public Object { public: Bool() {} Bool(Bool&& rhs) : _value(std::move(rhs._value)) {} Bool(GcRoot&& val) : _value(std::move(val)) {} virtual Tag tag() final { return Tag::Bool; } virtual PodObject* pod() final { return _value.get(); } virtual Result cmp(Arena& arena, Object& rhs) final { return -TRY(rhs.cmp(arena, *this)); } virtual Result cmp(Arena&, Bool& rhs) final { return 0; } virtual void move(Object* obj) final { new (obj) Bool(std::move(_value)); } static Result create(Arena& arena, PodBool* obj) { return Bool(TRY(MkGcRoot(obj, arena))); } static Result create(Arena& arena, bool val) { auto pod = TRY(arena.alloc()); pod->header.tag = Tag::Bool; pod->value = val; return Bool(TRY(MkGcRoot(pod, arena))); } bool value() { return _value->value; } virtual Result copy(Arena& arena) final; private: GcRoot _value; }; // note: this class doesn't perform proper destruction of objects in some cases class Value { public: Value() { new (buf) Nil(); } ~Value() { ((Object*)buf)->~Object(); } Value(Value&& val) { ((Object*)val.buf)->move((Object*)buf); new (val.buf) Nil(); } Value(const Value&) = delete; template Value(T&& obj) requires std::derived_from { new (buf) T(std::move(obj)); } Value& operator=(Value&& val) { ((Object*)buf)->~Object(); ((Object*)val.buf)->move((Object*)buf); new (val.buf) Nil(); return *this; } static Result create(Arena& arena, PodObject* obj); static Result create(Arena& arena, const int64_t& value) { return Value(TRY(Int64::create(arena, value))); } static Result create(Arena& arena, const char* value) { return Value(TRY(String::create(arena, value))); } template bool is() { return dynamic_cast((Object*)buf) != nullptr; } template T* to() { return dynamic_cast((Object*)buf); } PodObject* pod() { return ((Object*)buf)->pod(); } Tag tag() { return ((Object*)buf)->tag(); } Object& operator*() { return *(Object*)(buf); } Object* operator->() { return (Object*)(buf); } Result cmp(Arena& arena, Value& rhs) { return ((Object*)buf)->cmp(arena, *(Object*)rhs.buf); } // TODO: cmp() probably doesn't need arena parameter // Result operator==(Value& rhs) { return TRY(cmp(rhs)) == 0; } // Result operator!=(Value& rhs) { return TRY(cmp(rhs)) != 0; } Result copy(Arena& arena) { return ((Object*)buf)->copy(arena); } private: uint8_t buf[24]; }; Result syntax_unwrap(Value& val); Result reverse(Arena& arena, Value& val); Result debug_print(Arena& arena, String& val); Result debug_print(Arena& arena, Value& val); template Result Dict::insert(Arena& arena, const K& key, const V& value) { Value k = TRY(Value::create(arena, key)); Value v = TRY(Value::create(arena, value)); return insert(arena, k, v); }