From 38c17f093615a16a4b4ec6dc2b5d3edb5c1d3895 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Thu, 27 Sep 2018 21:40:52 -0400 Subject: More hkutil refactoring All database access goes through hatkirby::database now. verbly::token, verbly::statement::condition, and verbly::part have been converted to use mpark::variant now. verbly::binding has been deleted, and replaced with a mpark::variant typedef in statement.h. This means that the only remaining tagged union class is verbly::generator::part. refs #5 --- CMakeLists.txt | 17 +- lib/binding.cpp | 249 -------------------- lib/binding.h | 82 ------- lib/database.cpp | 123 ++-------- lib/database.h | 61 +++-- lib/form.cpp | 52 ++--- lib/form.h | 26 ++- lib/frame.cpp | 9 +- lib/frame.h | 10 +- lib/notion.cpp | 16 +- lib/notion.h | 118 +++++----- lib/part.cpp | 350 ++++++---------------------- lib/part.h | 97 ++++---- lib/pronunciation.cpp | 24 +- lib/pronunciation.h | 22 +- lib/query.h | 107 +++------ lib/statement.cpp | 621 ++++++++++++++++++++++---------------------------- lib/statement.h | 96 ++++---- lib/token.cpp | 495 +++++++++++----------------------------- lib/token.h | 76 +++--- lib/word.cpp | 85 +------ lib/word.h | 50 ++-- vendor/hkutil | 2 +- 23 files changed, 867 insertions(+), 1921 deletions(-) delete mode 100644 lib/binding.cpp delete mode 100644 lib/binding.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8dd2792..2adad19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,11 +6,24 @@ pkg_check_modules(sqlite3 sqlite3>=3.8.3 REQUIRED) set(CMAKE_BUILD_TYPE Debug) -include_directories( +add_library(verbly + lib/filter.cpp + lib/field.cpp + lib/notion.cpp + lib/word.cpp + lib/frame.cpp + lib/part.cpp + lib/form.cpp + lib/pronunciation.cpp + lib/statement.cpp + lib/database.cpp + lib/token.cpp) + +target_include_directories(verbly PUBLIC + lib vendor/hkutil vendor/hkutil/vendor) -add_library(verbly lib/filter.cpp lib/field.cpp lib/notion.cpp lib/word.cpp lib/frame.cpp lib/part.cpp lib/form.cpp lib/pronunciation.cpp lib/statement.cpp lib/binding.cpp lib/database.cpp lib/token.cpp lib/part.cpp) set_property(TARGET verbly PROPERTY CXX_STANDARD 11) set_property(TARGET verbly PROPERTY CXX_STANDARD_REQUIRED ON) target_link_libraries(verbly ${sqlite3_LIBRARIES}) diff --git a/lib/binding.cpp b/lib/binding.cpp deleted file mode 100644 index 0b58785..0000000 --- a/lib/binding.cpp +++ /dev/null @@ -1,249 +0,0 @@ -#include "binding.h" -#include -#include - -namespace verbly { - - binding::binding(const binding& other) - { - type_ = other.type_; - - switch (type_) - { - case type::integer: - { - integer_ = other.integer_; - - break; - } - - case type::string: - { - new(&string_) std::string(other.string_); - - break; - } - - case type::field: - { - new(&field_.table_) std::string(other.field_.table_); - new(&field_.column_) std::string(other.field_.column_); - - break; - } - - case type::invalid: - { - break; - } - } - } - - binding::binding(binding&& other) : binding() - { - swap(*this, other); - } - - binding& binding::operator=(binding other) - { - swap(*this, other); - - return *this; - } - - void swap(binding& first, binding& second) - { - using type = binding::type; - - type tempType = first.type_; - int tempInteger; - std::string tempString; - std::string tempTable; - std::string tempColumn; - - switch (first.type_) - { - case type::integer: - { - tempInteger = first.integer_; - - break; - } - - case type::string: - { - tempString = std::move(tempString); - - break; - } - - case type::field: - { - tempTable = std::move(first.field_.table_); - tempColumn = std::move(first.field_.column_); - - break; - } - - case type::invalid: - { - break; - } - } - - first.~binding(); - - first.type_ = second.type_; - - switch (second.type_) - { - case type::integer: - { - first.integer_ = second.integer_; - - break; - } - - case type::string: - { - new(&first.string_) std::string(std::move(second.string_)); - - break; - } - - case type::field: - { - new(&first.field_.table_) std::string(std::move(second.field_.table_)); - new(&first.field_.column_) std::string(std::move(second.field_.column_)); - - break; - } - - case type::invalid: - { - break; - } - } - - second.~binding(); - - second.type_ = tempType; - - switch (tempType) - { - case type::integer: - { - second.integer_ = tempInteger; - - break; - } - - case type::string: - { - new(&second.string_) std::string(std::move(tempString)); - - break; - } - - case type::field: - { - new(&first.field_.table_) std::string(std::move(tempTable)); - new(&first.field_.column_) std::string(std::move(tempColumn)); - - break; - } - - case type::invalid: - { - break; - } - } - } - - binding::~binding() - { - switch (type_) - { - case type::string: - { - using string_type = std::string; - string_.~string_type(); - - break; - } - - case type::field: - { - using string_type = std::string; - field_.table_.~string_type(); - field_.column_.~string_type(); - - break; - } - - case type::integer: - case type::invalid: - { - break; - } - } - } - - binding::binding(int arg) : - type_(type::integer), - integer_(arg) - { - } - - int binding::getInteger() const - { - if (type_ != type::integer) - { - throw std::domain_error("binding::getInteger called on non-integer binding"); - } - - return integer_; - } - - binding::binding(std::string arg) : type_(type::string) - { - new(&string_) std::string(std::move(arg)); - } - - std::string binding::getString() const - { - if (type_ != type::string) - { - throw std::domain_error("binding::getString called on non-string binding"); - } - - return string_; - } - - binding::binding(std::string table, std::string column) : type_(type::field) - { - new(&field_.table_) std::string(std::move(table)); - new(&field_.column_) std::string(std::move(column)); - } - - std::string binding::getTable() const - { - if (type_ != type::field) - { - throw std::domain_error("binding::getTable called on non-field binding"); - } - - return field_.table_; - } - - std::string binding::getColumn() const - { - if (type_ != type::field) - { - throw std::domain_error("binding::getColumn called on non-field binding"); - } - - return field_.column_; - } - -}; diff --git a/lib/binding.h b/lib/binding.h deleted file mode 100644 index 5da1e71..0000000 --- a/lib/binding.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef BINDING_H_CAE0B18E -#define BINDING_H_CAE0B18E - -#include - -namespace verbly { - - class binding { - public: - enum class type { - invalid, - integer, - string, - field - }; - - // Default constructor - - binding() - { - } - - // Copy and move constructors - - binding(const binding& other); - binding(binding&& other); - - // Assignment - - binding& operator=(binding other); - - // Swap - - friend void swap(binding& first, binding& second); - - // Destructor - - ~binding(); - - // Generic accessors - - type getType() const - { - return type_; - } - - // Integer - - binding(int arg); - - int getInteger() const; - - // String - - binding(std::string arg); - - std::string getString() const; - - // Field - - binding(std::string table, std::string column); - - std::string getTable() const; - std::string getColumn() const; - - private: - - union { - int integer_; - std::string string_; - struct { - std::string table_; - std::string column_; - } field_; - }; - - type type_ = type::invalid; - }; - -}; - -#endif /* end of include guard: BINDING_H_CAE0B18E */ diff --git a/lib/database.cpp b/lib/database.cpp index fe64763..96eed45 100644 --- a/lib/database.cpp +++ b/lib/database.cpp @@ -1,48 +1,19 @@ #include "database.h" -#include -#include #include #include "query.h" #include "version.h" namespace verbly { - database::database(std::string path) + database::database( + std::string path) : + ppdb_(std::move(path), hatkirby::dbmode::read) { - if (sqlite3_open_v2(path.c_str(), &ppdb_, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) - { - // We still have to free the resources allocated. In the event that - // allocation failed, ppdb will be null and sqlite3_close_v2 will just - // ignore it. - std::string errmsg(sqlite3_errmsg(ppdb_)); - sqlite3_close_v2(ppdb_); - - throw database_error("Could not open verbly datafile", errmsg); - } - - std::string queryString = "SELECT major, minor FROM version"; - - sqlite3_stmt* ppstmt; - if (sqlite3_prepare_v2(ppdb_, queryString.c_str(), queryString.length(), &ppstmt, NULL) != SQLITE_OK) - { - std::string errorMsg = sqlite3_errmsg(ppdb_); - sqlite3_finalize(ppstmt); - - throw database_error("Error reading database version", errorMsg); - } - - if (sqlite3_step(ppstmt) != SQLITE_ROW) - { - std::string errorMsg = sqlite3_errmsg(ppdb_); - sqlite3_finalize(ppstmt); - - throw database_error("Error reading database version", errorMsg); - } + hatkirby::row version = + ppdb_.queryFirst("SELECT major, minor FROM version"); - major_ = sqlite3_column_int(ppstmt, 0); - minor_ = sqlite3_column_int(ppstmt, 1); - - sqlite3_finalize(ppstmt); + major_ = mpark::get(version[0]); + minor_ = mpark::get(version[1]); if (major_ != DATABASE_MAJOR_VERSION) { @@ -50,28 +21,6 @@ namespace verbly { } } - database::database(database&& other) : database() - { - swap(*this, other); - } - - database& database::operator=(database&& other) - { - swap(*this, other); - - return *this; - } - - void swap(database& first, database& second) - { - std::swap(first.ppdb_, second.ppdb_); - } - - database::~database() - { - sqlite3_close_v2(ppdb_); - } - query database::notions(filter where, order sortOrder, int limit) const { return query(*this, ppdb_, std::move(where), std::move(sortOrder), limit); @@ -104,65 +53,35 @@ namespace verbly { std::set database::selrestrs(int partId) const { - std::string queryString = "SELECT selrestr FROM selrestrs WHERE part_id = ?"; - - sqlite3_stmt* ppstmt; - if (sqlite3_prepare_v2(ppdb_, queryString.c_str(), queryString.length(), &ppstmt, NULL) != SQLITE_OK) - { - std::string errorMsg = sqlite3_errmsg(ppdb_); - sqlite3_finalize(ppstmt); - - throw database_error("Error preparing query", errorMsg); - } - - if (sqlite3_bind_int(ppstmt, 1, partId) != SQLITE_OK) - { - std::string errorMsg = sqlite3_errmsg(ppdb_); - sqlite3_finalize(ppstmt); - - throw database_error("Error binding value to query", errorMsg); - } + std::vector rows = + ppdb_.queryAll( + "SELECT selrestr FROM selrestrs WHERE part_id = ?", + { partId }); std::set result; - while (sqlite3_step(ppstmt) == SQLITE_ROW) + + for (hatkirby::row& r : rows) { - result.insert(reinterpret_cast(sqlite3_column_blob(ppstmt, 0))); + result.emplace(std::move(mpark::get(r[0]))); } - sqlite3_finalize(ppstmt); - return result; } std::set database::synrestrs(int partId) const { - std::string queryString = "SELECT synrestr FROM synrestrs WHERE part_id = ?"; - - sqlite3_stmt* ppstmt; - if (sqlite3_prepare_v2(ppdb_, queryString.c_str(), queryString.length(), &ppstmt, NULL) != SQLITE_OK) - { - std::string errorMsg = sqlite3_errmsg(ppdb_); - sqlite3_finalize(ppstmt); - - throw database_error("Error preparing query", errorMsg); - } - - if (sqlite3_bind_int(ppstmt, 1, partId) != SQLITE_OK) - { - std::string errorMsg = sqlite3_errmsg(ppdb_); - sqlite3_finalize(ppstmt); - - throw database_error("Error binding value to query", errorMsg); - } + std::vector rows = + ppdb_.queryAll( + "SELECT synrestr FROM synrestrs WHERE part_id = ?", + { partId }); std::set result; - while (sqlite3_step(ppstmt) == SQLITE_ROW) + + for (hatkirby::row& r : rows) { - result.insert(reinterpret_cast(sqlite3_column_blob(ppstmt, 0))); + result.emplace(std::move(mpark::get(r[0]))); } - sqlite3_finalize(ppstmt); - return result; } diff --git a/lib/database.h b/lib/database.h index efb54e1..83c4c1c 100644 --- a/lib/database.h +++ b/lib/database.h @@ -1,11 +1,10 @@ -#ifndef DATABASE_H_0B0A47D2 -#define DATABASE_H_0B0A47D2 +#ifndef DATABASE_H_0B0A47D1 +#define DATABASE_H_0B0A47D1 #include -#include #include -#include #include +#include #include "notion.h" #include "word.h" #include "frame.h" @@ -14,8 +13,6 @@ #include "pronunciation.h" #include "order.h" -struct sqlite3; - namespace verbly { template @@ -28,24 +25,6 @@ namespace verbly { explicit database(std::string path); - // Disable copying - - database(const database& other) = delete; - database& operator=(const database& other) = delete; - - // Move constructor and move assignment - - database(database&& other); - database& operator=(database&& other); - - // Swap - - friend void swap(database& first, database& second); - - // Destructor - - ~database(); - // Information int getMajorVersion() const @@ -60,17 +39,35 @@ namespace verbly { // Queries - query notions(filter where, order sortOrder = {}, int limit = 1) const; + query notions( + filter where, + order sortOrder = {}, + int limit = 1) const; - query words(filter where, order sortOrder = {}, int limit = 1) const; + query words( + filter where, + order sortOrder = {}, + int limit = 1) const; - query frames(filter where, order sortOrder = {}, int limit = 1) const; + query frames( + filter where, + order sortOrder = {}, + int limit = 1) const; - query parts(filter where, order sortOrder = {}, int limit = 1) const; + query parts( + filter where, + order sortOrder = {}, + int limit = 1) const; - query
forms(filter where, order sortOrder = {}, int limit = 1) const; + query forms( + filter where, + order sortOrder = {}, + int limit = 1) const; - query pronunciations(filter where, order sortOrder = {}, int limit = 1) const; + query pronunciations( + filter where, + order sortOrder = {}, + int limit = 1) const; std::set selrestrs(int partId) const; @@ -78,9 +75,7 @@ namespace verbly { private: - database() = default; - - sqlite3* ppdb_ = nullptr; + mutable hatkirby::database ppdb_; int major_; int minor_; diff --git a/lib/form.cpp b/lib/form.cpp index b2c424d..4983274 100644 --- a/lib/form.cpp +++ b/lib/form.cpp @@ -1,5 +1,4 @@ #include "form.h" -#include #include #include "filter.h" #include "database.h" @@ -24,29 +23,15 @@ namespace verbly { return field::joinThroughWhere(object::form, "form_id", object::word, "lemmas_forms", "lemma_id", "category", static_cast(category)); } - form::form(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) + form::form(const database& db, hatkirby::row row) : valid_(true) { - id_ = sqlite3_column_int(row, 0); - text_ = std::string(reinterpret_cast(sqlite3_column_text(row, 1))); - complexity_ = sqlite3_column_int(row, 2); - proper_ = (sqlite3_column_int(row, 3) == 1); - length_ = sqlite3_column_int(row, 4); - } - - const std::vector& form::getPronunciations() const - { - if (!valid_) - { - throw std::domain_error("Bad access to uninitialized form"); - } - - if (!initializedPronunciations_) - { - pronunciations_ = db_->pronunciations(pronunciation::forms %= *this, pronunciation::id, -1).all(); - initializedPronunciations_ = true; - } + id_ = mpark::get(row[0]); + text_ = mpark::get(row[1]); + complexity_ = mpark::get(row[2]); + proper_ = (mpark::get(row[3]) == 1); + length_ = mpark::get(row[4]); - return pronunciations_; + pronunciations_ = db.pronunciations(*this, pronunciation::id, -1).all(); } bool form::startsWithVowelSound() const @@ -56,17 +41,24 @@ namespace verbly { throw std::domain_error("Bad access to uninitialized form"); } - const std::vector& pronunciations = getPronunciations(); - if (!pronunciations.empty()) + if (!pronunciations_.empty()) { - return std::any_of(std::begin(pronunciations), std::end(pronunciations), [] (const pronunciation& p) { - return p.getPhonemes().front().find_first_of("012") != std::string::npos; - }); + return std::any_of( + std::begin(pronunciations_), + std::end(pronunciations_), + [] (const pronunciation& p) { + return p.getPhonemes().front().find_first_of("012") != + std::string::npos; + }); } else { - // If the word is not in CMUDICT, fall back to checking whether the first letter is a vowel. - // Not perfect but will work in most cases. + // If the word is not in CMUDICT, fall back to checking whether the first + // letter is a vowel. Not perfect but will work in most cases. char ch = std::tolower(text_.front()); - return (ch == 'a') || (ch == 'e') || (ch == 'i') || (ch == 'o') || (ch == 'u'); + return (ch == 'a') || + (ch == 'e') || + (ch == 'i') || + (ch == 'o') || + (ch == 'u'); } } diff --git a/lib/form.h b/lib/form.h index 479672f..b365943 100644 --- a/lib/form.h +++ b/lib/form.h @@ -5,12 +5,11 @@ #include #include #include +#include #include "field.h" #include "pronunciation.h" #include "filter.h" -struct sqlite3_stmt; - namespace verbly { class database; @@ -24,7 +23,7 @@ namespace verbly { // Construct from database - form(const database& db, sqlite3_stmt* row); + form(const database& db, hatkirby::row row); // Accessors @@ -43,7 +42,7 @@ namespace verbly { return id_; } - std::string getText() const + const std::string& getText() const { if (!valid_) { @@ -83,7 +82,15 @@ namespace verbly { return length_; } - const std::vector& getPronunciations() const; + const std::vector& getPronunciations() const + { + if (!valid_) + { + throw std::domain_error("Bad access to uninitialized form"); + } + + return pronunciations_; + } // Convenience @@ -130,19 +137,14 @@ namespace verbly { static const field pronunciations; private: - bool valid_ = false; + bool valid_ = false; int id_; std::string text_; int complexity_; bool proper_; int length_; - - const database* db_; - - mutable bool initializedPronunciations_ = false; - mutable std::vector pronunciations_; - + std::vector pronunciations_; }; }; diff --git a/lib/frame.cpp b/lib/frame.cpp index 2351973..51d6936 100644 --- a/lib/frame.cpp +++ b/lib/frame.cpp @@ -1,5 +1,4 @@ #include "frame.h" -#include #include "database.h" #include "query.h" @@ -24,11 +23,11 @@ namespace verbly { return field::joinWhere(object::frame, "frame_id", object::part, "part_index", index); } - frame::frame(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) + frame::frame(const database& db, hatkirby::row row) : valid_(true) { - id_ = sqlite3_column_int(row, 0); - groupId_ = sqlite3_column_int(row, 1); - length_ = sqlite3_column_int(row, 2); + id_ = mpark::get(row[0]); + groupId_ = mpark::get(row[1]); + length_ = mpark::get(row[2]); parts_ = db.parts(*this, verbly::part::index, -1).all(); } diff --git a/lib/frame.h b/lib/frame.h index 5fa6c6b..3de1931 100644 --- a/lib/frame.h +++ b/lib/frame.h @@ -3,12 +3,11 @@ #include #include +#include #include "field.h" #include "filter.h" #include "part.h" -struct sqlite3_stmt; - namespace verbly { class database; @@ -22,7 +21,7 @@ namespace verbly { // Construct from database - frame(const database& db, sqlite3_stmt* row); + frame(const database& db, hatkirby::row row); // Accessors @@ -101,15 +100,12 @@ namespace verbly { static field parts(int index); private: - bool valid_ = false; + bool valid_ = false; int id_; int groupId_; int length_; std::vector parts_; - - const database* db_; - }; }; diff --git a/lib/notion.cpp b/lib/notion.cpp index c227b46..733c852 100644 --- a/lib/notion.cpp +++ b/lib/notion.cpp @@ -1,6 +1,6 @@ #include "notion.h" -#include #include +#include namespace verbly { @@ -58,21 +58,21 @@ namespace verbly { const field notion::preposition_group_field::isA = field::joinField(object::notion, "notion_id", "is_a"); const field notion::preposition_group_field::groupNameField = field::stringField("is_a", "groupname"); - notion::notion(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) + notion::notion(const database& db, hatkirby::row row) : valid_(true) { - id_ = sqlite3_column_int(row, 0); - partOfSpeech_ = static_cast(sqlite3_column_int(row, 1)); + id_ = mpark::get(row[0]); + partOfSpeech_ = static_cast(mpark::get(row[1])); - if (sqlite3_column_type(row, 2) != SQLITE_NULL) + if (!mpark::holds_alternative(row[2])) { hasWnid_ = true; - wnid_ = sqlite3_column_int(row, 2); + wnid_ = mpark::get(row[2]); } - if (sqlite3_column_type(row, 3) != SQLITE_NULL) + if (!mpark::holds_alternative(row[3])) { hasNumOfImages_ = true; - numOfImages_ = sqlite3_column_int(row, 3); + numOfImages_ = mpark::get(row[3]); } } diff --git a/lib/notion.h b/lib/notion.h index 5388e17..63afd2f 100644 --- a/lib/notion.h +++ b/lib/notion.h @@ -3,120 +3,119 @@ #include #include +#include #include "field.h" #include "filter.h" -struct sqlite3_stmt; - namespace verbly { - + class database; - + class notion { public: - + // Default constructor - + notion() = default; - + // Construct from database - - notion(const database& db, sqlite3_stmt* row); - + + notion(const database& db, hatkirby::row row); + // Accessors - + bool isValid() const { return valid_; } - + int getId() const { if (!valid_) { throw std::domain_error("Bad access to uninitialized notion"); } - + return id_; } - + part_of_speech getPartOfSpeech() const { if (!valid_) { throw std::domain_error("Bad access to uninitialized notion"); } - + return partOfSpeech_; } - + bool hasWnid() const { if (!valid_) { throw std::domain_error("Bad access to uninitialized notion"); } - + return hasWnid_; } - + int getWnid() const { if (!valid_) { throw std::domain_error("Bad access to uninitialized notion"); } - + if (!hasWnid_) { throw std::domain_error("Notion has no wnid"); } - + return wnid_; } - + bool hasNumOfImages() const { if (!valid_) { throw std::domain_error("Bad access to uninitialized notion"); } - + return hasNumOfImages_; } - + int getNumOfImages() const { if (!valid_) { throw std::domain_error("Bad access to uninitialized notion"); } - + if (!hasNumOfImages_) { throw std::domain_error("Notion does not have a number of images"); } - + return numOfImages_; } - + // Convenience - + std::string getImageNetUrl() const; - + // Type info - + static const object objectType; - + static const std::list select; - + // Query fields - + static const field id; static const field partOfSpeech; static const field wnid; static const field numOfImages; - + operator filter() const { if (!valid_) @@ -126,7 +125,7 @@ namespace verbly { return (id == id_); } - + filter operator!() const { if (!valid_) @@ -138,78 +137,75 @@ namespace verbly { } // Relationships with other objects - + static const field words; - + // Relationships with self - + static const field hypernyms; static const field hyponyms; - + static const field fullHypernyms; static const field fullHyponyms; - + static const field instances; static const field classes; - + static const field memberMeronyms; static const field memberHolonyms; - + static const field fullMemberMeronyms; static const field fullMemberHolonyms; - + static const field partMeronyms; static const field partHolonyms; - + static const field fullPartMeronyms; static const field fullPartHolonyms; - + static const field substanceMeronyms; static const field substanceHolonyms; - + static const field fullSubstanceMeronyms; static const field fullSubstanceHolonyms; - + static const field variants; static const field attributes; - + static const field similarAdjectives; - + static const field entails; static const field entailedBy; - + static const field causes; static const field effects; - + // Preposition group relationship - + class preposition_group_field { public: - + filter operator==(std::string groupName) const; - + private: - + static const field isA; static const field groupNameField; }; - + static const preposition_group_field prepositionGroups; - + private: + bool valid_ = false; - int id_; part_of_speech partOfSpeech_; bool hasWnid_ = false; int wnid_; bool hasNumOfImages_ = false; int numOfImages_; - - const database* db_; - }; - + }; #endif /* end of include guard: NOTION_H_FD1C7646 */ diff --git a/lib/part.cpp b/lib/part.cpp index e7e467b..bd8501a 100644 --- a/lib/part.cpp +++ b/lib/part.cpp @@ -1,6 +1,5 @@ #include "part.h" #include -#include #include #include "database.h" @@ -26,15 +25,19 @@ namespace verbly { const part::selrestr_field part::selrestrs = {}; const part::synrestr_field part::synrestrs = {}; - part part::createNounPhrase(std::string role, std::set selrestrs, std::set synrestrs) + part part::createNounPhrase( + std::string role, + std::set selrestrs, + std::set synrestrs) { - part p(part_type::noun_phrase); - - new(&p.noun_phrase_.role) std::string(std::move(role)); - new(&p.noun_phrase_.selrestrs) std::set(std::move(selrestrs)); - new(&p.noun_phrase_.synrestrs) std::set(std::move(synrestrs)); - - return p; + return part { + part_type::noun_phrase, + np_type { + std::move(role), + std::move(selrestrs), + std::move(synrestrs) + } + }; } part part::createVerb() @@ -44,12 +47,13 @@ namespace verbly { part part::createPreposition(std::vector choices, bool literal) { - part p(part_type::preposition); - - new(&p.preposition_.choices) std::vector(std::move(choices)); - p.preposition_.literal = literal; - - return p; + return part { + part_type::preposition, + prep_type { + std::move(choices), + literal + } + }; } part part::createAdjective() @@ -64,83 +68,53 @@ namespace verbly { part part::createLiteral(std::string value) { - part p(part_type::literal); - - new(&p.literal_) std::string(std::move(value)); - - return p; + return part { + part_type::literal, + std::move(value) + }; } - part::part(const database& db, sqlite3_stmt* row) + part::part(const database& db, hatkirby::row row) { - int id = sqlite3_column_int(row, 0); + int id = mpark::get(row[0]); - type_ = static_cast(sqlite3_column_int(row, 3)); + type_ = static_cast(mpark::get(row[3])); switch (type_) { case part_type::noun_phrase: { - new(&noun_phrase_.role) std::string(reinterpret_cast(sqlite3_column_blob(row, 4))); - new(&noun_phrase_.selrestrs) std::set(db.selrestrs(id)); - new(&noun_phrase_.synrestrs) std::set(db.synrestrs(id)); + variant_ = np_type { + mpark::get(row[4]), + db.selrestrs(id), + db.synrestrs(id) + }; break; } case part_type::preposition: { - std::string serializedChoices(reinterpret_cast(sqlite3_column_blob(row, 5))); - new(&preposition_.choices) std::vector(hatkirby::split>(serializedChoices, ",")); - - preposition_.literal = (sqlite3_column_int(row, 6) == 1); - - break; - } - - case part_type::literal: - { - new(&literal_) std::string(reinterpret_cast(sqlite3_column_blob(row, 7))); + hatkirby::blob_type raw = + mpark::get(row[5]); - break; - } + std::string serializedChoices( + std::begin(raw), + std::end(raw)); - case part_type::verb: - case part_type::adjective: - case part_type::adverb: - case part_type::invalid: - { - break; - } - } - } - - part::part(const part& other) - { - type_ = other.type_; - - switch (type_) - { - case part_type::noun_phrase: - { - new(&noun_phrase_.role) std::string(other.noun_phrase_.role); - new(&noun_phrase_.selrestrs) std::set(other.noun_phrase_.selrestrs); - new(&noun_phrase_.synrestrs) std::set(other.noun_phrase_.synrestrs); - - break; - } - - case part_type::preposition: - { - new(&preposition_.choices) std::vector(other.preposition_.choices); - preposition_.literal = other.preposition_.literal; + variant_ = prep_type { + hatkirby::split>( + std::move(serializedChoices), + ","), + (mpark::get(row[6]) == 1) + }; break; } case part_type::literal: { - new(&literal_) std::string(other.literal_); + variant_ = mpark::get(row[7]); break; } @@ -155,256 +129,74 @@ namespace verbly { } } - part::part(part&& other) : part() + const std::string& part::getNounRole() const { - swap(*this, other); - } - - part& part::operator=(part other) - { - swap(*this, other); - - return *this; - } - - void swap(part& first, part& second) - { - using type = part_type; - - type tempType = first.type_; - std::string tempRole; - std::set tempSelrestrs; - std::set tempSynrestrs; - std::vector tempChoices; - bool tempPrepLiteral; - std::string tempLiteralValue; - - switch (tempType) - { - case type::noun_phrase: - { - tempRole = std::move(first.noun_phrase_.role); - tempSelrestrs = std::move(first.noun_phrase_.selrestrs); - tempSynrestrs = std::move(first.noun_phrase_.synrestrs); - - break; - } - - case type::preposition: - { - tempChoices = std::move(first.preposition_.choices); - tempPrepLiteral = first.preposition_.literal; - - break; - } - - case type::literal: - { - tempLiteralValue = std::move(first.literal_); - - break; - } - - case type::verb: - case type::adjective: - case type::adverb: - case type::invalid: - { - break; - } - } - - first.~part(); - - first.type_ = second.type_; - - switch (first.type_) + if (type_ != part_type::noun_phrase) { - case type::noun_phrase: - { - new(&first.noun_phrase_.role) std::string(std::move(second.noun_phrase_.role)); - new(&first.noun_phrase_.selrestrs) std::set(std::move(second.noun_phrase_.selrestrs)); - new(&first.noun_phrase_.synrestrs) std::set(std::move(second.noun_phrase_.synrestrs)); - - break; - } - - case type::preposition: - { - new(&first.preposition_.choices) std::vector(std::move(second.preposition_.choices)); - first.preposition_.literal = second.preposition_.literal; - - break; - } - - case type::literal: - { - new(&first.literal_) std::string(std::move(second.literal_)); - - break; - } - - case type::verb: - case type::adjective: - case type::adverb: - case type::invalid: - { - break; - } + throw std::domain_error("part is not a noun phrase"); } - second.~part(); - - second.type_ = tempType; - - switch (second.type_) - { - case type::noun_phrase: - { - new(&second.noun_phrase_.role) std::string(std::move(tempRole)); - new(&second.noun_phrase_.selrestrs) std::set(std::move(tempSelrestrs)); - new(&second.noun_phrase_.synrestrs) std::set(std::move(tempSynrestrs)); - - break; - } - - case type::preposition: - { - new(&second.preposition_.choices) std::vector(std::move(tempChoices)); - second.preposition_.literal = tempPrepLiteral; - - break; - } - - case type::literal: - { - new(&second.literal_) std::string(std::move(tempLiteralValue)); - - break; - } - - case type::verb: - case type::adjective: - case type::adverb: - case type::invalid: - { - break; - } - } + return mpark::get(variant_).role; } - part::~part() + const std::set& part::getNounSelrestrs() const { - switch (type_) + if (type_ != part_type::noun_phrase) { - case part_type::noun_phrase: - { - using string_type = std::string; - using set_type = std::set; - - noun_phrase_.role.~string_type(); - noun_phrase_.selrestrs.~set_type(); - noun_phrase_.synrestrs.~set_type(); - - break; - } - - case part_type::preposition: - { - using vector_type = std::vector; - - preposition_.choices.~vector_type(); - - break; - } - - case part_type::literal: - { - using string_type = std::string; - - literal_.~string_type(); - - break; - } - - case part_type::verb: - case part_type::adjective: - case part_type::adverb: - case part_type::invalid: - { - break; - } + throw std::domain_error("part is not a noun phrase"); } - } - std::string part::getNounRole() const - { - if (type_ == part_type::noun_phrase) - { - return noun_phrase_.role; - } else { - throw std::domain_error("part::getNounRole is only valid for noun phrase parts"); - } + return mpark::get(variant_).selrestrs; } - std::set part::getNounSelrestrs() const + const std::set& part::getNounSynrestrs() const { - if (type_ == part_type::noun_phrase) + if (type_ != part_type::noun_phrase) { - return noun_phrase_.selrestrs; - } else { - throw std::domain_error("part::getNounSelrestrs is only valid for noun phrase parts"); + throw std::domain_error("part is not a noun phrase"); } - } - std::set part::getNounSynrestrs() const - { - if (type_ == part_type::noun_phrase) - { - return noun_phrase_.synrestrs; - } else { - throw std::domain_error("part::getNounSynrestrs is only valid for noun phrase parts"); - } + return mpark::get(variant_).synrestrs; } bool part::nounHasSynrestr(std::string synrestr) const { if (type_ != part_type::noun_phrase) { - throw std::domain_error("part::nounHasSynrestr is only valid for noun phrase parts"); + throw std::domain_error("part is not a noun phrase"); } - return (noun_phrase_.synrestrs.count(synrestr) == 1); + return mpark::get(variant_).synrestrs.count(synrestr); } - std::vector part::getPrepositionChoices() const + const std::vector& part::getPrepositionChoices() const { - if (type_ == part_type::preposition) + if (type_ != part_type::preposition) { - return preposition_.choices; - } else { - throw std::domain_error("part::getPrepositionChoices is only valid for preposition parts"); + throw std::domain_error("part is not a preposition"); } + + return mpark::get(variant_).choices; } bool part::isPrepositionLiteral() const { - if (type_ == part_type::preposition) + if (type_ != part_type::preposition) { - return preposition_.literal; - } else { - throw std::domain_error("part::isPrepositionLiteral is only valid for preposition parts"); + throw std::domain_error("part is not a preposition"); } + + return mpark::get(variant_).literal; } - std::string part::getLiteralValue() const + const std::string& part::getLiteralValue() const { - if (type_ == part_type::literal) + if (type_ != part_type::literal) { - return literal_; - } else { - throw std::domain_error("part::getLiteralValue is only valid for literal parts"); + throw std::domain_error("part is not a literal"); } + + return mpark::get(variant_); } filter part::synrestr_field::operator%=(std::string synrestr) const diff --git a/lib/part.h b/lib/part.h index 456bad0..7783a61 100644 --- a/lib/part.h +++ b/lib/part.h @@ -5,12 +5,12 @@ #include #include #include +#include +#include #include "field.h" #include "filter.h" #include "enums.h" -struct sqlite3_stmt; - namespace verbly { class database; @@ -20,11 +20,16 @@ namespace verbly { // Static factories - static part createNounPhrase(std::string role, std::set selrestrs, std::set synrestrs); + static part createNounPhrase( + std::string role, + std::set selrestrs, + std::set synrestrs); static part createVerb(); - static part createPreposition(std::vector choices, bool literal); + static part createPreposition( + std::vector choices, + bool literal); static part createAdjective(); @@ -32,41 +37,12 @@ namespace verbly { static part createLiteral(std::string value); - // Default constructor - - part() - { - } - // Construct from database - part(const database& db, sqlite3_stmt* row); - - // Copy and move constructors - - part(const part& other); - - part(part&& other); - - // Assignment - - part& operator=(part other); - - // Swap - - friend void swap(part& first, part& second); - - // Destructor - - ~part(); + part(const database& db, hatkirby::row row); // General accessors - bool isValid() const - { - return (type_ != part_type::invalid); - } - part_type getType() const { return type_; @@ -74,23 +50,23 @@ namespace verbly { // Noun phrase accessors - std::string getNounRole() const; + const std::string& getNounRole() const; - std::set getNounSelrestrs() const; + const std::set& getNounSelrestrs() const; - std::set getNounSynrestrs() const; + const std::set& getNounSynrestrs() const; bool nounHasSynrestr(std::string synrestr) const; // Preposition accessors - std::vector getPrepositionChoices() const; + const std::vector& getPrepositionChoices() const; bool isPrepositionLiteral() const; // Literal accessors - std::string getLiteralValue() const; + const std::string& getLiteralValue() const; // Type info @@ -123,7 +99,7 @@ namespace verbly { }; static const selrestr_field selrestrs; - + class synrestr_field { public: @@ -139,29 +115,36 @@ namespace verbly { private: - // Private constructors - - part(part_type t) : type_(t) - { - } - // Data - union { - struct { - std::string role; - std::set selrestrs; - std::set synrestrs; - } noun_phrase_; - struct { - std::vector choices; - bool literal; - } preposition_; - std::string literal_; + struct np_type { + std::string role; + std::set selrestrs; + std::set synrestrs; + }; + + struct prep_type { + std::vector choices; + bool literal; }; + using variant_type = + mpark::variant< + mpark::monostate, + np_type, + prep_type, + std::string>; + + variant_type variant_; + part_type type_ = part_type::invalid; + // Private constructors + + part(part_type t, variant_type v = {}) : type_(t), variant_(v) + { + } + }; }; diff --git a/lib/pronunciation.cpp b/lib/pronunciation.cpp index 1f36899..3aef815 100644 --- a/lib/pronunciation.cpp +++ b/lib/pronunciation.cpp @@ -1,5 +1,4 @@ #include "pronunciation.h" -#include #include #include "form.h" #include "word.h" @@ -22,22 +21,27 @@ namespace verbly { const field pronunciation::rhymes_field::rhymeJoin = field::joinField(object::pronunciation, "rhyme", object::pronunciation); const pronunciation::rhymes_field pronunciation::rhymes = {}; - pronunciation::pronunciation(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) + pronunciation::pronunciation( + const database& db, + hatkirby::row row) : + valid_(true) { - id_ = sqlite3_column_int(row, 0); + id_ = mpark::get(row[0]); - std::string phonemesStr(reinterpret_cast(sqlite3_column_text(row, 1))); - phonemes_ = hatkirby::split>(phonemesStr, " "); + phonemes_ = + hatkirby::split>( + mpark::get(row[1]), + " "); - syllables_ = sqlite3_column_int(row, 2); - stress_ = std::string(reinterpret_cast(sqlite3_column_text(row, 3))); + syllables_ = mpark::get(row[2]); + stress_ = mpark::get(row[3]); - if (sqlite3_column_type(row, 5) != SQLITE_NULL) + if (!mpark::holds_alternative(row[5])) { hasRhyme_ = true; - prerhyme_ = std::string(reinterpret_cast(sqlite3_column_text(row, 4))); - rhyme_ = std::string(reinterpret_cast(sqlite3_column_text(row, 5))); + prerhyme_ = mpark::get(row[4]); + rhyme_ = mpark::get(row[5]); } } diff --git a/lib/pronunciation.h b/lib/pronunciation.h index 73329e4..cd241bd 100644 --- a/lib/pronunciation.h +++ b/lib/pronunciation.h @@ -4,11 +4,10 @@ #include #include #include +#include #include "field.h" #include "filter.h" -struct sqlite3_stmt; - namespace verbly { class form; @@ -24,7 +23,7 @@ namespace verbly { // Construct from database - pronunciation(const database& db, sqlite3_stmt* row); + pronunciation(const database& db, hatkirby::row row); // Accessors @@ -63,7 +62,7 @@ namespace verbly { return syllables_; } - std::string getStress() const + const std::string& getStress() const { if (!valid_) { @@ -83,7 +82,7 @@ namespace verbly { return hasRhyme_; } - std::string getPrerhyme() const + const std::string& getPrerhyme() const { if (!valid_) { @@ -98,7 +97,7 @@ namespace verbly { return prerhyme_; } - std::string getRhyme() const + const std::string& getRhyme() const { if (!valid_) { @@ -167,8 +166,11 @@ namespace verbly { static const rhymes_field rhymes; private: - bool valid_ = false; + static const field prerhyme; + static const field rhyme; + + bool valid_ = false; int id_; std::vector phonemes_; int syllables_; @@ -176,12 +178,6 @@ namespace verbly { bool hasRhyme_ = false; std::string prerhyme_; std::string rhyme_; - - const database* db_; - - static const field prerhyme; - static const field rhyme; - }; }; diff --git a/lib/query.h b/lib/query.h index 0f490ed..65b4e9d 100644 --- a/lib/query.h +++ b/lib/query.h @@ -5,10 +5,8 @@ #include #include #include -#include -#include +#include #include "statement.h" -#include "binding.h" #include "order.h" namespace verbly { @@ -16,7 +14,10 @@ namespace verbly { class database_error : public std::logic_error { public: - database_error(std::string msg, std::string sqlMsg) : std::logic_error(msg + " (" + sqlMsg + ")") + database_error( + std::string msg, + std::string sqlMsg) : + std::logic_error(msg + " (" + sqlMsg + ")") { } }; @@ -25,107 +26,57 @@ namespace verbly { class query { public: - query(const database& db, sqlite3* ppdb, filter queryFilter, order sortOrder, int limit) : db_(&db) + query( + const database& db, + hatkirby::database& ppdb, + filter queryFilter, + order sortOrder, + int limit) : + db_(db), + ppdb_(ppdb) { if ((sortOrder.getType() == order::type::field) && (sortOrder.getSortField().getObject() != Object::objectType)) { - throw std::invalid_argument("Can only sort query by a field in the result table"); + throw std::invalid_argument( + "Can only sort query by a field in the result table"); } statement stmt(Object::objectType, std::move(queryFilter)); - std::string queryString = stmt.getQueryString(Object::select, std::move(sortOrder), limit); - std::list bindings = stmt.getBindings(); + queryString_ = + stmt.getQueryString(Object::select, std::move(sortOrder), limit); - if (sqlite3_prepare_v2(ppdb, queryString.c_str(), queryString.length(), &ppstmt_, NULL) != SQLITE_OK) - { - std::string errorMsg = sqlite3_errmsg(ppdb); - sqlite3_finalize(ppstmt_); - - throw database_error("Error preparing query", errorMsg); - } - - int i = 1; - for (const binding& value : bindings) - { - switch (value.getType()) - { - case binding::type::integer: - { - if (sqlite3_bind_int(ppstmt_, i, value.getInteger()) != SQLITE_OK) - { - std::string errorMsg = sqlite3_errmsg(ppdb); - sqlite3_finalize(ppstmt_); - - throw database_error("Error binding value to query", errorMsg); - } - - break; - } - - case binding::type::string: - { - if (sqlite3_bind_text(ppstmt_, i, value.getString().c_str(), value.getString().length(), SQLITE_TRANSIENT) != SQLITE_OK) - { - std::string errorMsg = sqlite3_errmsg(ppdb); - sqlite3_finalize(ppstmt_); - - throw database_error("Error binding value to query", errorMsg); - } - - break; - } - - case binding::type::invalid: - { - throw std::logic_error("Cannot use invalid bindings"); - } - - case binding::type::field: - { - throw std::logic_error("Compare field binding made it past statement generation"); - } - } - - i++; - } - } - - ~query() - { - sqlite3_finalize(ppstmt_); + bindings_ = stmt.getBindings(); } std::vector all() const { + std::vector rows = + ppdb_.queryAll(queryString_, bindings_); + std::vector result; - while (sqlite3_step(ppstmt_) == SQLITE_ROW) + for (hatkirby::row& r : rows) { - result.emplace_back(*db_, ppstmt_); + result.emplace_back(db_, std::move(r)); } - sqlite3_reset(ppstmt_); - return result; } Object first() const { - std::vector results = all(); - if (!results.empty()) - { - return results.front(); - } else { - throw std::logic_error("query returned empty dataset"); - } + return { db_, ppdb_.queryFirst(queryString_, bindings_) }; } private: - const database* db_; - sqlite3_stmt* ppstmt_; + const database& db_; + hatkirby::database& ppdb_; + + std::string queryString_; + std::list bindings_; }; }; diff --git a/lib/statement.cpp b/lib/statement.cpp index ac83084..669dc2a 100644 --- a/lib/statement.cpp +++ b/lib/statement.cpp @@ -133,19 +133,19 @@ namespace verbly { return queryStream.str(); } - std::list statement::getBindings() const + std::list statement::getBindings() const { - std::list result; + std::list result; for (const with& w : withs_) { - for (binding value : w.getCondition().flattenBindings()) + for (hatkirby::binding value : w.getCondition().flattenBindings()) { result.push_back(std::move(value)); } } - for (binding value : topCondition_.flattenBindings()) + for (hatkirby::binding value : topCondition_.flattenBindings()) { result.push_back(std::move(value)); } @@ -203,77 +203,152 @@ namespace verbly { { case filter::comparison::is_null: { - return condition(topTable_, clause.getField().getColumn(), true); + return { + topTable_, + clause.getField().getColumn(), + true + }; } case filter::comparison::is_not_null: { - return condition(topTable_, clause.getField().getColumn(), false); + return { + topTable_, + clause.getField().getColumn(), + false + }; } case filter::comparison::int_equals: { - return condition(topTable_, clause.getField().getColumn(), condition::comparison::equals, clause.getIntegerArgument()); + return { + topTable_, + clause.getField().getColumn(), + condition::comparison::equals, + clause.getIntegerArgument() + }; } case filter::comparison::int_does_not_equal: { - return condition(topTable_, clause.getField().getColumn(), condition::comparison::does_not_equal, clause.getIntegerArgument()); + return { + topTable_, + clause.getField().getColumn(), + condition::comparison::does_not_equal, + clause.getIntegerArgument() + }; } case filter::comparison::int_is_at_least: { - return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_at_least, clause.getIntegerArgument()); + return { + topTable_, + clause.getField().getColumn(), + condition::comparison::is_at_least, + clause.getIntegerArgument() + }; } case filter::comparison::int_is_greater_than: { - return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_greater_than, clause.getIntegerArgument()); + return { + topTable_, + clause.getField().getColumn(), + condition::comparison::is_greater_than, + clause.getIntegerArgument() + }; } case filter::comparison::int_is_at_most: { - return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_at_most, clause.getIntegerArgument()); + return { + topTable_, + clause.getField().getColumn(), + condition::comparison::is_at_most, + clause.getIntegerArgument() + }; } case filter::comparison::int_is_less_than: { - return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_less_than, clause.getIntegerArgument()); + return { + topTable_, + clause.getField().getColumn(), + condition::comparison::is_less_than, + clause.getIntegerArgument() + }; } case filter::comparison::boolean_equals: { - return condition(topTable_, clause.getField().getColumn(), condition::comparison::equals, clause.getBooleanArgument() ? 1 : 0); + return { + topTable_, + clause.getField().getColumn(), + condition::comparison::equals, + clause.getBooleanArgument() ? 1 : 0 + }; } case filter::comparison::string_equals: { - return condition(topTable_, clause.getField().getColumn(), condition::comparison::equals, clause.getStringArgument()); + return { + topTable_, + clause.getField().getColumn(), + condition::comparison::equals, + clause.getStringArgument() + }; } case filter::comparison::string_does_not_equal: { - return condition(topTable_, clause.getField().getColumn(), condition::comparison::does_not_equal, clause.getStringArgument()); + return { + topTable_, + clause.getField().getColumn(), + condition::comparison::does_not_equal, + clause.getStringArgument() + }; } case filter::comparison::string_is_like: { - return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_like, clause.getStringArgument()); + return { + topTable_, + clause.getField().getColumn(), + condition::comparison::is_like, + clause.getStringArgument() + }; } case filter::comparison::string_is_not_like: { - return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_not_like, clause.getStringArgument()); + return { + topTable_, + clause.getField().getColumn(), + condition::comparison::is_not_like, + clause.getStringArgument() + }; } case filter::comparison::field_equals: { - return condition(topTable_, clause.getField().getColumn(), condition::comparison::equals, {"", clause.getCompareField().getColumn()}, clause.getCompareField().getObject()); + return { + topTable_, + clause.getField().getColumn(), + condition::comparison::equals, + field_binding {"", clause.getCompareField().getColumn()}, + clause.getCompareField().getObject() + }; } case filter::comparison::field_does_not_equal: { - return condition(topTable_, clause.getField().getColumn(), condition::comparison::does_not_equal, {"", clause.getCompareField().getColumn()}, clause.getCompareField().getObject()); + return { + topTable_, + clause.getField().getColumn(), + condition::comparison::does_not_equal, + field_binding {"", clause.getCompareField().getColumn()}, + clause.getCompareField().getObject() + }; } case filter::comparison::matches: @@ -680,206 +755,19 @@ namespace verbly { << j.getJoinColumn(); } - statement::condition::condition(const condition& other) - { - type_ = other.type_; - - switch (type_) - { - case type::empty: - { - break; - } - - case type::singleton: - { - new(&singleton_.table_) std::string(other.singleton_.table_); - new(&singleton_.column_) std::string(other.singleton_.column_); - singleton_.comparison_ = other.singleton_.comparison_; - new(&singleton_.value_) binding(other.singleton_.value_); - singleton_.parentObject_ = other.singleton_.parentObject_; - - break; - } - - case type::group: - { - new(&group_.children_) std::list(other.group_.children_); - group_.orlogic_ = other.group_.orlogic_; - - break; - } - } - } - - statement::condition::condition(condition&& other) : condition() - { - swap(*this, other); - } - - statement::condition& statement::condition::operator=(condition other) - { - swap(*this, other); - - return *this; - } - - void swap(statement::condition& first, statement::condition& second) - { - using type = statement::condition::type; - using condition = statement::condition; - - type tempType = first.type_; - std::string tempTable; - std::string tempColumn; - condition::comparison tempComparison; - binding tempBinding; - object tempParentObject; - std::list tempChildren; - bool tempOrlogic; - - switch (tempType) - { - case type::empty: - { - break; - } - - case type::singleton: - { - tempTable = std::move(first.singleton_.table_); - tempColumn = std::move(first.singleton_.column_); - tempComparison = first.singleton_.comparison_; - tempBinding = std::move(first.singleton_.value_); - tempParentObject = first.singleton_.parentObject_; - - break; - } - - case type::group: - { - tempChildren = std::move(first.group_.children_); - tempOrlogic = first.group_.orlogic_; - - break; - } - } - - first.~condition(); - - first.type_ = second.type_; - - switch (first.type_) - { - case type::empty: - { - break; - } - - case type::singleton: - { - new(&first.singleton_.table_) std::string(std::move(second.singleton_.table_)); - new(&first.singleton_.column_) std::string(std::move(second.singleton_.column_)); - first.singleton_.comparison_ = second.singleton_.comparison_; - new(&first.singleton_.value_) binding(std::move(second.singleton_.value_)); - first.singleton_.parentObject_ = second.singleton_.parentObject_; - - break; - } - - case type::group: - { - new(&first.group_.children_) std::list(std::move(second.group_.children_)); - first.group_.orlogic_ = second.group_.orlogic_; - - break; - } - } - - second.~condition(); - - second.type_ = tempType; - - switch (second.type_) - { - case type::empty: - { - break; - } - - case type::singleton: - { - new(&second.singleton_.table_) std::string(std::move(tempTable)); - new(&second.singleton_.column_) std::string(std::move(tempColumn)); - second.singleton_.comparison_ = tempComparison; - new(&second.singleton_.value_) binding(std::move(tempBinding)); - second.singleton_.parentObject_ = tempParentObject; - - break; - } - - case type::group: - { - new(&second.group_.children_) std::list(std::move(tempChildren)); - second.group_.orlogic_ = tempOrlogic; - - break; - } - } - } - - statement::condition::~condition() - { - switch (type_) - { - case type::empty: - { - break; - } - - case type::singleton: - { - using string_type = std::string; - - singleton_.table_.~string_type(); - singleton_.column_.~string_type(); - singleton_.value_.~binding(); - - break; - } - - case type::group: - { - using list_type = std::list; - - group_.children_.~list_type(); - - break; - } - } - } - - statement::condition::condition() : type_(type::empty) - { - } - statement::condition::condition( std::string table, std::string column, bool isNull) : - type_(type::singleton) + type_(type::singleton), + variant_(singleton_type { + std::move(table), + std::move(column), + isNull ? comparison::is_null : comparison::is_not_null, + {}, + object::undefined + }) { - new(&singleton_.table_) std::string(std::move(table)); - new(&singleton_.column_) std::string(std::move(column)); - - if (isNull) - { - singleton_.comparison_ = comparison::is_null; - } else { - singleton_.comparison_ = comparison::is_not_null; - } - - singleton_.parentObject_ = object::undefined; } statement::condition::condition( @@ -888,201 +776,210 @@ namespace verbly { comparison comp, binding value, object parentObject) : - type_(type::singleton) + type_(type::singleton), + variant_(singleton_type { + std::move(table), + std::move(column), + comp, + std::move(value), + parentObject + }) { - new(&singleton_.table_) std::string(std::move(table)); - new(&singleton_.column_) std::string(std::move(column)); - singleton_.comparison_ = comp; - new(&singleton_.value_) binding(std::move(value)); - singleton_.parentObject_ = parentObject; } std::string statement::condition::toSql(bool toplevel, bool debug) const { + std::ostringstream sql; + switch (type_) { case type::empty: { - return ""; + break; } case type::singleton: { - switch (singleton_.comparison_) + const singleton_type& singleton = mpark::get(variant_); + + sql << singleton.table << "." << singleton.column; + + switch (singleton.comparison) { case comparison::equals: + case comparison::does_not_equal: { - if (debug) + if (singleton.comparison == comparison::equals) { - switch (singleton_.value_.getType()) - { - case binding::type::string: - { - return singleton_.table_ + "." + singleton_.column_ + " = \"" + singleton_.value_.getString() + "\""; - } - - case binding::type::integer: - { - return singleton_.table_ + "." + singleton_.column_ + " = " + std::to_string(singleton_.value_.getInteger()); - } - - case binding::type::field: - { - return singleton_.table_ + "." + singleton_.column_ + " = " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn(); - } - - case binding::type::invalid: - { - throw std::logic_error("Invalid binding in statement generation"); - } - } + sql << " = "; } else { - if (singleton_.value_.getType() == binding::type::field) - { - return singleton_.table_ + "." + singleton_.column_ + " = " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn(); - } else { - return singleton_.table_ + "." + singleton_.column_ + " = ?"; - } + sql << " != "; } - } - case comparison::does_not_equal: - { - if (debug) + if (mpark::holds_alternative(singleton.value)) + { + sql << std::get<0>(mpark::get(singleton.value)) + << "." + << std::get<1>(mpark::get(singleton.value)); + } else if (debug) { - switch (singleton_.value_.getType()) + if (mpark::holds_alternative(singleton.value)) { - case binding::type::string: - { - return singleton_.table_ + "." + singleton_.column_ + " != \"" + singleton_.value_.getString() + "\""; - } - - case binding::type::integer: - { - return singleton_.table_ + "." + singleton_.column_ + " != " + std::to_string(singleton_.value_.getInteger()); - } - - case binding::type::field: - { - return singleton_.table_ + "." + singleton_.column_ + " != " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn(); - } - - case binding::type::invalid: - { - throw std::logic_error("Invalid binding in statement generation"); - } + sql << "\"" << mpark::get(singleton.value) << "\""; } - } else { - if (singleton_.value_.getType() == binding::type::field) + else if (mpark::holds_alternative(singleton.value)) { - return singleton_.table_ + "." + singleton_.column_ + " != " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn(); - } else { - return singleton_.table_ + "." + singleton_.column_ + " != ?"; + sql << mpark::get(singleton.value); } + } else { + sql << "?"; } + + break; } case comparison::is_greater_than: { + sql << " > "; + if (debug) { - return singleton_.table_ + "." + singleton_.column_ + " > " + std::to_string(singleton_.value_.getInteger()); + sql << mpark::get(singleton.value); } else { - return singleton_.table_ + "." + singleton_.column_ + " > ?"; + sql << "?"; } + + break; } case comparison::is_at_most: { + sql << " <= "; + if (debug) { - return singleton_.table_ + "." + singleton_.column_ + " <= " + std::to_string(singleton_.value_.getInteger()); + sql << mpark::get(singleton.value); } else { - return singleton_.table_ + "." + singleton_.column_ + " <= ?"; + sql << "?"; } + + break; } case comparison::is_less_than: { + sql << " < "; + if (debug) { - return singleton_.table_ + "." + singleton_.column_ + " < " + std::to_string(singleton_.value_.getInteger()); + sql << mpark::get(singleton.value); } else { - return singleton_.table_ + "." + singleton_.column_ + " < ?"; + sql << "?"; } + + break; } case comparison::is_at_least: { + sql << " >= "; + if (debug) { - return singleton_.table_ + "." + singleton_.column_ + " >= " + std::to_string(singleton_.value_.getInteger()); + sql << mpark::get(singleton.value); } else { - return singleton_.table_ + "." + singleton_.column_ + " >= ?"; + sql << "?"; } + + break; } case comparison::is_like: { + sql << " LIKE "; + if (debug) { - return singleton_.table_ + "." + singleton_.column_ + " LIKE \"" + singleton_.value_.getString() + "\""; + sql << "\"" << mpark::get(singleton.value) << "\""; } else { - return singleton_.table_ + "." + singleton_.column_ + " LIKE ?"; + sql << "?"; } + + break; } case comparison::is_not_like: { + sql << " NOT LIKE "; + if (debug) { - return singleton_.table_ + "." + singleton_.column_ + " NOT LIKE \"" + singleton_.value_.getString() + "\""; + sql << "\"" << mpark::get(singleton.value) << "\""; } else { - return singleton_.table_ + "." + singleton_.column_ + " NOT LIKE ?"; + sql << "?"; } + + break; } case comparison::is_not_null: { - return singleton_.table_ + "." + singleton_.column_ + " IS NOT NULL"; + sql << " IS NOT NULL"; + + break; } case comparison::is_null: { - return singleton_.table_ + "." + singleton_.column_ + " IS NULL"; + sql << " IS NULL"; + + break; } } + + break; } case type::group: { + const group_type& group = mpark::get(variant_); + std::list clauses; - for (const condition& cond : group_.children_) + for (const condition& cond : group.children) { clauses.push_back(cond.toSql(false, debug)); } - if (clauses.empty()) + if (clauses.size() == 1) { - return ""; - } else if (clauses.size() == 1) + sql << clauses.front(); + } else if (!clauses.empty()) { - return clauses.front(); - } else { - std::string result = hatkirby::implode(std::begin(clauses), std::end(clauses), group_.orlogic_ ? " OR " : " AND "); + if (!toplevel) + { + sql << "("; + } + + sql << + hatkirby::implode( + std::begin(clauses), + std::end(clauses), + group.orlogic ? " OR " : " AND "); - if (toplevel) + if (!toplevel) { - return result; - } else { - return "(" + result + ")"; + sql << ")"; } } + + break; } } + + return sql.str(); } - std::list statement::condition::flattenBindings() const + std::list statement::condition::flattenBindings() const { switch (type_) { @@ -1093,39 +990,27 @@ namespace verbly { case type::singleton: { - if (singleton_.value_.getType() == binding::type::field) + const singleton_type& singleton = mpark::get(variant_); + + if (mpark::holds_alternative(singleton.value)) { - return {}; + return {{ mpark::get(singleton.value) }}; + } else if (mpark::holds_alternative(singleton.value)) + { + return {{ mpark::get(singleton.value) }}; } else { - switch (singleton_.comparison_) - { - case comparison::equals: - case comparison::does_not_equal: - case comparison::is_greater_than: - case comparison::is_at_most: - case comparison::is_less_than: - case comparison::is_at_least: - case comparison::is_like: - case comparison::is_not_like: - { - return {singleton_.value_}; - } - - case comparison::is_not_null: - case comparison::is_null: - { - return {}; - } - } + return {}; } } case type::group: { - std::list bindings; - for (const condition& cond : group_.children_) + const group_type& group = mpark::get(variant_); + + std::list bindings; + for (const condition& cond : group.children) { - for (binding value : cond.flattenBindings()) + for (hatkirby::binding value : cond.flattenBindings()) { bindings.push_back(std::move(value)); } @@ -1136,22 +1021,24 @@ namespace verbly { } } - statement::condition::condition(bool orlogic) : type_(type::group) + statement::condition::condition( + bool orlogic) : + type_(type::group), + variant_(group_type { {}, orlogic }) { - new(&group_.children_) std::list(); - group_.orlogic_ = orlogic; } statement::condition& statement::condition::operator+=(condition n) { - if (type_ == type::group) + if (type_ != type::group) { - group_.children_.push_back(std::move(n)); - - return *this; - } else { throw std::domain_error("Cannot add condition to non-group condition"); } + + group_type& group = mpark::get(variant_); + group.children.emplace_back(std::move(n)); + + return *this; } statement::condition& statement::condition::operator&=(condition n) @@ -1187,14 +1074,17 @@ namespace verbly { return *this; } - const std::list& statement::condition::getChildren() const + const std::list& statement::condition::getChildren() + const { - if (type_ == type::group) + if (type_ != type::group) { - return group_.children_; - } else { throw std::domain_error("Cannot get children of non-group condition"); } + + const group_type& group = mpark::get(variant_); + + return group.children; } statement::condition statement::condition::flatten() const @@ -1209,17 +1099,27 @@ namespace verbly { case type::group: { - condition result(group_.orlogic_); + const group_type& group = mpark::get(variant_); - for (const condition& child : group_.children_) + condition result(group.orlogic); + + for (const condition& child : group.children) { condition newChild = child.flatten(); - if ((newChild.type_ == type::group) && (newChild.group_.orlogic_ == group_.orlogic_)) + if (newChild.type_ == type::group) { - for (condition subChild : std::move(newChild.group_.children_)) + group_type& childGroup = + mpark::get(newChild.variant_); + + if (childGroup.orlogic == group.orlogic) { - result += std::move(subChild); + for (condition subChild : std::move(childGroup.children)) + { + result += std::move(subChild); + } + } else { + result += std::move(newChild); } } else { result += std::move(newChild); @@ -1231,7 +1131,9 @@ namespace verbly { } } - statement::condition statement::condition::resolveCompareFields(object context, std::string tableName) const + statement::condition statement::condition::resolveCompareFields( + object context, + std::string tableName) const { switch (type_) { @@ -1242,9 +1144,20 @@ namespace verbly { case type::singleton: { - if ((singleton_.parentObject_ != object::undefined) && (singleton_.parentObject_ == context)) + const singleton_type& singleton = mpark::get(variant_); + + if (singleton.parentObject != object::undefined && + singleton.parentObject == context) { - return condition(singleton_.table_, singleton_.column_, singleton_.comparison_, {tableName, singleton_.value_.getColumn()}); + return { + singleton.table, + singleton.column, + singleton.comparison, + field_binding { + tableName, + std::get<1>(mpark::get(singleton.value)) + } + }; } else { return *this; } @@ -1252,8 +1165,10 @@ namespace verbly { case type::group: { - condition result(group_.orlogic_); - for (const condition& cond : group_.children_) + const group_type& group = mpark::get(variant_); + + condition result(group.orlogic); + for (const condition& cond : group.children) { result += cond.resolveCompareFields(context, tableName); } diff --git a/lib/statement.h b/lib/statement.h index 2fadf05..6c2e42e 100644 --- a/lib/statement.h +++ b/lib/statement.h @@ -4,8 +4,8 @@ #include #include #include -#include -#include "binding.h" +#include +#include #include "enums.h" #include "field.h" #include "filter.h" @@ -15,14 +15,28 @@ namespace verbly { class filter; class order; + using field_binding = + std::tuple; + + using binding = + mpark::variant< + mpark::monostate, + std::string, + int, + field_binding>; + class statement { public: statement(object context, filter queryFilter); - std::string getQueryString(std::list select, order sortOrder, int limit, bool debug = false) const; + std::string getQueryString( + std::list select, + order sortOrder, + int limit, + bool debug = false) const; - std::list getBindings() const; + std::list getBindings() const; private: @@ -108,23 +122,6 @@ namespace verbly { is_null }; - // Copy and move constructors - - condition(const condition& other); - condition(condition&& other); - - // Assignment - - condition& operator=(condition other); - - // Swap - - friend void swap(condition& first, condition& second); - - // Destructor - - ~condition(); - // Accessors type getType() const @@ -134,13 +131,21 @@ namespace verbly { // Empty - condition(); + condition() = default; // Singleton - condition(std::string table, std::string column, bool isNull); + condition( + std::string table, + std::string column, + bool isNull); - condition(std::string table, std::string column, comparison comp, binding value, object parentObject = object::undefined); + condition( + std::string table, + std::string column, + comparison comp, + binding value, + object parentObject = object::undefined); // Group @@ -156,30 +161,39 @@ namespace verbly { std::string toSql(bool toplevel, bool debug = false) const; - std::list flattenBindings() const; + std::list flattenBindings() const; condition flatten() const; - condition resolveCompareFields(object context, std::string tableName) const; + condition resolveCompareFields( + object context, + std::string tableName) const; private: - union { - struct { - std::string table_; - std::string column_; - comparison comparison_; - binding value_; - object parentObject_; - } singleton_; - struct { - std::list children_; - bool orlogic_; - } group_; + + struct singleton_type { + std::string table; + std::string column; + comparison comparison; + binding value; + object parentObject; }; - type type_; - }; - friend void swap(condition& first, condition& second); + struct group_type { + std::list children; + bool orlogic; + }; + + using variant_type = + mpark::variant< + mpark::monostate, + singleton_type, + group_type>; + + variant_type variant_; + + type type_ = type::empty; + }; class with { public: diff --git a/lib/token.cpp b/lib/token.cpp index 7b1d1fa..b3c7062 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -5,322 +5,43 @@ namespace verbly { - token::token(const token& other) - { - type_ = other.type_; - - switch (type_) - { - case type::word: - { - new(&word_.word_) word(other.word_.word_); - word_.category_ = other.word_.category_; - - break; - } - - case type::literal: - { - new(&literal_) std::string(other.literal_); - - break; - } - - case type::part: - { - new(&part_) part(other.part_); - - break; - } - - case type::fillin: - { - new(&fillin_) std::set(other.fillin_); - - break; - } - - case type::utterance: - { - new(&utterance_) std::list(other.utterance_); - - break; - } - - case type::transform: - { - transform_.type_ = other.transform_.type_; - new(&transform_.strParam_) std::string(other.transform_.strParam_); - new(&transform_.strParam2_) std::string(other.transform_.strParam2_); - transform_.casingParam_ = other.transform_.casingParam_; - new(&transform_.inner_) std::unique_ptr(new token(*other.transform_.inner_)); - - break; - } - } - } - - token::token(token&& other) : token() - { - swap(*this, other); - } - - token& token::operator=(token other) - { - swap(*this, other); - - return *this; - } - - void swap(token& first, token& second) - { - using type = token::type; - using transform_type = token::transform_type; - using casing = token::casing; - - type tempType = first.type_; - word tempWord; - inflection tempCategory; - std::string tempLiteral; - part tempPart; - std::set tempFillin; - std::list tempUtterance; - transform_type tempTransformType; - std::string tempTransformStrParam; - std::string tempTransformStrParam2; - casing tempTransformCasingParam; - std::unique_ptr tempTransformInner; - - switch (tempType) - { - case type::word: - { - tempWord = std::move(first.word_.word_); - tempCategory = first.word_.category_; - - break; - } - - case type::literal: - { - tempLiteral = std::move(first.literal_); - - break; - } - - case type::part: - { - tempPart = std::move(first.part_); - - break; - } - - case type::fillin: - { - tempFillin = std::move(first.fillin_); - - break; - } - - case type::utterance: - { - tempUtterance = std::move(first.utterance_); - - break; - } - - case type::transform: - { - tempTransformType = first.transform_.type_; - tempTransformStrParam = std::move(first.transform_.strParam_); - tempTransformStrParam2 = std::move(first.transform_.strParam2_); - tempTransformCasingParam = first.transform_.casingParam_; - tempTransformInner = std::move(first.transform_.inner_); - - break; - } - } - - first.~token(); - - first.type_ = second.type_; - - switch (first.type_) - { - case type::word: - { - new(&first.word_.word_) word(std::move(second.word_.word_)); - first.word_.category_ = second.word_.category_; - - break; - } - - case type::literal: - { - new(&first.literal_) std::string(std::move(second.literal_)); - - break; - } - - case type::part: - { - new(&first.part_) part(std::move(second.part_)); - - break; - } - - case type::fillin: - { - new(&first.fillin_) std::set(std::move(second.fillin_)); - - break; - } - - case type::utterance: - { - new(&first.utterance_) std::list(std::move(second.utterance_)); - - break; - } - - case type::transform: - { - first.transform_.type_ = second.transform_.type_; - new(&first.transform_.strParam_) std::string(std::move(second.transform_.strParam_)); - new(&first.transform_.strParam2_) std::string(std::move(second.transform_.strParam2_)); - first.transform_.casingParam_ = second.transform_.casingParam_; - new(&first.transform_.inner_) std::unique_ptr(std::move(second.transform_.inner_)); - - break; - } - } - - second.~token(); - - second.type_ = tempType; - - switch (second.type_) - { - case type::word: - { - new(&second.word_.word_) word(std::move(tempWord)); - second.word_.category_ = tempCategory; - - break; - } - - case type::literal: - { - new(&second.literal_) std::string(std::move(tempLiteral)); - - break; - } - - case type::part: - { - new(&second.part_) part(std::move(tempPart)); - - break; - } - - case type::fillin: - { - new(&second.fillin_) std::set(std::move(tempFillin)); - - break; - } - - case type::utterance: - { - new(&second.utterance_) std::list(std::move(tempUtterance)); - - break; - } - - case type::transform: - { - second.transform_.type_ = tempTransformType; - new(&second.transform_.strParam_) std::string(std::move(tempTransformStrParam)); - new(&second.transform_.strParam2_) std::string(std::move(tempTransformStrParam2)); - second.transform_.casingParam_ = tempTransformCasingParam; - new(&second.transform_.inner_) std::unique_ptr(std::move(tempTransformInner)); - - break; - } - } - } - - token::~token() + bool token::isComplete() const { switch (type_) { case type::word: - { - word_.word_.~word(); - - break; - } - case type::literal: { - using string_type = std::string; - literal_.~string_type(); - - break; + return true; } case type::part: - { - part_.~part(); - - break; - } - case type::fillin: { - using set_type = std::set; - fillin_.~set_type(); - - break; + return false; } case type::utterance: { - using list_type = std::list; - utterance_.~list_type(); + const utterance_type& utterance = mpark::get(variant_); - break; + return std::all_of( + std::begin(utterance), + std::end(utterance), + [] (const token& tkn) { + return tkn.isComplete(); + }); } case type::transform: { - using string_type = std::string; - using ptr_type = std::unique_ptr; - - transform_.strParam_.~string_type(); - transform_.strParam2_.~string_type(); - transform_.inner_.~ptr_type(); + const transform_type& transform = mpark::get(variant_); - break; + return transform.inner->isComplete(); } } } - bool token::isComplete() const - { - switch (type_) - { - case type::word: return true; - case type::literal: return true; - case type::part: return false; - case type::fillin: return false; - case type::utterance: return std::all_of(std::begin(utterance_), std::end(utterance_), [] (const token& tkn) { - return tkn.isComplete(); - }); - case type::transform: return transform_.inner_->isComplete(); - } - } - std::string token::compile() const { return compileHelper(" ", false, casing::normal); @@ -335,8 +56,9 @@ namespace verbly { { case type::word: { - const form& wordForm = word_.word_.getInflections(word_.category_) - .front(); + const word_type& w = mpark::get(variant_); + + const form& wordForm = w.value.getInflections(w.category).front(); std::string result = wordForm.getText(); @@ -381,13 +103,12 @@ namespace verbly { } } - return result; } case type::literal: { - std::string result = literal_; + std::string result = mpark::get(variant_); if (indefiniteArticle && std::isalpha(result[0])) { @@ -435,14 +156,19 @@ namespace verbly { return result; } - case type::part: throw std::domain_error("Cannot compile incomplete token"); - case type::fillin: throw std::domain_error("Cannot compile incomplete token"); + case type::part: + case type::fillin: + { + throw std::domain_error("Cannot compile incomplete token"); + } case type::utterance: { + const utterance_type& utterance = mpark::get(variant_); + bool first = true; std::list compiled; - for (const token& tkn : utterance_) + for (const token& tkn : utterance) { casing propagateCasing = capitalization; if ((capitalization == casing::capitalize) && (!first)) @@ -458,58 +184,70 @@ namespace verbly { first = false; } - return hatkirby::implode(std::begin(compiled), std::end(compiled), separator); + return hatkirby::implode( + std::begin(compiled), + std::end(compiled), + separator); } case type::transform: { - switch (transform_.type_) + const transform_type& transform = mpark::get(variant_); + + switch (transform.type) { - case transform_type::separator: + case transform_mode::separator: { - return transform_.inner_->compileHelper( - transform_.strParam_, indefiniteArticle, capitalization); + return transform.inner->compileHelper( + transform.strParam, + indefiniteArticle, + capitalization); } - case transform_type::punctuation: + case transform_mode::punctuation: { - return transform_.inner_->compileHelper( - separator, indefiniteArticle, capitalization) - + transform_.strParam_; + return transform.inner->compileHelper( + separator, + indefiniteArticle, + capitalization) + transform.strParam; } - case transform_type::indefinite_article: + case transform_mode::indefinite_article: { - return transform_.inner_->compileHelper( - separator, true, capitalization); + return transform.inner->compileHelper( + separator, + true, + capitalization); } - case transform_type::capitalize: + case transform_mode::capitalize: { - return transform_.inner_->compileHelper( + return transform.inner->compileHelper( separator, indefiniteArticle, - transform_.casingParam_); + transform.casingParam); } - case transform_type::quote: + case transform_mode::quote: { - return transform_.strParam_ + - transform_.inner_->compileHelper( + return transform.strParam + + transform.inner->compileHelper( separator, indefiniteArticle, capitalization) + - transform_.strParam2_; + transform.strParam2; } } } } } - token::token(word arg, inflection category) : type_(type::word) + token::token( + word arg, + inflection category) : + type_(type::word), + variant_(word_type { std::move(arg), category }) { - new(&word_.word_) word(std::move(arg)); - word_.category_ = category; } const word& token::getWord() const @@ -519,7 +257,7 @@ namespace verbly { throw std::domain_error("Token is not a word"); } - return word_.word_; + return mpark::get(variant_).value; } token token::inflect(inflection category) const @@ -529,46 +267,57 @@ namespace verbly { throw std::domain_error("Token is not a word"); } - return token(word_.word_, category); + return { + mpark::get(variant_).value, + category + }; } - token::token(std::string arg) : type_(type::literal) + token::token( + std::string arg) : + type_(type::literal), + variant_(std::move(arg)) { - new(&literal_) std::string(std::move(arg)); } - token::token(const char* arg) : token(std::string(arg)) + token::token( + const char* arg) : + token(std::string(arg)) { } - std::string token::getLiteral() const + const std::string& token::getLiteral() const { if (type_ != type::literal) { throw std::domain_error("Token is not a literal"); } - return literal_; + return mpark::get(variant_); } - token::token(part arg) : type_(type::part) + token::token( + part arg) : + type_(type::part), + variant_(std::move(arg)) { - new(&part_) part(std::move(arg)); } - part token::getPart() const + const part& token::getPart() const { if (type_ != type::part) { throw std::domain_error("Token is not a part"); } - return part_; + return mpark::get(variant_); } - token::token(std::set synrestrs) : type_(type::fillin) + token::token( + std::set synrestrs) : + type_(type::fillin), + variant_(std::move(synrestrs)) { - new(&fillin_) std::set(std::move(synrestrs)); } const std::set& token::getSynrestrs() const @@ -578,7 +327,7 @@ namespace verbly { throw std::domain_error("Token is not a fillin"); } - return fillin_; + return mpark::get(variant_); } bool token::hasSynrestr(std::string synrestr) const @@ -588,7 +337,7 @@ namespace verbly { throw std::domain_error("Token is not a fillin"); } - return (fillin_.count(synrestr) == 1); + return mpark::get(variant_).count(synrestr); } void token::addSynrestr(std::string synrestr) @@ -598,22 +347,28 @@ namespace verbly { throw std::domain_error("Token is not a fillin"); } - fillin_.insert(std::move(synrestr)); + fillin_type& fillin = mpark::get(variant_); + fillin.insert(std::move(synrestr)); } - token::token() : type_(type::utterance) + token::token() : + type_(type::utterance), + variant_(utterance_type {}) { - new(&utterance_) std::list(); } - token::token(std::vector parts) : type_(type::utterance) + token::token( + std::vector parts) : + type_(type::utterance), + variant_(utterance_type { std::begin(parts), std::end(parts) }) { - new(&utterance_) std::list(std::begin(parts), std::end(parts)); } - token::token(std::initializer_list parts) : type_(type::utterance) + token::token( + std::initializer_list parts) : + type_(type::utterance), + variant_(utterance_type { std::move(parts) }) { - new(&utterance_) std::list(std::move(parts)); } token::iterator token::begin() @@ -623,7 +378,7 @@ namespace verbly { throw std::domain_error("Token is not an utterance"); } - return std::begin(utterance_); + return std::begin(mpark::get(variant_)); } token::const_iterator token::begin() const @@ -633,7 +388,7 @@ namespace verbly { throw std::domain_error("Token is not an utterance"); } - return std::begin(utterance_); + return std::begin(mpark::get(variant_)); } token::iterator token::end() @@ -643,7 +398,7 @@ namespace verbly { throw std::domain_error("Token is not an utterance"); } - return std::end(utterance_); + return std::end(mpark::get(variant_)); } token::const_iterator token::end() const @@ -653,7 +408,7 @@ namespace verbly { throw std::domain_error("Token is not an utterance"); } - return std::end(utterance_); + return std::end(mpark::get(variant_)); } token& token::operator<<(token arg) @@ -663,35 +418,36 @@ namespace verbly { throw std::domain_error("Token is not an utterance"); } - utterance_.push_back(std::move(arg)); + utterance_type& utterance = mpark::get(variant_); + utterance.push_back(std::move(arg)); return *this; } token token::separator(std::string param, token inner) { - return token(transform_type::separator, std::move(param), "", std::move(inner)); + return token(transform_mode::separator, std::move(param), "", std::move(inner)); } token token::punctuation(std::string param, token inner) { - return token(transform_type::punctuation, std::move(param), "", std::move(inner)); + return token(transform_mode::punctuation, std::move(param), "", std::move(inner)); } token token::indefiniteArticle(token inner) { - return token(transform_type::indefinite_article, "", "", std::move(inner)); + return token(transform_mode::indefinite_article, "", "", std::move(inner)); } token token::capitalize(casing param, token inner) { - return token(transform_type::capitalize, param, std::move(inner)); + return token(transform_mode::capitalize, param, std::move(inner)); } token token::quote(std::string opening, std::string closing, token inner) { return token( - transform_type::quote, + transform_mode::quote, std::move(opening), std::move(closing), std::move(inner)); @@ -704,7 +460,7 @@ namespace verbly { throw std::domain_error("Invalid access on non-tranform token"); } - return *transform_.inner_; + return *mpark::get(variant_).inner; } const token& token::getInnerToken() const @@ -714,33 +470,38 @@ namespace verbly { throw std::domain_error("Invalid access on non-tranform token"); } - return *transform_.inner_; + return *mpark::get(variant_).inner; } token::token( - transform_type type, + transform_mode type, std::string param1, std::string param2, token inner) : - type_(type::transform) + type_(type::transform), + variant_(transform_type { + type, + std::move(param1), + std::move(param2), + casing::normal, + new token(std::move(inner)) + }) { - transform_.type_ = type; - new(&transform_.strParam_) std::string(std::move(param1)); - new(&transform_.strParam2_) std::string(std::move(param2)); - new(&transform_.inner_) std::unique_ptr(new token(std::move(inner))); } token::token( - transform_type type, + transform_mode type, casing param, token inner) : - type_(type::transform) + type_(type::transform), + variant_(transform_type { + type, + {}, + {}, + param, + new token(std::move(inner)) + }) { - transform_.type_ = type; - new(&transform_.strParam_) std::string(); - new(&transform_.strParam2_) std::string(); - transform_.casingParam_ = param; - new(&transform_.inner_) std::unique_ptr(new token(std::move(inner))); } std::ostream& operator<<(std::ostream& os, token::type type) diff --git a/lib/token.h b/lib/token.h index ae7bf96..910a465 100644 --- a/lib/token.h +++ b/lib/token.h @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include "enums.h" #include "word.h" #include "part.h" @@ -22,23 +24,6 @@ namespace verbly { transform }; - // Copy & move constructors - - token(const token& other); - token(token&& other); - - // Assignment operator - - token& operator=(token other); - - // Swap - - friend void swap(token& first, token& second); - - // Destructor - - ~token(); - // Accessors type getType() const @@ -52,7 +37,8 @@ namespace verbly { bool isEmpty() const { - return ((type_ == type::utterance) && (utterance_.empty())); + return (type_ == type::utterance && + mpark::get(variant_).empty()); } // Word @@ -68,13 +54,13 @@ namespace verbly { token(std::string arg); token(const char* arg); - std::string getLiteral() const; + const std::string& getLiteral() const; // Part token(part arg); - part getPart() const; + const part& getPart() const; // Fillin @@ -128,7 +114,7 @@ namespace verbly { bool indefiniteArticle, casing capitalization) const; - enum class transform_type { + enum class transform_mode { separator, punctuation, indefinite_article, @@ -137,34 +123,46 @@ namespace verbly { }; token( - transform_type type, + transform_mode type, std::string param1, std::string param2, token inner); token( - transform_type type, + transform_mode type, casing param, token inner); - union { - struct { - word word_; - inflection category_; - } word_; - std::string literal_; - part part_; - std::set fillin_; - std::list utterance_; - struct { - transform_type type_; - std::string strParam_; - std::string strParam2_; - casing casingParam_; - std::unique_ptr inner_; - } transform_; + struct word_type { + word value; + inflection category; }; + + using literal_type = std::string; + + using fillin_type = std::set; + + using utterance_type = std::list; + + struct transform_type { + transform_mode type; + std::string strParam; + std::string strParam2; + casing casingParam; + hatkirby::recptr inner; + }; + + using variant_type = + mpark::variant< + word_type, + literal_type, + part, + fillin_type, + utterance_type, + transform_type>; + type type_; + variant_type variant_; }; std::ostream& operator<<(std::ostream& os, token::type type); diff --git a/lib/word.cpp b/lib/word.cpp index 6f0fe22..60657ba 100644 --- a/lib/word.cpp +++ b/lib/word.cpp @@ -1,5 +1,4 @@ #include "word.h" -#include #include "form.h" #include "util.h" #include "database.h" @@ -45,89 +44,27 @@ namespace verbly { return field::joinThroughWhere(object::word, "lemma_id", object::form, "lemmas_forms", "form_id", "category", static_cast(category)); } - word::word(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) + word::word(const database& db, hatkirby::row row) : db_(db), valid_(true) { - id_ = sqlite3_column_int(row, 0); - notionId_ = sqlite3_column_int(row, 1); - lemmaId_ = sqlite3_column_int(row, 2); + id_ = mpark::get(row[0]); - if (sqlite3_column_type(row, 3) != SQLITE_NULL) - { - hasTagCount_ = true; - tagCount_ = sqlite3_column_int(row, 3); - } - - if (sqlite3_column_type(row, 4) != SQLITE_NULL) - { - adjectivePosition_ = static_cast(sqlite3_column_int(row, 4)); - } - - if (sqlite3_column_type(row, 5) != SQLITE_NULL) - { - hasGroup_ = true; - groupId_ = sqlite3_column_int(row, 5); - } - } - - const notion& word::getNotion() const - { - if (!valid_) - { - throw std::domain_error("Bad access to uninitialized word"); - } + notion_ = db.notions(notion::id == mpark::get(row[1])).first(); - if (!notion_.isValid()) + if (!mpark::holds_alternative(row[3])) { - notion_ = db_->notions(notion::id == notionId_).first(); - } - - return notion_; - } - - bool word::hasFrames() const - { - if (!valid_) - { - throw std::domain_error("Bad access to uninitialized word"); - } - - if (!hasGroup_) - { - return false; - } - - if (!initializedFrames_) - { - initializeFrames(); - } - - return !frames_.empty(); - } - - const std::vector& word::getFrames() const - { - if (!valid_) - { - throw std::domain_error("Bad access to uninitialized word"); + hasTagCount_ = true; + tagCount_ = mpark::get(row[3]); } - if (!hasGroup_) + if (!mpark::holds_alternative(row[4])) { - throw std::domain_error("Word does not have a group"); + adjectivePosition_ = static_cast(mpark::get(row[4])); } - if (!initializedFrames_) + if (!mpark::holds_alternative(row[5])) { - initializeFrames(); + frames_ = db.frames(*this).all(); } - - return frames_; - } - - void word::initializeFrames() const - { - initializedFrames_ = true; - frames_ = db_->frames(*this, {}, -1).all(); } const form& word::getBaseForm() const @@ -167,7 +104,7 @@ namespace verbly { void word::initializeForm(inflection infl) const { - forms_[infl] = db_->forms(form::words(infl) %= *this, verbly::form::id, -1).all(); + forms_[infl] = db_.forms(form::words(infl) %= *this, verbly::form::id, -1).all(); } filter word::synonyms_field::operator%=(filter joinCondition) const diff --git a/lib/word.h b/lib/word.h index 8c8de51..f52cc4d 100644 --- a/lib/word.h +++ b/lib/word.h @@ -3,14 +3,13 @@ #include #include +#include #include "field.h" #include "filter.h" #include "notion.h" #include "frame.h" #include "form.h" -struct sqlite3_stmt; - namespace verbly { class database; @@ -24,7 +23,7 @@ namespace verbly { // Construct from database - word(const database& db, sqlite3_stmt* row); + word(const database& db, hatkirby::row row); // Accessors @@ -93,11 +92,35 @@ namespace verbly { return adjectivePosition_; } - const notion& getNotion() const; + const notion& getNotion() const + { + if (!valid_) + { + throw std::domain_error("Bad access to uninitialized word"); + } - bool hasFrames() const; + return notion_; + } - const std::vector& getFrames() const; + bool hasFrames() const + { + if (!valid_) + { + throw std::domain_error("Bad access to uninitialized word"); + } + + return !frames_.empty(); + } + + const std::vector& getFrames() const + { + if (!valid_) + { + throw std::domain_error("Bad access to uninitialized word"); + } + + return frames_; + } const form& getBaseForm() const; @@ -181,26 +204,17 @@ namespace verbly { private: void initializeForm(inflection category) const; - void initializeFrames() const; bool valid_ = false; - int id_; bool hasTagCount_ = false; int tagCount_; positioning adjectivePosition_ = positioning::undefined; - int notionId_; - int lemmaId_; - bool hasGroup_ = false; - int groupId_; - - const database* db_; - - mutable notion notion_; - mutable bool initializedFrames_ = false; - mutable std::vector frames_; + notion notion_; + std::vector frames_; mutable std::map> forms_; + const database& db_; }; }; diff --git a/vendor/hkutil b/vendor/hkutil index b430eb5..a9a5996 160000 --- a/vendor/hkutil +++ b/vendor/hkutil @@ -1 +1 @@ -Subproject commit b430eb58654298d17492a36c7bcda9f803a327fe +Subproject commit a9a5996310bb33207d3338f353aab97b9ed3a5e8 -- cgit 1.4.1