From 69010737b905c2b289c12e0bca4ec19ac074a2c7 Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Mon, 12 Jun 2023 23:16:47 +0200 Subject: [PATCH] Begin sketch of C++ implementation --- implementations/cpp/.gitignore | 3 + implementations/cpp/Makefile | 8 + implementations/cpp/main.cpp | 5 + implementations/cpp/preserves.hpp | 348 +++++++++++++++++++++++++ implementations/cpp/preserves_text.hpp | 13 + 5 files changed, 377 insertions(+) create mode 100644 implementations/cpp/.gitignore create mode 100644 implementations/cpp/Makefile create mode 100644 implementations/cpp/main.cpp create mode 100644 implementations/cpp/preserves.hpp create mode 100644 implementations/cpp/preserves_text.hpp diff --git a/implementations/cpp/.gitignore b/implementations/cpp/.gitignore new file mode 100644 index 0000000..3548018 --- /dev/null +++ b/implementations/cpp/.gitignore @@ -0,0 +1,3 @@ +m.output.txt +m +.vscode diff --git a/implementations/cpp/Makefile b/implementations/cpp/Makefile new file mode 100644 index 0000000..737efbd --- /dev/null +++ b/implementations/cpp/Makefile @@ -0,0 +1,8 @@ +m: main.cpp preserves.hpp preserves_text.hpp + gcc -Wall -Wextra -Werror -g3 -o $@ main.cpp + +go: m + cat ../../tests/samples.bin | ./m | tee m.output.txt + +clean: + rm -f m diff --git a/implementations/cpp/main.cpp b/implementations/cpp/main.cpp new file mode 100644 index 0000000..22d8053 --- /dev/null +++ b/implementations/cpp/main.cpp @@ -0,0 +1,5 @@ +#include "preserves.hpp" + +int main() { + return 0; +} \ No newline at end of file diff --git a/implementations/cpp/preserves.hpp b/implementations/cpp/preserves.hpp new file mode 100644 index 0000000..6ee3105 --- /dev/null +++ b/implementations/cpp/preserves.hpp @@ -0,0 +1,348 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace Preserves { + + enum class ValueKind { + Boolean, + Float, + Double, + SignedInteger, + String, + ByteString, + Symbol, + + Record, + Sequence, + Set, + Dictionary, + + Embedded, + }; + + template class Record; + template class ValueImpl; + + template + class Value { + std::shared_ptr> p; + + public: + static Value from_bool(bool b); + static Value from_float(float f); + static Value from_double(double d); + static Value from_int(uint64_t i); + static Value from_int(int64_t i); + static Value from_string(std::string const& s); + static Value from_bytes(std::vector const& v); + static Value from_bytes(std::vector const& v); + static Value from_bytes(void *p, size_t len); + static Value from_symbol(std::string const& s); + + static Value record(Record const& r); + static Value record(Value const& label, std::vector const& fields); + static Value sequence(std::vector const& items); + static Value set(std::set const& items); + static Value dictionary(std::map const& entries); + + static Value from_embedded(std::shared_ptr const& p); + + static Value from_number(uint64_t i) { return from_int(i); } + static Value from_number(int64_t i) { return from_int(i); } + static Value from_number(float f) { return from_float(f); } + static Value from_number(double d) { return from_double(d); } + + static Value from(bool b) { return from_bool(b); } + static Value from(float f) { return from_float(f); } + static Value from(double d) { return from_double(d); } + static Value from(uint64_t i) { return from_int(i); } + static Value from(int64_t i) { return from_int(i); } + static Value from(std::string const& s) { return from_string(s); } + static Value from(std::vector const& v) { return from_bytes(v); } + static Value from(std::vector const& v) { return from_bytes(v); } + static Value from(void *p, size_t len) { return from_bytes(p, len); } + static Value from(Record const& r) { return record(r); } + static Value from(Value const& label, std::vector const& fields) { return record(label, fields); } + static Value from(std::vector const& items) { return sequence(items); } + static Value from(std::set const& items) { return set(items); } + static Value from(std::map const& entries) { return dictionary(entries); } + + ValueImpl& operator*() const { return *p; } + ValueImpl* operator->() const { return p.get(); } + + ValueKind value_kind() const; + bool is_mutable() const; + + bool is_bool() const { return value_kind() == ValueKind::Boolean; } + bool is_float() const { return value_kind() == ValueKind::Float; } + bool is_double() const { return value_kind() == ValueKind::Double; } + bool is_int() const { return value_kind() == ValueKind::SignedInteger; } + bool is_string() const { return value_kind() == ValueKind::String; } + bool is_bytes() const { return value_kind() == ValueKind::ByteString; } + bool is_symbol() const { return value_kind() == ValueKind::Symbol; } + bool is_record() const { return value_kind() == ValueKind::Record; } + bool is_sequence() const { return value_kind() == ValueKind::Sequence; } + bool is_set() const { return value_kind() == ValueKind::Set; } + bool is_dictionary() const { return value_kind() == ValueKind::Dictionary; } + + boost::optional as_bool() const; + bool to_bool() const { return as_bool().value(); } + + boost::optional as_float() const; + float to_float() const { return as_float().value(); } + + boost::optional as_double() const; + double to_double() const { return as_double().value(); } + + boost::optional as_unsigned() const; + uint64_t to_unsigned() const { return as_unsigned().value(); } + + boost::optional as_signed() const; + int64_t to_signed() const { return as_signed().value(); } + + boost::optional as_string() const; + std::string const& to_string() const { return as_string().value(); } + + boost::optional const&> as_bytes() const; + std::vector const& to_bytes() const { return as_bytes().value(); } + + boost::optional as_symbol() const; + std::string const& to_symbol() const { return as_symbol().value(); } + + boost::optional const&> as_record() const; + Record const& to_record() const { return as_record().value(); }; + + boost::optional const&> as_sequence() const; + std::vector const& to_sequence() const { return as_sequence().value(); } + + boost::optional const&> as_set() const; + std::set const& to_set() const { return as_set().value(); } + + boost::optional const&> as_dictionary() const; + std::map const& to_dictionary() const { return as_dictionary().value(); } + + boost::optional> as_embedded() const; + std::shared_ptr to_embedded() const { return as_embedded().value(); } + + boost::optional label() const; + size_t size() const; + bool contains(Value const& key) const; + boost::optional get(Value const& key) const; + Value operator[](Value const& key) const { return get(key).value(); } + boost::optional get(size_t index) const; + Value operator[](size_t index) const { return get(index).value(); } + }; + + template + class ValueImpl { + public: + virtual ~ValueImpl() {} + + virtual ValueKind value_kind() const = 0; + virtual bool is_mutable() const { return false; } + + virtual boost::optional as_bool() const { return boost::none; } + virtual boost::optional as_float() const { return boost::none; } + virtual boost::optional as_double() const { return boost::none; } + virtual boost::optional as_unsigned() const { return boost::none; } + virtual boost::optional as_signed() const { return boost::none; } + virtual boost::optional as_string() const { return boost::none; } + virtual boost::optional const&> as_bytes() const { return boost::none; } + virtual boost::optional as_symbol() const { return boost::none; } + virtual boost::optional const&> as_record() const { return boost::none; } + virtual boost::optional> const&> as_sequence() const { return boost::none; } + virtual boost::optional> const&> as_set() const { return boost::none; } + virtual boost::optional,Value> const&> as_dictionary() const { return boost::none; } + virtual boost::optional> as_embedded() const { return boost::none; } + + virtual boost::optional> label() const { return boost::none; } + virtual size_t size() const { return 0; } + virtual bool contains(Value const& key) const { return false; } + virtual boost::optional> get(Value const& key) const { return boost::none; } + virtual boost::optional> get(size_t index) const { return boost::none; } + + virtual bool add(Value const& item) { + throw std::runtime_error("Cannot add item to Preserves value"); + } + virtual bool set(Value const& key, Value const& value) { + throw std::runtime_error("Cannot set item by key in Preserves value"); + } + virtual bool set(size_t index, Value const& value) { + throw std::runtime_error("Cannot set item by index in Preserves value"); + } + virtual bool erase(Value const& key) { + throw std::runtime_error("Cannot erase item in Preserves value"); + } + }; + + template inline ValueKind Value::value_kind() const { return p->value_kind(); } + template inline bool Value::is_mutable() const { return p->is_mutable(); } + + template + class Atomic: ValueImpl { + protected: + Atom value; + public: + Atomic(Atom value) : value(value) {} + ValueKind value_kind() const { return kind; } + }; + +#define PRESERVES_ATOMIC_VALUE_CLASS(Name, a_t, r_t, VK, getter, extra) \ + template \ + class Name: Atomic { \ + boost::optional getter() const override { return this->value; } \ + extra \ + } + + PRESERVES_ATOMIC_VALUE_CLASS(Boolean, bool, bool, ValueKind::Boolean, as_bool,); + PRESERVES_ATOMIC_VALUE_CLASS(Float, float, float, ValueKind::Float, as_float,); + PRESERVES_ATOMIC_VALUE_CLASS(Double, double, double, ValueKind::Double, as_double,); + PRESERVES_ATOMIC_VALUE_CLASS(Uint64, uint64_t, uint64_t, ValueKind::SignedInteger, as_unsigned, + boost::optional as_signed() const override { + if (this->value <= uint64_t(std::numeric_limits::max())) { + return this->value; + } else { + return boost::none; + } + }); + PRESERVES_ATOMIC_VALUE_CLASS(Int64, int64_t, int64_t, ValueKind::SignedInteger, as_signed, + boost::optional as_unsigned() const override { + if (this->value >= 0) { + return this->value; + } else { + return boost::none; + } + }); + PRESERVES_ATOMIC_VALUE_CLASS(String, std::string, std::string const&, ValueKind::String, as_string,); + PRESERVES_ATOMIC_VALUE_CLASS(ByteString, std::vector, std::vector const&, ValueKind::ByteString, as_bytes,); + PRESERVES_ATOMIC_VALUE_CLASS(Symbol, std::string, std::string const&, ValueKind::Symbol, as_symbol,); + + template + class Record: ValueImpl { + public: + Value labelValue; + std::vector> fields; + + Record(Value const& label) : labelValue(label), fields() {} + Record(Value const& label, std::vector> const& fields) : labelValue(label), fields(fields) {} + ValueKind value_kind() const { return ValueKind::Record; } + boost::optional> label() const override { return labelValue; } + size_t size() const { return fields.size(); } + boost::optional> get(size_t index) const { + if (index < size()) { + return fields[index]; + } else { + return boost::none; + } + } + bool add(Value const& value) override { + fields.push_back(value); + return true; + } + bool set(size_t index, Value const& value) override { + if (index < size()) { + fields[index] = value; + } + return false; + } + }; + + template + class Sequence: ValueImpl { + public: + std::vector> values; + + Sequence() : values() {} + Sequence(std::vector> const& values) : values(values) {} + ValueKind value_kind() const { return ValueKind::Sequence; } + boost::optional> const&> as_sequence() const override { + return values; + } + size_t size() const override { return values.size(); } + boost::optional> get(size_t index) const override { + if (index < size()) { + return values[index]; + } else { + return boost::none; + } + } + bool add(Value const& value) override { + values.push_back(value); + return true; + } + bool set(size_t index, Value const& value) override { + if (index < size()) { + values[index] = value; + } + return false; + } + }; + + template + class Set: ValueImpl { + public: + std::set> values; + + Set() : values() {} + Set(std::set> const& values) : values(values) {} + ValueKind value_kind() const { return ValueKind::Set; } + boost::optional> const&> as_set() const override { + return values; + } + size_t size() const override { return values.size(); } + bool contains(Value const& key) const override { return values.count(key) > 0; } + bool add(Value const& value) override { + return values.insert(value)->second; + } + bool erase(Value const& value) override { + return values.erase(value) > 0; + } + }; + + template + class Dictionary: ValueImpl { + public: + std::map, Value> values; + + Dictionary() : values() {} + Dictionary(std::map, Value> const& values) : values(values) {} + ValueKind value_kind() const { return ValueKind::Dictionary; } + boost::optional, Value> const&> as_dictionary() const override { + return values; + } + size_t size() const override { return values.size(); } + bool contains(Value const& key) const override { return values.count(key) > 0; } + boost::optional> get(Value const& key) const override { + auto i = values.find(key); + if (i == values.end()) return boost::none; + return i->second; + } + bool set(Value const& key, Value const& value) override { + return values.emplace(key, value)->second; + } + bool erase(Value const& key) override { + return values.erase(key) > 0; + } + }; + + template + class Embedded: ValueImpl { + public: + std::shared_ptr value; + + Embedded(std::shared_ptr const& value) : value() {} + ValueKind value_kind() const { return ValueKind::Embedded; } + boost::optional> as_embedded() const override { + return value; + } + }; +} diff --git a/implementations/cpp/preserves_text.hpp b/implementations/cpp/preserves_text.hpp new file mode 100644 index 0000000..a277aaa --- /dev/null +++ b/implementations/cpp/preserves_text.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +#include "preserves.hpp" + +namespace Preserves { + template + class TextReader { + public: + TextReader(std::istream& i); + }; +} \ No newline at end of file