#pragma once #include #include #include #include #include "arena.hpp" #include "error.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() const = 0; virtual PodObject* pod() = 0; virtual void move(Object*) = 0; virtual ~Object() = default; virtual Result cmp(Object&) = 0; virtual Result cmp(Nil&) { return cmp_tag(tag(), Tag::Nil); } virtual Result cmp(Int64&) { return cmp_tag(tag(), Tag::Int64); } virtual Result cmp(Float&) { return cmp_tag(tag(), Tag::Float); } virtual Result cmp(Bool&) { return cmp_tag(tag(), Tag::Bool); } virtual Result cmp(String&) { return cmp_tag(tag(), Tag::String); } virtual Result cmp(Symbol&) { return cmp_tag(tag(), Tag::Symbol); } virtual Result cmp(Syntax&) { return cmp_tag(tag(), Tag::Syntax); } virtual Result cmp(Pair&) { return cmp_tag(tag(), Tag::Pair); } virtual Result cmp(Array&) { return cmp_tag(tag(), Tag::Array); } virtual Result cmp(ByteArray&) { return cmp_tag(tag(), Tag::ByteArray); } virtual Result get(Value& key); 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(Object& rhs) final { return -TRY(rhs.cmp(*this)); } virtual Result cmp(Nil& rhs) final { return 0; } static Result create(PodNil* obj) { return Nil(TRY(MkGcRoot(obj))); } static Result create() { auto pod = TRY(arena_alloc()); pod->header.tag = Tag::Nil; return Nil(TRY(MkGcRoot(pod))); } virtual Result copy() const 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(Object& rhs) final { return -TRY(rhs.cmp(*this)); } virtual Result cmp(Array& rhs) final { return 0; } virtual void move(Object* obj) final { new (obj) Array(std::move(_value)); } static Result create(PodArray* obj) { return Array(TRY(MkGcRoot(obj))); } static Result create() { auto pod = TRY(arena_alloc()); pod->header.tag = Tag::Array; pod->size = 0; return Array(TRY(MkGcRoot(pod))); } uint64_t size() { return _value->size; } virtual Result copy() const final; Result get(uint64_t idx); virtual Result get(Value& key) final; Result append(Value& rhs); template Result append(const V& value); Result concat(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; for (uint64_t i = 0; i < size(); i++) { pod->data[i] = _value->data[i]; } for (uint64_t i = size(); i < rhs.size(); i++) { pod->data[i] = rhs._value->data[i]; } return Array(TRY(MkGcRoot(pod))); } Result sub(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; for (uint64_t i = 0; i < end - start; i++) { pod->data[i] = _value->data[start + i]; } return Array(TRY(MkGcRoot(pod))); } 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(Object& rhs) final { return -TRY(rhs.cmp(*this)); } virtual Result cmp(ByteArray& rhs) final { return 0; } virtual void move(Object* obj) final { new (obj) ByteArray(std::move(_value)); } static Result create(PodByteArray* obj) { return ByteArray(TRY(MkGcRoot(obj))); } static Result create(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))); } static Result create(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))); } static Result create(String& str); uint64_t size() { return _value->size; } virtual Result copy() const final; Result operator[](uint64_t idx) { if (idx >= _value->size) return ErrorCode::IndexOutOfRange; return _value->data[idx]; } Result concat(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))); } Result concat(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))); } Result concat(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))); } Result sub(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))); } 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(Object& rhs) final { return -TRY(rhs.cmp(*this)); } virtual Result cmp(Dict& rhs) final; virtual void move(Object* obj) final { new (obj) Dict(std::move(_value)); } static Result create(PodDict* obj) { return Dict(TRY(MkGcRoot(obj))); } static Result create() { auto pod = TRY(arena_alloc()); pod->header.tag = Tag::Dict; pod->size = 0; return Dict(TRY(MkGcRoot(pod))); } virtual Result copy() const final; virtual Result get(Value& key) final; Result insert(Value& key, Value& value); template Result insert(const K& key, const V& value); Result concat(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(2 * res_size * sizeof(PodObject*))); pod->size = res_size; for (uint64_t i = 0; i < 2 * size(); i++) { pod->data[i] = _value->data[i]; } for (uint64_t i = 2 * size(); i < 2 * rhs.size(); i++) { pod->data[i] = rhs._value->data[i]; } return Dict(TRY(MkGcRoot(pod))); } private: Result find(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(Object& rhs) final { return -TRY(rhs.cmp(*this)); } virtual Result cmp(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(PodString* obj) { return String(TRY(MkGcRoot(obj))); } static Result create(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))); } static Result create(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))); } uint64_t size() { return _value->size; } virtual Result copy() const final; Result operator[](uint64_t idx) { if (idx >= _value->size) return ErrorCode::IndexOutOfRange; return _value->data[idx]; } Result concat(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))); } Result concat(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))); } Result concat(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))); } Result sub(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))); } 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(Object& rhs) final { return -TRY(rhs.cmp(*this)); } virtual Result cmp(Symbol& 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) Symbol(std::move(_value)); } static Result create(PodSymbol* obj) { return Symbol(TRY(MkGcRoot(obj))); } static Result create(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))); } static Result create(const char* str) { uint64_t size = strlen(str); auto pod = TRY(arena_alloc(size * sizeof(char32_t))); pod->header.tag = Tag::Symbol; for (uint64_t i = 0; i < size; i++) pod->data[i] = str[i]; pod->size = size; return Symbol(TRY(MkGcRoot(pod))); } static Result create(String& rhs); virtual Result copy() const 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(Object& rhs) final { return -TRY(rhs.cmp(*this)); } virtual Result cmp(Syntax& rhs) final { return 0; } virtual void move(Object* obj) final { new (obj) Syntax(std::move(_value)); } static Result create(PodSyntax* obj) { return Syntax(TRY(MkGcRoot(obj))); } Result get_value(); virtual Result copy() const 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(Object& rhs) final { return -TRY(rhs.cmp(*this)); } virtual Result cmp(Pair& rhs) final { return 0; } virtual void move(Object* obj) final { new (obj) Pair(std::move(_value)); } static Result create(PodPair* obj) { return Pair(TRY(MkGcRoot(obj))); } static Result create(Value& first, Value& rest); Result first(); Result rest(); virtual Result copy() const 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(Object& rhs) final { return -TRY(rhs.cmp(*this)); } virtual Result cmp(Int64& rhs) final { return (_value->value > rhs._value->value) - (_value->value < rhs._value->value); } virtual Result cmp(Float& rhs) final; virtual void move(Object* obj) final { new (obj) Int64(std::move(_value)); } static Result create(PodInt64* obj) { return Int64(TRY(MkGcRoot(obj))); } static Result create(double val) { auto pod = TRY(arena_alloc()); pod->header.tag = Tag::Int64; pod->value = val; return Int64(TRY(MkGcRoot(pod))); } int64_t value() { return _value->value; } virtual Result copy() const 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(Object& rhs) final { return -TRY(rhs.cmp(*this)); } virtual Result cmp(Float& rhs) final { return (_value->value > rhs._value->value) - (_value->value < rhs._value->value); } virtual Result cmp(Int64& rhs) final; virtual void move(Object* obj) final { new (obj) Float(std::move(_value)); } static Result create(PodFloat* obj) { return Float(TRY(MkGcRoot(obj))); } static Result create(double val) { auto pod = TRY(arena_alloc()); pod->header.tag = Tag::Float; pod->value = val; return Float(TRY(MkGcRoot(pod))); } double value() { return _value->value; } virtual Result copy() const 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(Object& rhs) final { return -TRY(rhs.cmp(*this)); } virtual Result cmp(Bool& rhs) final { return 0; } virtual void move(Object* obj) final { new (obj) Bool(std::move(_value)); } static Result create(PodBool* obj) { return Bool(TRY(MkGcRoot(obj))); } static Result create(bool val) { auto pod = TRY(arena_alloc()); pod->header.tag = Tag::Bool; pod->value = val; return Bool(TRY(MkGcRoot(pod))); } bool value() { return _value->value; } virtual Result copy() const 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(PodObject* obj); static Result create(const int64_t& value) { return Value(TRY(Int64::create(value))); } static Result create(const char* value) { return Value(TRY(String::create(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(Value& rhs) { return ((Object*)buf)->cmp(*(Object*)rhs.buf); } Result get(Value& key) { return ((Object*)buf)->get(key); } Result get(int64_t key) { Value k = TRY(Int64::create(key)); return ((Object*)buf)->get(k); } // 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() const { return ((Object*)buf)->copy(); } private: uint8_t buf[24]; }; Result syntax_unwrap(Value& val); Result reverse(Value& val); Result debug_print(String& val); Result debug_print(Value& val); template Result Dict::insert(const K& key, const V& value) { Value k = TRY(Value::create(key)); Value v = TRY(Value::create(value)); return insert(k, v); } template Result Array::append(const V& value) { Value v = TRY(Value::create(value)); return append(v); }