diff --git a/CMakeLists.txt b/CMakeLists.txt index ad901a9..4b56701 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,7 +55,7 @@ add_library(simfil ${LIBRARY_TYPE} src/exception-handler.cpp src/model/model.cpp src/model/nodes.cpp - src/model/fields.cpp) + src/model/string-pool.cpp) target_sources(simfil PUBLIC FILE_SET public_headers @@ -75,7 +75,7 @@ target_sources(simfil PUBLIC include/simfil/exception-handler.h include/simfil/model/arena.h - include/simfil/model/fields.h + include/simfil/model/string-pool.h include/simfil/model/model.h include/simfil/model/nodes.h include/simfil/model/bitsery-traits.h) @@ -122,7 +122,7 @@ if (SIMFIL_WITH_MODEL_JSON) include/simfil/model/json.h) target_link_libraries(simfil - PRIVATE + PUBLIC nlohmann_json::nlohmann_json) endif() diff --git a/README.md b/README.md index cdb6c80..c76ffba 100644 --- a/README.md +++ b/README.md @@ -165,10 +165,10 @@ target_link_libraries( PUBLIC simfil) ```c++ #include "simfil/simfil.h" #include "simfil/model/model.h" -#include "simfil/model/fields.h" +#include "simfil/model/string-pool.h" // Shared string pool used for string interning -auto strings = std::make_shared(); +auto strings = std::make_shared(); // Declare a model with one object auto model = std::make_shared(strings); diff --git a/examples/minimal/main.cpp b/examples/minimal/main.cpp index 6eb3e37..9d0c5b7 100644 --- a/examples/minimal/main.cpp +++ b/examples/minimal/main.cpp @@ -3,12 +3,12 @@ #include "simfil/simfil.h" #include "simfil/model/model.h" -#include "simfil/model/fields.h" +#include "simfil/model/string-pool.h" int main() { // Shared string pool. - auto strings = std::make_shared(); + auto strings = std::make_shared(); // Data model pool auto model = std::make_shared(strings); diff --git a/include/simfil/environment.h b/include/simfil/environment.h index e183fe9..23a9566 100644 --- a/include/simfil/environment.h +++ b/include/simfil/environment.h @@ -34,11 +34,11 @@ struct Environment /** * Construct a SIMFIL execution environment with a string cache, * which is used to map field names to short integer IDs. - * @param fieldNames The string cache used by this environment. + * @param strings The string cache used by this environment. * Must be the same cache that is also used by ModelPools which * are queried using this environment. */ - explicit Environment(std::shared_ptr fieldNames); + explicit Environment(std::shared_ptr strings); /** * Constructor for instantiating an environment explicitly with @@ -78,7 +78,8 @@ struct Environment * Obtain a strong reference to this environment's string cache. * Guaranteed not-null. */ - auto fieldNames() const -> std::shared_ptr; + [[nodiscard]] + auto strings() const -> std::shared_ptr; public: std::unique_ptr warnMtx; @@ -91,7 +92,7 @@ struct Environment std::map functions; Debug* debug = nullptr; - std::shared_ptr fieldNames_; + std::shared_ptr stringPool; }; /** diff --git a/include/simfil/model/model.h b/include/simfil/model/model.h index 4390cfa..abb798b 100644 --- a/include/simfil/model/model.h +++ b/include/simfil/model/model.h @@ -1,10 +1,13 @@ // Copyright (c) Navigation Data Standard e.V. - See "LICENSE" file. - #pragma once -#include "simfil/value.h" +#include "simfil/model/string-pool.h" +#if defined(SIMFIL_WITH_MODEL_JSON) +# include "nlohmann/json.hpp" +#endif #include +#include #include #include #include @@ -44,17 +47,29 @@ class Model : public std::enable_shared_from_this T fn_; }; - /// Virtual destructor to allow polymorphism + /** Virtual destructor to allow polymorphism */ virtual ~Model() = default; - /// Get a callback with the actual class of the given node. - /// This facilitates the Virtual Function Table role of the Model. + /** + * Get a callback with the actual class of the given node. + * This facilitates the Virtual Function Table role of the Model. + */ virtual void resolve(ModelNode const& n, ResolveFn const& cb) const; - /// Add a small scalar value and get its model node view + /** Add a small scalar value and get its model node view */ ModelNode::Ptr newSmallValue(bool value); ModelNode::Ptr newSmallValue(int16_t value); ModelNode::Ptr newSmallValue(uint16_t value); + + /** + * Lookup a field name for a field-id. + * + * This is in the base Model class to allow JSON + * serialization, without having to downcast to ModelPool; + * which contains the `strings()` function. The base + * implementation returns an unset optional. + */ + virtual std::optional lookupStringId(StringId id) const; }; /** @@ -69,8 +84,8 @@ class ModelPool : public Model public: /** * The pool consists of multiple ModelNode columns, - * each for a different data type. Each column - * is identified by a static column ID. + * each for a different data type. Each column + * is identified by a static column ID. */ enum ColumnId : uint8_t { Objects = FirstNontrivialColumnId, @@ -82,71 +97,93 @@ class ModelPool : public Model FirstCustomColumnId = 128, }; - /// Default ctor with own string storage + /** Default ctor with own string storage */ ModelPool(); ~ModelPool(); - /// Ctor with shared string storage - explicit ModelPool(std::shared_ptr stringStore); + /** Ctor with shared string storage */ + explicit ModelPool(std::shared_ptr stringStore); - /// Validate that all internal string/node references are valid - /// Returns a list of found errors. + /** + * Validate that all internal string/node references are valid + * Returns a list of found errors. + */ virtual std::vector checkForErrors() const; - /// Get a callback with the actual class of the given node. - /// This facilitates the Virtual Function Table role of the ModelPool. + /** + * Get a callback with the actual class of the given node. + * This facilitates the Virtual Function Table role of the ModelPool. + */ void resolve(ModelNode const& n, ResolveFn const& cb) const override; - /// Clear all columns and roots + /** Clear all columns and roots */ virtual void clear(); - /// Check for errors, throw if there are any + /** Check for errors, throw if there are any */ void validate() const; - /// Get number of root nodes + /** Get number of root nodes */ [[nodiscard]] size_t numRoots() const; - /// Get specific root node + /** Get specific root node */ [[nodiscard]] ModelNode::Ptr root(size_t const& i) const; - /// Designate a model node index as a root + /** Designate a model node index as a root */ void addRoot(ModelNode::Ptr const& rootNode); - /// Adopt members from the given vector and obtain a new object - /// model index which has these members. + /** + * Adopt members from the given vector and obtain a new object + * model index which has these members. + */ shared_model_ptr newObject(size_t initialFieldCapacity = 2); - /// Adopt members from the given vector and obtain a new array - /// model index which has these members. + /** + * Adopt members from the given vector and obtain a new array + * model index which has these members. + */ shared_model_ptr newArray(size_t initialFieldCapacity = 2); - /// Add a scalar value and get its new model node index. + /** Add a scalar value and get its new model node index. */ ModelNode::Ptr newValue(int64_t const& value); ModelNode::Ptr newValue(double const& value); ModelNode::Ptr newValue(std::string_view const& value); - /// Node-type-specific resolve-functions + /** Node-type-specific resolve-functions */ + [[nodiscard]] shared_model_ptr resolveObject(ModelNode::Ptr const& n) const; + [[nodiscard]] shared_model_ptr resolveArray(ModelNode::Ptr const& n) const; - /// Access the field name storage - std::shared_ptr fieldNames() const; + /** Access the field name storage */ + [[nodiscard]] + std::shared_ptr strings() const; + + /** + * Change the string pool of this model to a different one. + * Note: This will potentially create new field entries in the newDict, + * for field names which were not there before. + */ + virtual void setStrings(std::shared_ptr const& strings); - /// Change the fields dict of this model to a different one. - /// Note: This will potentially create new field entries in the newDict, - /// for field names which were not there before. - virtual void setFieldNames(std::shared_ptr const& newDict); + std::optional lookupStringId(StringId id) const override; - /// Serialization + /** Serialization */ virtual void write(std::ostream& outputStream); virtual void read(std::istream& inputStream); +#if defined(SIMFIL_WITH_MODEL_JSON) + /** JSON Serialization */ + virtual nlohmann::json toJson() const; +#endif + protected: struct Impl; std::unique_ptr impl_; - /// Protected object/array member storage access, - /// so derived ModelPools can create Object/Array-derived nodes. + /** + * Protected object/array member storage access, + * so derived ModelPools can create Object/Array-derived nodes. + */ Object::Storage& objectMemberStorage(); Array::Storage& arrayMemberStorage(); }; diff --git a/include/simfil/model/nodes.h b/include/simfil/model/nodes.h index 080353a..29f13b8 100644 --- a/include/simfil/model/nodes.h +++ b/include/simfil/model/nodes.h @@ -5,10 +5,14 @@ #include #include "arena.h" -#include "fields.h" +#include "string-pool.h" #include +#if defined(SIMFIL_WITH_MODEL_JSON) +# include "nlohmann/json.hpp" +#endif + namespace bitsery { // Pre-declare bitsery protected member accessor. class Access; @@ -179,13 +183,13 @@ struct ModelNode [[nodiscard]] virtual ValueType type() const; /// Get a child by name - [[nodiscard]] virtual Ptr get(FieldId const& f) const; + [[nodiscard]] virtual Ptr get(StringId const& f) const; /// Get a child by index [[nodiscard]] virtual Ptr at(int64_t i) const; /// Get an Object model's field names - [[nodiscard]] virtual FieldId keyAt(int64_t i) const; + [[nodiscard]] virtual StringId keyAt(int64_t i) const; /// Get the number of children [[nodiscard]] virtual uint32_t size() const; @@ -212,38 +216,38 @@ struct ModelNode virtual bool iterate(IterCallback const& cb) const; // NOLINT (allow discard) /// Iterator Support - /// * `fieldNames()`: Returns range object which supports `for(FieldId const& f: node.fieldNames())` + /// * `fieldNames()`: Returns range object which supports `for(StringId const& f: node.fieldNames())` /// * `fields()`: Returns range object which supports `for(auto const& [fieldId, value] : node.fields())` /// * Normal `begin()`/`end()`: Supports `for(ModelNode::Ptr child : node)` - // Implement fieldNames() by creating an iterator for FieldId - class FieldIdIterator + // Implement fieldNames() by creating an iterator for StringId + class StringIdIterator { int64_t index; ModelNode const* parent; public: using iterator_category = std::input_iterator_tag; - using value_type = FieldId; + using value_type = StringId; using difference_type = std::ptrdiff_t; - using pointer = FieldId*; - using reference = FieldId&; - FieldIdIterator(int64_t idx, ModelNode const* p) : index(idx), parent(p) {} - FieldIdIterator& operator++() { ++index; return *this; } - bool operator==(const FieldIdIterator& other) const { return index == other.index; } - bool operator!=(const FieldIdIterator& other) const { return index != other.index; } - FieldId operator*() const { return parent->keyAt(index); } + using pointer = StringId*; + using reference = StringId&; + StringIdIterator(int64_t idx, ModelNode const* p) : index(idx), parent(p) {} + StringIdIterator& operator++() { ++index; return *this; } + bool operator==(const StringIdIterator& other) const { return index == other.index; } + bool operator!=(const StringIdIterator& other) const { return index != other.index; } + StringId operator*() const { return parent->keyAt(index); } }; - class FieldIdRange + class StringIdRange { ModelNode const* node; public: - explicit FieldIdRange(ModelNode const* n) : node(n) {} - [[nodiscard]] FieldIdIterator begin() const { return node->fieldNamesBegin(); } - [[nodiscard]] FieldIdIterator end() const { return node->fieldNamesEnd(); } + explicit StringIdRange(ModelNode const* const n) : node(n) {} + [[nodiscard]] StringIdIterator begin() const { return node->fieldNamesBegin(); } + [[nodiscard]] StringIdIterator end() const { return node->fieldNamesEnd(); } }; - [[nodiscard]] FieldIdIterator fieldNamesBegin() const { return {0, this}; } - [[nodiscard]] FieldIdIterator fieldNamesEnd() const { return {size(), this}; } - [[nodiscard]] auto fieldNames() const { return FieldIdRange{this}; } + [[nodiscard]] StringIdIterator fieldNamesBegin() const { return {0, this}; } + [[nodiscard]] StringIdIterator fieldNamesEnd() const { return {size(), this}; } + [[nodiscard]] auto fieldNames() const { return StringIdRange{this}; } // Implement fields() by creating an iterator for field-value pairs class FieldIterator @@ -252,21 +256,21 @@ struct ModelNode ModelNode const* parent; public: using iterator_category = std::input_iterator_tag; - using value_type = std::pair; + using value_type = std::pair; using difference_type = std::ptrdiff_t; using pointer = value_type*; using reference = value_type&; - FieldIterator(int64_t idx, ModelNode const* p) : index(idx), parent(p) {} + FieldIterator(int64_t const idx, ModelNode const* const p) : index(idx), parent(p) {} FieldIterator& operator++() { ++index; return *this; } bool operator==(const FieldIterator& other) const { return index == other.index; } bool operator!=(const FieldIterator& other) const { return index != other.index; } - std::pair operator*() const { return std::make_pair(parent->keyAt(index), parent->at(index)); } + std::pair operator*() const { return std::make_pair(parent->keyAt(index), parent->at(index)); } }; class FieldRange { ModelNode const* node; public: - explicit FieldRange(ModelNode const* n) : node(n) {} + explicit FieldRange(ModelNode const* const n) : node(n) {} [[nodiscard]] FieldIterator begin() const { return node->fieldsBegin(); } [[nodiscard]] FieldIterator end() const { return node->fieldsEnd(); } }; @@ -285,7 +289,7 @@ struct ModelNode using difference_type = std::ptrdiff_t; using pointer = value_type*; using reference = value_type&; - ChildIterator(int64_t idx, ModelNode const* p) : index(idx), parent(p) {} + ChildIterator(int64_t const idx, ModelNode const* const p) : index(idx), parent(p) {} ChildIterator& operator++() { ++index; return *this; } bool operator==(const ChildIterator& other) const { return index == other.index; } bool operator!=(const ChildIterator& other) const { return index != other.index; } @@ -294,6 +298,10 @@ struct ModelNode [[nodiscard]] ChildIterator begin() const { return {0, this}; } [[nodiscard]] ChildIterator end() const { return {size(), this}; } +#if defined(SIMFIL_WITH_MODEL_JSON) + [[nodiscard]] virtual nlohmann::json toJson() const; +#endif + protected: ModelNode() = default; ModelNode(ModelNode const&) = default; @@ -325,9 +333,9 @@ struct ModelNodeBase : public ModelNode [[nodiscard]] ScalarValueType value() const override; [[nodiscard]] ValueType type() const override; - [[nodiscard]] ModelNode::Ptr get(const FieldId&) const override; + [[nodiscard]] ModelNode::Ptr get(const StringId&) const override; [[nodiscard]] ModelNode::Ptr at(int64_t) const override; - [[nodiscard]] FieldId keyAt(int64_t) const override; + [[nodiscard]] StringId keyAt(int64_t) const override; [[nodiscard]] uint32_t size() const override; bool iterate(IterCallback const&) const override {return true;} // NOLINT (allow discard) @@ -361,9 +369,8 @@ struct MandatoryDerivedModelNodeBase : public ModelNodeBase protected: template inline ModelType_* modelPtr() const { - static_assert(std::is_base_of::value); - return reinterpret_cast(const_cast(model_.get())); - } // NOLINT + return static_cast(const_cast(model_.get())); + } MandatoryDerivedModelNodeBase() = default; MandatoryDerivedModelNodeBase(ModelConstPtr p, ModelNodeAddress a={}, ScalarValueType data={}) // NOLINT @@ -463,8 +470,8 @@ struct Object : public MandatoryModelPoolNodeBase [[nodiscard]] ValueType type() const override; [[nodiscard]] ModelNode::Ptr at(int64_t) const override; [[nodiscard]] uint32_t size() const override; - [[nodiscard]] ModelNode::Ptr get(const FieldId &) const override; - [[nodiscard]] FieldId keyAt(int64_t) const override; + [[nodiscard]] ModelNode::Ptr get(const StringId &) const override; + [[nodiscard]] StringId keyAt(int64_t) const override; bool iterate(IterCallback const& cb) const override; // NOLINT (allow discard) template @@ -495,8 +502,8 @@ struct Object : public MandatoryModelPoolNodeBase struct Field { Field() = default; - Field(FieldId name, ModelNodeAddress a) : name_(name), node_(a) {} - FieldId name_ = Fields::Empty; + Field(StringId name, ModelNodeAddress a) : name_(name), node_(a) {} + StringId name_ = StringPool::Empty; ModelNodeAddress node_; template @@ -524,7 +531,7 @@ class ProceduralObject : public Object public: [[nodiscard]] ModelNode::Ptr at(int64_t i) const override { if (i < fields_.size()) - return fields_[i].second(reinterpret_cast(*this)); + return fields_[i].second(static_cast(*this)); return Object::at(i - fields_.size()); } @@ -532,14 +539,14 @@ class ProceduralObject : public Object return fields_.size() + Object::size(); } - [[nodiscard]] ModelNode::Ptr get(const FieldId & field) const override { + [[nodiscard]] ModelNode::Ptr get(const StringId & field) const override { for (auto const& [k, v] : fields_) if (k == field) - return v(reinterpret_cast(*this)); + return v(static_cast(*this)); return Object::get(field); } - [[nodiscard]] FieldId keyAt(int64_t i) const override { + [[nodiscard]] StringId keyAt(int64_t i) const override { if (i < fields_.size()) return fields_[i].first; return Object::keyAt(i - fields_.size()); @@ -547,7 +554,7 @@ class ProceduralObject : public Object bool iterate(IterCallback const& cb) const override { // NOLINT (allow discard) for (auto const& [k, v] : fields_) { - auto vv = v(reinterpret_cast(*this)); + auto vv = v(static_cast(*this)); if (!cb(*vv)) return false; } @@ -560,7 +567,7 @@ class ProceduralObject : public Object : Object(i, pool, a) {} sfl::small_vector< - std::pair>, + std::pair>, MaxProceduralFields> fields_; }; diff --git a/include/simfil/model/fields.h b/include/simfil/model/string-pool.h similarity index 61% rename from include/simfil/model/fields.h rename to include/simfil/model/string-pool.h index 4d1ef0b..d650d3d 100644 --- a/include/simfil/model/fields.h +++ b/include/simfil/model/string-pool.h @@ -1,8 +1,8 @@ #pragma once +#include +#include #include -#include -#include #include #include #include @@ -14,15 +14,16 @@ namespace simfil { -using FieldId = uint16_t; +using StringId = uint16_t; +static_assert(std::is_unsigned_v, "StringId must be unsigned!"); /** - * Fast and efficient case-insensitive field name storage - - * used to store object keys. + * Fast and efficient case-insensitive string interner, + * used to store object keys. */ -struct Fields +struct StringPool { - enum StaticFieldIds: FieldId { + enum StaticStringIds : StringId { Empty = 0, OverlaySum, OverlayValue, @@ -33,30 +34,30 @@ struct Fields }; /// Default constructor initializes strings for static Ids - Fields(); + StringPool(); /// Copy constructor - Fields(Fields const&); + StringPool(StringPool const& other); /// Virtual destructor to allow polymorphism - virtual ~Fields() = default; + virtual ~StringPool() = default; /// Use this function to lookup a stored string, or insert it /// if it doesn't exist yet. - FieldId emplace(std::string_view const& str); + StringId emplace(std::string_view const& str); /// Returns the ID of the given string, or `Empty` if /// no such string was ever inserted. - FieldId get(std::string_view const& str); + StringId get(std::string_view const& str); /// Get the actual string for the given ID, or /// nullopt if the given ID is invalid. /// Virtual to allow defining an inherited StringPool which understands - /// additional StaticFieldIds. - virtual std::optional resolve(FieldId const& id); + /// additional StaticStringIds. + virtual std::optional resolve(StringId const& id) const; /// Get highest stored field id - FieldId highest() const; + StringId highest() const; /// Get stats size_t size() const; @@ -65,18 +66,18 @@ struct Fields size_t misses() const; /// Add a static key-string mapping - Warning: Not thread-safe. - void addStaticKey(FieldId k, std::string const& v); + void addStaticKey(StringId k, std::string const& v); /// Serialization - write to stream, starting from a specific /// id offset if necessary (for partial serialisation). - virtual void write(std::ostream& outputStream, FieldId offset=0) const; // NOLINT + virtual void write(std::ostream& outputStream, StringId offset = {}) const; // NOLINT virtual void read(std::istream& inputStream); private: mutable std::shared_mutex stringStoreMutex_; - std::unordered_map idForString_; - std::unordered_map stringForId_; - FieldId nextId_ = FirstDynamicId; + std::unordered_map idForString_; + std::unordered_map stringForId_; + StringId nextId_ = FirstDynamicId; std::atomic_int64_t byteSize_{0}; std::atomic_int64_t cacheHits_{0}; std::atomic_int64_t cacheMisses_{0}; diff --git a/include/simfil/overlay.h b/include/simfil/overlay.h index 25ad8b8..2539550 100644 --- a/include/simfil/overlay.h +++ b/include/simfil/overlay.h @@ -10,7 +10,7 @@ namespace simfil struct OverlayNodeStorage final : public Model { Value value_; - std::map overlayChildren_; + std::map overlayChildren_; explicit OverlayNodeStorage(Value const& val) : value_(val) {} // NOLINT @@ -24,12 +24,12 @@ class OverlayNode final : public MandatoryDerivedModelNodeBase void; + auto set(StringId const& key, Value const& child) -> void; [[nodiscard]] ScalarValueType value() const override; [[nodiscard]] ValueType type() const override; - [[nodiscard]] ModelNode::Ptr get(const FieldId& key) const override; + [[nodiscard]] ModelNode::Ptr get(const StringId& key) const override; [[nodiscard]] ModelNode::Ptr at(int64_t i) const override; - [[nodiscard]] FieldId keyAt(int64_t i) const override; + [[nodiscard]] StringId keyAt(int64_t i) const override; [[nodiscard]] uint32_t size() const override; [[nodiscard]] bool iterate(IterCallback const& cb) const override; }; diff --git a/include/simfil/value.h b/include/simfil/value.h index d2d92ec..ff6c266 100644 --- a/include/simfil/value.h +++ b/include/simfil/value.h @@ -4,10 +4,6 @@ #include #include #include -#include -#include -#include -#include #include "model/nodes.h" #include "transient.h" @@ -66,9 +62,13 @@ struct ValueToString return ""s; } - auto operator()(const ModelNode&) const + auto operator()([[maybe_unused]] const ModelNode& node) const { - return "model"s; +#if defined(SIMFIL_WITH_MODEL_JSON) + return nlohmann::to_string(node.toJson()); +#else + return ""s; +#endif } }; } diff --git a/repl/repl.cpp b/repl/repl.cpp index 279c977..c5dd821 100644 --- a/repl/repl.cpp +++ b/repl/repl.cpp @@ -168,7 +168,7 @@ int main(int argc, char *argv[]) continue; } - simfil::Environment env(model->fieldNames()); + simfil::Environment env(model->strings()); simfil::ExprPtr expr; try { expr = simfil::compile(env, cmd, options.auto_any); diff --git a/src/environment.cpp b/src/environment.cpp index 957b400..9588270 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -4,13 +4,12 @@ namespace simfil { -Environment::Environment(std::shared_ptr fieldNames) +Environment::Environment(std::shared_ptr strings) : warnMtx(std::make_unique()) , traceMtx(std::make_unique()) - , - fieldNames_(std::move(fieldNames)) + , stringPool(std::move(strings)) { - if (!fieldNames_) + if (!stringPool) raise("The string cache must not be null."); functions["any"] = &AnyFn::Fn; @@ -26,8 +25,8 @@ Environment::Environment(std::shared_ptr fieldNames) functions["trace"] = &TraceFn::Fn; } -Environment::Environment(NewStringCache_) : - Environment(std::make_shared()) {} +Environment::Environment(NewStringCache_) + : Environment(std::make_shared()) {} Environment::~Environment() {} @@ -51,8 +50,8 @@ auto Environment::findFunction(std::string name) const -> const Function* return nullptr; } -auto Environment::fieldNames() const -> std::shared_ptr { - return fieldNames_; +auto Environment::strings() const -> std::shared_ptr { + return stringPool; } Context::Context(Environment* env, Context::Phase phase) diff --git a/src/function.cpp b/src/function.cpp index 6f0dfc1..702d5ce 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -563,12 +563,12 @@ auto SumFn::eval(Context ctx, Value val, const std::vector& args, const (void)args[0]->eval(ctx, val, LambdaResultFn([&, n = 0](Context ctx, Value vv) mutable { if (subexpr) { auto ov = shared_model_ptr::make(vv); - ov->set(Fields::OverlaySum, sum); - ov->set(Fields::OverlayValue, vv); - ov->set(Fields::OverlayIndex, Value::make((int64_t)n++)); + ov->set(StringPool::OverlaySum, sum); + ov->set(StringPool::OverlayValue, vv); + ov->set(StringPool::OverlayIndex, Value::make((int64_t)n++)); subexpr->eval(ctx, Value::field(ov), LambdaResultFn([&ov, &sum](auto ctx, auto vv) { - ov->set(Fields::OverlaySum, vv); + ov->set(StringPool::OverlaySum, vv); sum = vv; return Result::Continue; })); @@ -612,7 +612,7 @@ auto KeysFn::eval(Context ctx, Value val, const std::vector& args, cons if (vv.node) for (auto&& fieldName : vv.node->fieldNames()) { - if (auto key = ctx.env->fieldNames_->resolve(fieldName)) { + if (auto key = ctx.env->stringPool->resolve(fieldName)) { if (res(ctx, Value::strref(*key)) == Result::Stop) return Result::Stop; } diff --git a/src/model/model.cpp b/src/model/model.cpp index 2f14633..367ab62 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -1,8 +1,11 @@ #include "simfil/model/model.h" #include "simfil/model/arena.h" #include "simfil/model/bitsery-traits.h" +#include "simfil/model/nodes.h" #include +#include +#include #include #include @@ -40,8 +43,8 @@ void Model::resolve(const ModelNode& n, const ResolveFn& cb) const struct ModelPool::Impl { - explicit Impl(std::shared_ptr fieldNames) : - fieldNames_(std::move(fieldNames)) + explicit Impl(std::shared_ptr strings) : + strings_(std::move(strings)) { columns_.stringData_.reserve(detail::ColumnPageSize*4); } @@ -58,7 +61,7 @@ struct ModelPool::Impl }; /// This model pool's field name store - std::shared_ptr fieldNames_; + std::shared_ptr strings_; struct { sfl::segmented_vector roots_; @@ -89,10 +92,10 @@ struct ModelPool::Impl }; ModelPool::ModelPool() - : impl_(std::make_unique(std::make_shared())) + : impl_(std::make_unique(std::make_shared())) {} -ModelPool::ModelPool(std::shared_ptr stringStore) +ModelPool::ModelPool(std::shared_ptr stringStore) : impl_(std::make_unique(std::move(stringStore))) {} @@ -111,11 +114,11 @@ std::vector ModelPool::checkForErrors() const return true; }; - auto validateFieldName = [&, this](FieldId const& str) + auto validateFieldName = [&, this](StringId const& str) { - if (!impl_->fieldNames_) + if (!impl_->strings_) return; - if (!impl_->fieldNames_->resolve(str)) + if (!impl_->strings_->resolve(str)) errors.push_back(fmt::format("Bad string ID: {}", str)); }; @@ -275,6 +278,11 @@ ModelNode::Ptr Model::newSmallValue(uint16_t value) return ModelNode(shared_from_this(), {UInt16, (uint32_t)value}); } +std::optional Model::lookupStringId(const StringId) const +{ + return {}; +} + ModelNode::Ptr ModelPool::newValue(int64_t const& value) { if (value < 0 && value >= std::numeric_limits::min()) @@ -314,21 +322,26 @@ shared_model_ptr ModelPool::resolveArray(ModelNode::Ptr const& n) const return Array(shared_from_this(), n->addr_); } -std::shared_ptr ModelPool::fieldNames() const +std::shared_ptr ModelPool::strings() const { - return impl_->fieldNames_; + return impl_->strings_; } -void ModelPool::setFieldNames(std::shared_ptr const& newDict) +void ModelPool::setStrings(std::shared_ptr const& strings) { // Translate object field IDs to the new dictionary. for (auto memberArray : impl_->columns_.objectMemberArrays_) { for (auto& member : memberArray) { - if (auto resolvedName = impl_->fieldNames_->resolve(member.name_)) - member.name_ = newDict->emplace(*resolvedName); + if (auto resolvedName = impl_->strings_->resolve(member.name_)) + member.name_ = strings->emplace(*resolvedName); } } - impl_->fieldNames_ = newDict; + impl_->strings_ = strings; +} + +std::optional ModelPool::lookupStringId(const StringId id) const +{ + return impl_->strings_->resolve(id); } Object::Storage& ModelPool::objectMemberStorage() { @@ -354,4 +367,17 @@ void ModelPool::read(std::istream& inputStream) { } } +#if defined(SIMFIL_WITH_MODEL_JSON) +nlohmann::json ModelPool::toJson() const +{ + auto roots = nlohmann::json::array(); + const auto n = numRoots(); + for (auto i = 0u; i < n; ++i) { + roots.push_back(root(i)->toJson()); + } + + return roots; +} +#endif + } // namespace simfil diff --git a/src/model/nodes.cpp b/src/model/nodes.cpp index 5300aaa..b385138 100644 --- a/src/model/nodes.cpp +++ b/src/model/nodes.cpp @@ -28,7 +28,7 @@ ValueType ModelNode::type() const { } /// Get a child by name -ModelNode::Ptr ModelNode::get(const FieldId& field) const { +ModelNode::Ptr ModelNode::get(const StringId& field) const { ModelNode::Ptr result; if (model_) model_ @@ -46,8 +46,8 @@ ModelNode::Ptr ModelNode::at(int64_t index) const { } /// Get an Object model's field names -FieldId ModelNode::keyAt(int64_t i) const { - FieldId result = 0; +StringId ModelNode::keyAt(int64_t i) const { + StringId result = 0; if (model_) model_->resolve(*this, Model::Lambda([&](auto&& resolved) { result = resolved.keyAt(i); })); return result; @@ -69,6 +69,42 @@ bool ModelNode::iterate(const IterCallback& cb) const { return result; } +#if defined(SIMFIL_WITH_MODEL_JSON) +nlohmann::json ModelNode::toJson() const +{ + if (type() == ValueType::Object) { + auto j = nlohmann::json::object(); + for (const auto& [fieldId, childNode] : fields()) { + if (auto resolvedField = model_->lookupStringId(fieldId)) { + j[*resolvedField] = childNode->toJson(); + } + } + return j; + } + else if (type() == ValueType::Array) { + auto j = nlohmann::json::array(); + for (const auto& i : *this) { + j.push_back(i->toJson()); + } + return j; + } + else { + auto j = nlohmann::json{}; + std::visit( + [&j](auto&& v) + { + using T = decltype(v); + if constexpr (!std::is_same_v, std::monostate>) { + j = std::forward(v); + } else { + j = nullptr; + } + }, value()); + return j; + } +} +#endif + /** Model Node Base Impl. */ ModelNodeBase::ModelNodeBase(ModelConstPtr pool, ModelNodeAddress addr, ScalarValueType data) @@ -91,7 +127,7 @@ ValueType ModelNodeBase::type() const return ValueType::Null; } -ModelNode::Ptr ModelNodeBase::get(const FieldId&) const +ModelNode::Ptr ModelNodeBase::get(const StringId&) const { return nullptr; } @@ -101,7 +137,7 @@ ModelNode::Ptr ModelNodeBase::at(int64_t) const return nullptr; } -FieldId ModelNodeBase::keyAt(int64_t) const +StringId ModelNodeBase::keyAt(int64_t) const { return 0; } @@ -134,7 +170,7 @@ ValueType ValueNode::type() const { /** Model Node impls. for SmallValueNode */ template<> ScalarValueType SmallValueNode::value() const { - return (int64_t)addr_.int16(); + return static_cast(addr_.int16()); } template<> ValueType SmallValueNode::type() const { @@ -147,7 +183,7 @@ SmallValueNode::SmallValueNode(ModelConstPtr p, ModelNodeAddress a) {} template<> ScalarValueType SmallValueNode::value() const { - return (int64_t)addr_.uint16(); + return static_cast(addr_.uint16()); } template<> ValueType SmallValueNode::type() const { @@ -160,7 +196,7 @@ SmallValueNode::SmallValueNode(ModelConstPtr p, ModelNodeAddress a) {} template<> ScalarValueType SmallValueNode::value() const { - return (bool)addr_.uint16(); + return addr_.uint16() != 0; } template<> ValueType SmallValueNode::type() const { @@ -252,7 +288,7 @@ ModelNode::Ptr Object::at(int64_t i) const return ModelNode::Ptr::make(model_, storage_->at(members_, i).node_); } -FieldId Object::keyAt(int64_t i) const { +StringId Object::keyAt(int64_t i) const { if (i < 0 || i >= (int64_t)storage_->size(members_)) return {}; return storage_->at(members_, i).name_; @@ -263,7 +299,7 @@ uint32_t Object::size() const return (uint32_t)storage_->size(members_); } -ModelNode::Ptr Object::get(const FieldId & field) const +ModelNode::Ptr Object::get(const StringId & field) const { ModelNode::Ptr result; storage_->iterate(members_, [&field, &result, this](auto&& member){ @@ -277,48 +313,48 @@ ModelNode::Ptr Object::get(const FieldId & field) const } ModelNode::Ptr Object::get(std::string_view const& fieldName) const { - auto fieldId = model().fieldNames()->emplace(fieldName); + auto fieldId = model().strings()->emplace(fieldName); return get(fieldId); } Object& Object::addBool(std::string_view const& name, bool value) { - auto fieldId = model().fieldNames()->emplace(name); + auto fieldId = model().strings()->emplace(name); storage_->emplace_back(members_, fieldId, model().newSmallValue(value)->addr()); return *this; } Object& Object::addField(std::string_view const& name, uint16_t value) { - auto fieldId = model().fieldNames()->emplace(name); + auto fieldId = model().strings()->emplace(name); storage_->emplace_back(members_, fieldId, model().newSmallValue(value)->addr()); return *this; } Object& Object::addField(std::string_view const& name, int16_t value) { - auto fieldId = model().fieldNames()->emplace(name); + auto fieldId = model().strings()->emplace(name); storage_->emplace_back(members_, fieldId, model().newSmallValue(value)->addr()); return *this; } Object& Object::addField(std::string_view const& name, int64_t const& value) { - auto fieldId = model().fieldNames()->emplace(name); + auto fieldId = model().strings()->emplace(name); storage_->emplace_back(members_, fieldId, model().newValue(value)->addr()); return *this; } Object& Object::addField(std::string_view const& name, double const& value) { - auto fieldId = model().fieldNames()->emplace(name); + auto fieldId = model().strings()->emplace(name); storage_->emplace_back(members_, fieldId, model().newValue(value)->addr()); return *this; } Object& Object::addField(std::string_view const& name, std::string_view const& value) { - auto fieldId = model().fieldNames()->emplace(name); + auto fieldId = model().strings()->emplace(name); storage_->emplace_back(members_, fieldId, model().newValue(value)->addr()); return *this; } Object& Object::addField(std::string_view const& name, ModelNode::Ptr const& value) { - auto fieldId = model().fieldNames()->emplace(name); + auto fieldId = model().strings()->emplace(name); storage_->emplace_back(members_, fieldId, value->addr()); return *this; } diff --git a/src/model/fields.cpp b/src/model/string-pool.cpp similarity index 51% rename from src/model/fields.cpp rename to src/model/string-pool.cpp index ac6d197..8ec1932 100644 --- a/src/model/fields.cpp +++ b/src/model/string-pool.cpp @@ -1,4 +1,4 @@ -#include "simfil/model/fields.h" +#include "simfil/model/string-pool.h" #include "simfil/exception-handler.h" #include @@ -7,11 +7,14 @@ #include #include #include +#include +#include +#include namespace simfil { -Fields::Fields() +StringPool::StringPool() { addStaticKey(Empty, ""); addStaticKey(OverlaySum, "$sum"); @@ -19,7 +22,7 @@ Fields::Fields() addStaticKey(OverlayIndex, "$idx"); } -Fields::Fields(const Fields& other) : +StringPool::StringPool(const StringPool& other) : idForString_(other.idForString_), stringForId_(other.stringForId_), nextId_(other.nextId_), @@ -29,7 +32,7 @@ Fields::Fields(const Fields& other) : { } -FieldId Fields::emplace(std::string_view const& str) +StringId StringPool::emplace(std::string_view const& str) { /// Unfortunately, we have to create a copy of the string here /// on the heap for lower-casing. @@ -40,7 +43,7 @@ FieldId Fields::emplace(std::string_view const& str) lowerCaseStr.begin(), lowerCaseStr.end(), lowerCaseStr.begin(), - [](auto ch) { return std::tolower(ch); }); + [](auto ch) { return std::tolower(ch, std::locale{}); }); { std::shared_lock stringStoreReadAccess_(stringStoreMutex_); @@ -52,144 +55,148 @@ FieldId Fields::emplace(std::string_view const& str) } { std::unique_lock stringStoreWriteAccess_(stringStoreMutex_); - auto [it, insertionTookPlace] = idForString_.emplace(lowerCaseStr, nextId_); + auto [it, insertionTookPlace] = idForString_.try_emplace(lowerCaseStr, nextId_); if (insertionTookPlace) { - stringForId_.emplace(nextId_, str); - byteSize_ += (int64_t)str.size(); + (void)stringForId_.try_emplace(nextId_, str); + byteSize_ += static_cast(str.size()); ++cacheMisses_; ++nextId_; + if (nextId_ < it->second) { + raise("StringPool id overflow!"); + } } return it->second; } } -FieldId Fields::get(std::string_view const& str) +StringId StringPool::get(std::string_view const& str) { auto lowerCaseStr = std::string(str); std::transform( lowerCaseStr.begin(), lowerCaseStr.end(), lowerCaseStr.begin(), - [](auto ch) { return std::tolower(ch); }); + [](auto ch) { return std::tolower(ch, std::locale{}); }); std::shared_lock stringStoreReadAccess_(stringStoreMutex_); auto it = idForString_.find(lowerCaseStr); if (it != idForString_.end()) return it->second; - return Fields::Empty; + return StringPool::Empty; } -std::optional Fields::resolve(FieldId const& id) +std::optional StringPool::resolve(const StringId& id) const { std::shared_lock stringStoreReadAccess_(stringStoreMutex_); - auto it = stringForId_.find(id); + const auto it = stringForId_.find(id); if (it != stringForId_.end()) return it->second; return std::nullopt; } -FieldId Fields::highest() const { +StringId StringPool::highest() const { return nextId_ - 1; } -size_t Fields::size() const +size_t StringPool::size() const { std::shared_lock stringStoreReadAccess_(stringStoreMutex_); return idForString_.size(); } -size_t Fields::bytes() const +size_t StringPool::bytes() const { return byteSize_; } -size_t Fields::hits() const +size_t StringPool::hits() const { return cacheHits_; } -size_t Fields::misses() const +size_t StringPool::misses() const { return cacheMisses_; } -void Fields::addStaticKey(FieldId k, std::string const& v) { +void StringPool::addStaticKey(StringId k, std::string const& v) { auto lowerCaseStr = v; std::transform( lowerCaseStr.begin(), lowerCaseStr.end(), lowerCaseStr.begin(), - [](auto ch) { return std::tolower(ch); }); + [](auto ch) { return std::tolower(ch, std::locale{}); }); idForString_[lowerCaseStr] = k; stringForId_[k] = v; } -void Fields::write(std::ostream& outputStream, FieldId offset) const // NOLINT +void StringPool::write(std::ostream& outputStream, const StringId offset) const // NOLINT { std::shared_lock stringStoreReadAccess_(stringStoreMutex_); bitsery::Serializer s(outputStream); - // Calculate how many fields will be sent - FieldId sendFieldCount = 0; - for (FieldId fieldId = offset; fieldId <= highest(); ++fieldId) { - auto it = stringForId_.find(fieldId); + // Calculate how many strings will be sent + StringId sendStrCount = 0; + const auto high = highest(); + for (auto strId = offset; strId <= high; ++strId) { + auto it = stringForId_.find(strId); if (it != stringForId_.end()) - ++sendFieldCount; + ++sendStrCount; } - s.value2b(sendFieldCount); + s.value2b(sendStrCount); - // Send the field key-name pairs - for (FieldId fieldId = offset; fieldId <= highest(); ++fieldId) { - auto it = stringForId_.find(fieldId); + // Send the pool's key-string pairs + for (auto strId = offset; strId <= high; ++strId) { + auto it = stringForId_.find(strId); if (it != stringForId_.end()) { - s.value2b(fieldId); - // Don't support field names longer than 64kB. + s.value2b(strId); + // Don't support strings longer than 64kB. s.text1b(it->second, std::numeric_limits::max()); } } } -void Fields::read(std::istream& inputStream) +void StringPool::read(std::istream& inputStream) { std::unique_lock stringStoreWriteAccess_(stringStoreMutex_); bitsery::Deserializer s(inputStream); - // Determine how many fields are to be received - FieldId rcvFieldCount{}; - s.value2b(rcvFieldCount); + // Determine how many strings are to be received + StringId rcvStringCount{}; + s.value2b(rcvStringCount); - // Read fields - for (auto i = 0; i < rcvFieldCount; ++i) + // Read strings + for (auto i = 0; i < rcvStringCount; ++i) { - // Read field key - FieldId fieldId{}; - s.value2b(fieldId); + // Read string key + StringId stringId{}; + s.value2b(stringId); - // Don't support field names longer than 64kB. - std::string fieldName; - s.text1b(fieldName, std::numeric_limits::max()); - auto lowerCaseFieldName = std::string(fieldName); + // Don't support strings longer than 64kB. + std::string stringValue; + s.text1b(stringValue, std::numeric_limits::max()); + auto lowerCaseStringValue = std::string(stringValue); - // Insert field name into pool + // Insert string into the pool std::transform( - lowerCaseFieldName.begin(), - lowerCaseFieldName.end(), - lowerCaseFieldName.begin(), - [](auto ch) { return std::tolower(ch); }); - auto [it, insertionTookPlace] = idForString_.emplace(lowerCaseFieldName, fieldId); + lowerCaseStringValue.begin(), + lowerCaseStringValue.end(), + lowerCaseStringValue.begin(), + [](auto ch) { return std::tolower(ch, std::locale{}); }); + auto [it, insertionTookPlace] = idForString_.try_emplace(lowerCaseStringValue, stringId); if (insertionTookPlace) { - stringForId_.emplace(fieldId, fieldName); - byteSize_ += (int64_t)fieldName.size(); - nextId_ = std::max((FieldId)nextId_, (FieldId)(fieldId + 1)); + stringForId_.try_emplace(stringId, stringValue); + byteSize_ += static_cast(stringValue.size()); + nextId_ = std::max(nextId_, stringId + 1); } } if (s.adapter().error() != bitsery::ReaderError::NoError) { raise(fmt::format( - "Failed to read Fields: Error {}", + "Failed to read StringPool: Error {}", static_cast>(s.adapter().error()))); } } diff --git a/src/overlay.cpp b/src/overlay.cpp index c7bdc5d..612ea8e 100644 --- a/src/overlay.cpp +++ b/src/overlay.cpp @@ -18,7 +18,7 @@ OverlayNode::OverlayNode(ModelNode const& n) : MandatoryDerivedModelNodeBase(n) {} -auto OverlayNode::set(FieldId const& key, Value const& child) -> void +auto OverlayNode::set(StringId const& key, Value const& child) -> void { model().overlayChildren_.insert({key, child}); } @@ -33,7 +33,7 @@ auto OverlayNode::set(FieldId const& key, Value const& child) -> void return ValueType::Object; } -[[nodiscard]] ModelNode::Ptr OverlayNode::get(const FieldId& key) const +[[nodiscard]] ModelNode::Ptr OverlayNode::get(const StringId& key) const { auto iter = model().overlayChildren_.find(key); if (iter != model().overlayChildren_.end()) { @@ -49,7 +49,7 @@ auto OverlayNode::set(FieldId const& key, Value const& child) -> void return model().value_.node->at(i); } -[[nodiscard]] FieldId OverlayNode::keyAt(int64_t i) const +[[nodiscard]] StringId OverlayNode::keyAt(int64_t i) const { return model().value_.node->keyAt(i); } diff --git a/src/simfil.cpp b/src/simfil.cpp index 57ef824..8ce0b6f 100644 --- a/src/simfil.cpp +++ b/src/simfil.cpp @@ -258,7 +258,7 @@ class FieldExpr : public Expr return res(ctx, Value::null()); if (!nameId_) - nameId_ = ctx.env->fieldNames()->get(name_); + nameId_ = ctx.env->strings()->get(name_); if (!nameId_) /* If the field name is not in the string cache, then there @@ -281,7 +281,7 @@ class FieldExpr : public Expr } std::string name_; - mutable FieldId nameId_ = {}; + mutable StringId nameId_ = {}; }; class MultiConstExpr : public Expr @@ -388,7 +388,7 @@ class SubscriptExpr : public Expr /* String subscript */ else if (ival.isa(ValueType::String)) { auto key = ival.as(); - if (auto keyStrId = ctx.env->fieldNames()->get(key)) + if (auto keyStrId = ctx.env->strings()->get(key)) node = lval.node->get(keyStrId); } @@ -1318,7 +1318,7 @@ auto compile(Environment& env, std::string_view sv, bool any) -> ExprPtr auto eval(Environment& env, const Expr& ast, ModelPool const& model, size_t rootIndex) -> std::vector { - if (env.fieldNames() != model.fieldNames()) + if (env.strings() != model.strings()) raise("Environment must use same field name resource as model."); return eval(env, ast, *model.root(rootIndex)); diff --git a/src/types.cpp b/src/types.cpp index eb3c460..6b36c34 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -33,7 +33,7 @@ auto IRangeType::unaryOp(std::string_view op, const IRange& self) const -> Value return Value::make(fmt::format("{}..{}", self.begin, self.end)); if (op == OperatorLen::name()) - return Value::make((int64_t)self.high() - self.low()); + return Value::make(static_cast(self.high() - self.low())); raise(op); } diff --git a/test/complex.cpp b/test/complex.cpp index 464333a..98050a3 100644 --- a/test/complex.cpp +++ b/test/complex.cpp @@ -1,8 +1,8 @@ #include #include #include -#include +#include "simfil/model/string-pool.h" #include "simfil/simfil.h" #include "simfil/model/json.h" @@ -55,7 +55,7 @@ static const auto invoice = R"json( static auto joined_result(std::string_view query) { auto model = json::parse(invoice); - Environment env(model->fieldNames()); + Environment env(model->strings()); auto ast = compile(env, query, false); INFO("AST: " << ast->toString()); @@ -126,15 +126,15 @@ TEST_CASE("Serialization", "[yaml.complex.serialization]") { SECTION("Test Fields write/read") { std::stringstream stream; - model->fieldNames()->write(stream); + model->strings()->write(stream); - auto recoveredFields = std::make_shared(); + const auto recoveredFields = std::make_shared(); recoveredFields->read(stream); - REQUIRE(model->fieldNames()->size() == recoveredFields->size()); - REQUIRE(model->fieldNames()->highest() == recoveredFields->highest()); - REQUIRE(model->fieldNames()->bytes() == recoveredFields->bytes()); - for (FieldId fieldId = 0; fieldId <= recoveredFields->highest(); ++fieldId) - REQUIRE(model->fieldNames()->resolve(fieldId) == recoveredFields->resolve(fieldId)); + REQUIRE(model->strings()->size() == recoveredFields->size()); + REQUIRE(model->strings()->highest() == recoveredFields->highest()); + REQUIRE(model->strings()->bytes() == recoveredFields->bytes()); + for (StringId sId = 0; sId <= recoveredFields->highest(); ++sId) + REQUIRE(model->strings()->resolve(sId) == recoveredFields->resolve(StringId(sId))); } } diff --git a/test/performance.cpp b/test/performance.cpp index d1a5f47..a5226d1 100644 --- a/test/performance.cpp +++ b/test/performance.cpp @@ -112,7 +112,7 @@ static auto generate_model(std::size_t n) static auto result(const ModelPoolPtr& model, std::string_view query) { - Environment env(model->fieldNames()); + Environment env(model->strings()); auto ast = compile(env, query, false); INFO("AST: " << ast->toString()); diff --git a/test/simfil.cpp b/test/simfil.cpp index c320f45..641526c 100644 --- a/test/simfil.cpp +++ b/test/simfil.cpp @@ -6,7 +6,7 @@ using namespace simfil; -static const auto StaticTestKey = Fields::NextStaticId; +static const auto StaticTestKey = StringPool::NextStaticId; static auto getASTString(std::string_view input) @@ -218,7 +218,7 @@ static const char* doc = R"json( static auto joined_result(std::string_view query) { auto model = simfil::json::parse(doc); - Environment env(model->fieldNames()); + Environment env(model->strings()); auto ast = compile(env, query, false); INFO("AST: " << ast->toString()); @@ -238,11 +238,9 @@ static auto joined_result(std::string_view query) REQUIRE(joined_result(query) == result) TEST_CASE("Path Wildcard", "[yaml.path-wildcard]") { - REQUIRE_RESULT("sub.*", "sub a|sub b|model"); - /* ^- sub.sub */ - REQUIRE_RESULT("sub.**", "model|sub a|sub b|model|sub sub a|sub sub b"); - /* ^- sub ^- sub.sub */ - + REQUIRE_RESULT("sub.*", R"(sub a|sub b|{"a":"sub sub a","b":"sub sub b"})"); + REQUIRE_RESULT("sub.**", R"({"a":"sub a","b":"sub b","sub":{"a":"sub sub a","b":"sub sub b"}}|sub a|sub b|)" + R"({"a":"sub sub a","b":"sub sub b"}|sub sub a|sub sub b)"); REQUIRE_RESULT("(sub.*.{typeof _ != 'model'} + sub.*.{typeof _ != 'model'})._", "sub asub a|sub asub b|sub bsub a|sub bsub b"); /* . filters null */ REQUIRE_RESULT("sub.*.{typeof _ != 'model'} + sub.*.{typeof _ != 'model'}", "sub asub a|sub asub b|sub bsub a|sub bsub b"); /* {_} filters null */ REQUIRE_RESULT("count(*)", "8"); @@ -261,10 +259,10 @@ TEST_CASE("Array Access", "[yaml.array-access]") { REQUIRE_RESULT("c[-1]","null"); /* Out of bounds */ REQUIRE_RESULT("c[4]", "null"); /* Out of bounds */ - REQUIRE_RESULT("c", "model"); - REQUIRE_RESULT("c._", "model"); /* No implicit child traversal! */ + REQUIRE_RESULT("c", R"(["a","b","c"])"); + REQUIRE_RESULT("c._", R"(["a","b","c"])"); /* No implicit child traversal! */ REQUIRE_RESULT("c.*", "a|b|c"); - REQUIRE_RESULT("c.**", "model|a|b|c"); + REQUIRE_RESULT("c.**", R"(["a","b","c"]|a|b|c)"); REQUIRE_RESULT("c[arr(0,2)]", "a|c"); REQUIRE_RESULT("c[range(0,2)...]", "a|b|c"); @@ -275,14 +273,19 @@ TEST_CASE("Array Access", "[yaml.array-access]") { } TEST_CASE("Single Values", "[yaml.single-values]") { - REQUIRE_RESULT("_", "model"); - REQUIRE_RESULT("_._", "model"); + + auto json = R"({"a":1,"b":2,"c":["a","b","c"],"d":[0,1,2],"geoLineString":{"geometry":{"coordinates":[[1,2],[3,4]],"type":"LineString"}},"geoPoint":{"geometry":{"coordinates":[1,2],"type":"Point"}},"geoPolygon":{"geometry":{"coordinates":[[[1,2],[3,4],[5,6]]],"type":"Polygon"}},"sub":{"a":"sub a","b":"sub b","sub":{"a":"sub sub a","b":"sub sub b"}}})"; + auto sub_json = R"({"a":"sub a","b":"sub b","sub":{"a":"sub sub a","b":"sub sub b"}})"; + auto sub_sub_json = R"({"a":"sub sub a","b":"sub sub b"})"; + + REQUIRE_RESULT("_", json); + REQUIRE_RESULT("_._", json); REQUIRE_RESULT("a", "1"); REQUIRE_RESULT("['a']", "1"); REQUIRE_RESULT("b", "2"); - REQUIRE_RESULT("sub", "model"); + REQUIRE_RESULT("sub", sub_json); REQUIRE_RESULT("sub.a", "sub a"); - REQUIRE_RESULT("sub.sub", "model"); + REQUIRE_RESULT("sub.sub", sub_sub_json); REQUIRE_RESULT("sub.sub.a", "sub sub a"); } @@ -385,7 +388,7 @@ TEST_CASE("Model Pool Validation", "[model.validation]") { TEST_CASE("Procedural Object Node", "[model.procedural]") { auto pool = std::make_shared(); - pool->fieldNames()->addStaticKey(StaticTestKey, "test"); + pool->strings()->addStaticKey(StaticTestKey, "test"); struct DerivedProceduralObject : public ProceduralObject<2, DerivedProceduralObject> { DerivedProceduralObject(ModelConstPtr pool, ModelNodeAddress a) @@ -401,7 +404,7 @@ TEST_CASE("Procedural Object Node", "[model.procedural]") { baseObj->addField("mood", "blue"); auto proceduralObj = shared_model_ptr::make(pool, baseObj->addr()); - REQUIRE(proceduralObj->get(pool->fieldNames()->get("mood"))->value() == ScalarValueType(std::string_view("blue"))); + REQUIRE(proceduralObj->get(pool->strings()->get("mood"))->value() == ScalarValueType(std::string_view("blue"))); REQUIRE(proceduralObj->get(StaticTestKey)->value() == ScalarValueType(std::string_view("static"))); } @@ -462,21 +465,21 @@ TEST_CASE("Object/Array Extend", "[model.extend]") { } } -TEST_CASE("Switch Model Fields Dict", "[model.setfieldnames]") +TEST_CASE("Switch Model String Pool", "[model.setStrings]") { auto pool = std::make_shared(); - auto oldFieldDict = pool->fieldNames(); - auto newFieldDict = std::make_shared(*oldFieldDict); - pool->setFieldNames(newFieldDict); - REQUIRE(pool->fieldNames() == newFieldDict); + auto oldFieldDict = pool->strings(); + auto newFieldDict = std::make_shared(*oldFieldDict); + pool->setStrings(newFieldDict); + REQUIRE(pool->strings() == newFieldDict); auto obj = pool->newObject(); obj->addField("hello", "world"); oldFieldDict->emplace("gobbledigook"); - pool->setFieldNames(oldFieldDict); + pool->setStrings(oldFieldDict); - REQUIRE(pool->fieldNames() == oldFieldDict); + REQUIRE(pool->strings() == oldFieldDict); REQUIRE_NOTHROW(pool->validate()); REQUIRE(oldFieldDict->size() != newFieldDict->size()); }