summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2018-09-27 21:40:52 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2018-09-27 21:40:52 -0400
commit38c17f093615a16a4b4ec6dc2b5d3edb5c1d3895 (patch)
tree8da5a3d0eacf5e2fd04c33f57d592e4c1ca303ad
parent3a8bfa95a5df04d97f05545d5bb8df5f3c3f96a3 (diff)
downloadverbly-38c17f093615a16a4b4ec6dc2b5d3edb5c1d3895.tar.gz
verbly-38c17f093615a16a4b4ec6dc2b5d3edb5c1d3895.tar.bz2
verbly-38c17f093615a16a4b4ec6dc2b5d3edb5c1d3895.zip
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
-rw-r--r--CMakeLists.txt17
-rw-r--r--lib/binding.cpp249
-rw-r--r--lib/binding.h82
-rw-r--r--lib/database.cpp123
-rw-r--r--lib/database.h61
-rw-r--r--lib/form.cpp52
-rw-r--r--lib/form.h26
-rw-r--r--lib/frame.cpp9
-rw-r--r--lib/frame.h10
-rw-r--r--lib/notion.cpp16
-rw-r--r--lib/notion.h118
-rw-r--r--lib/part.cpp350
-rw-r--r--lib/part.h97
-rw-r--r--lib/pronunciation.cpp24
-rw-r--r--lib/pronunciation.h22
-rw-r--r--lib/query.h107
-rw-r--r--lib/statement.cpp621
-rw-r--r--lib/statement.h96
-rw-r--r--lib/token.cpp495
-rw-r--r--lib/token.h76
-rw-r--r--lib/word.cpp85
-rw-r--r--lib/word.h50
m---------vendor/hkutil0
23 files changed, 866 insertions, 1920 deletions
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)
6 6
7set(CMAKE_BUILD_TYPE Debug) 7set(CMAKE_BUILD_TYPE Debug)
8 8
9include_directories( 9add_library(verbly
10 lib/filter.cpp
11 lib/field.cpp
12 lib/notion.cpp
13 lib/word.cpp
14 lib/frame.cpp
15 lib/part.cpp
16 lib/form.cpp
17 lib/pronunciation.cpp
18 lib/statement.cpp
19 lib/database.cpp
20 lib/token.cpp)
21
22target_include_directories(verbly PUBLIC
23 lib
10 vendor/hkutil 24 vendor/hkutil
11 vendor/hkutil/vendor) 25 vendor/hkutil/vendor)
12 26
13add_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)
14set_property(TARGET verbly PROPERTY CXX_STANDARD 11) 27set_property(TARGET verbly PROPERTY CXX_STANDARD 11)
15set_property(TARGET verbly PROPERTY CXX_STANDARD_REQUIRED ON) 28set_property(TARGET verbly PROPERTY CXX_STANDARD_REQUIRED ON)
16target_link_libraries(verbly ${sqlite3_LIBRARIES}) 29target_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 @@
1#include "binding.h"
2#include <stdexcept>
3#include <utility>
4
5namespace verbly {
6
7 binding::binding(const binding& other)
8 {
9 type_ = other.type_;
10
11 switch (type_)
12 {
13 case type::integer:
14 {
15 integer_ = other.integer_;
16
17 break;
18 }
19
20 case type::string:
21 {
22 new(&string_) std::string(other.string_);
23
24 break;
25 }
26
27 case type::field:
28 {
29 new(&field_.table_) std::string(other.field_.table_);
30 new(&field_.column_) std::string(other.field_.column_);
31
32 break;
33 }
34
35 case type::invalid:
36 {
37 break;
38 }
39 }
40 }
41
42 binding::binding(binding&& other) : binding()
43 {
44 swap(*this, other);
45 }
46
47 binding& binding::operator=(binding other)
48 {
49 swap(*this, other);
50
51 return *this;
52 }
53
54 void swap(binding& first, binding& second)
55 {
56 using type = binding::type;
57
58 type tempType = first.type_;
59 int tempInteger;
60 std::string tempString;
61 std::string tempTable;
62 std::string tempColumn;
63
64 switch (first.type_)
65 {
66 case type::integer:
67 {
68 tempInteger = first.integer_;
69
70 break;
71 }
72
73 case type::string:
74 {
75 tempString = std::move(tempString);
76
77 break;
78 }
79
80 case type::field:
81 {
82 tempTable = std::move(first.field_.table_);
83 tempColumn = std::move(first.field_.column_);
84
85 break;
86 }
87
88 case type::invalid:
89 {
90 break;
91 }
92 }
93
94 first.~binding();
95
96 first.type_ = second.type_;
97
98 switch (second.type_)
99 {
100 case type::integer:
101 {
102 first.integer_ = second.integer_;
103
104 break;
105 }
106
107 case type::string:
108 {
109 new(&first.string_) std::string(std::move(second.string_));
110
111 break;
112 }
113
114 case type::field:
115 {
116 new(&first.field_.table_) std::string(std::move(second.field_.table_));
117 new(&first.field_.column_) std::string(std::move(second.field_.column_));
118
119 break;
120 }
121
122 case type::invalid:
123 {
124 break;
125 }
126 }
127
128 second.~binding();
129
130 second.type_ = tempType;
131
132 switch (tempType)
133 {
134 case type::integer:
135 {
136 second.integer_ = tempInteger;
137
138 break;
139 }
140
141 case type::string:
142 {
143 new(&second.string_) std::string(std::move(tempString));
144
145 break;
146 }
147
148 case type::field:
149 {
150 new(&first.field_.table_) std::string(std::move(tempTable));
151 new(&first.field_.column_) std::string(std::move(tempColumn));
152
153 break;
154 }
155
156 case type::invalid:
157 {
158 break;
159 }
160 }
161 }
162
163 binding::~binding()
164 {
165 switch (type_)
166 {
167 case type::string:
168 {
169 using string_type = std::string;
170 string_.~string_type();
171
172 break;
173 }
174
175 case type::field:
176 {
177 using string_type = std::string;
178 field_.table_.~string_type();
179 field_.column_.~string_type();
180
181 break;
182 }
183
184 case type::integer:
185 case type::invalid:
186 {
187 break;
188 }
189 }
190 }
191
192 binding::binding(int arg) :
193 type_(type::integer),
194 integer_(arg)
195 {
196 }
197
198 int binding::getInteger() const
199 {
200 if (type_ != type::integer)
201 {
202 throw std::domain_error("binding::getInteger called on non-integer binding");
203 }
204
205 return integer_;
206 }
207
208 binding::binding(std::string arg) : type_(type::string)
209 {
210 new(&string_) std::string(std::move(arg));
211 }
212
213 std::string binding::getString() const
214 {
215 if (type_ != type::string)
216 {
217 throw std::domain_error("binding::getString called on non-string binding");
218 }
219
220 return string_;
221 }
222
223 binding::binding(std::string table, std::string column) : type_(type::field)
224 {
225 new(&field_.table_) std::string(std::move(table));
226 new(&field_.column_) std::string(std::move(column));
227 }
228
229 std::string binding::getTable() const
230 {
231 if (type_ != type::field)
232 {
233 throw std::domain_error("binding::getTable called on non-field binding");
234 }
235
236 return field_.table_;
237 }
238
239 std::string binding::getColumn() const
240 {
241 if (type_ != type::field)
242 {
243 throw std::domain_error("binding::getColumn called on non-field binding");
244 }
245
246 return field_.column_;
247 }
248
249};
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 @@
1#ifndef BINDING_H_CAE0B18E
2#define BINDING_H_CAE0B18E
3
4#include <string>
5
6namespace verbly {
7
8 class binding {
9 public:
10 enum class type {
11 invalid,
12 integer,
13 string,
14 field
15 };
16
17 // Default constructor
18
19 binding()
20 {
21 }
22
23 // Copy and move constructors
24
25 binding(const binding& other);
26 binding(binding&& other);
27
28 // Assignment
29
30 binding& operator=(binding other);
31
32 // Swap
33
34 friend void swap(binding& first, binding& second);
35
36 // Destructor
37
38 ~binding();
39
40 // Generic accessors
41
42 type getType() const
43 {
44 return type_;
45 }
46
47 // Integer
48
49 binding(int arg);
50
51 int getInteger() const;
52
53 // String
54
55 binding(std::string arg);
56
57 std::string getString() const;
58
59 // Field
60
61 binding(std::string table, std::string column);
62
63 std::string getTable() const;
64 std::string getColumn() const;
65
66 private:
67
68 union {
69 int integer_;
70 std::string string_;
71 struct {
72 std::string table_;
73 std::string column_;
74 } field_;
75 };
76
77 type type_ = type::invalid;
78 };
79
80};
81
82#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 @@
1#include "database.h" 1#include "database.h"
2#include <sqlite3.h>
3#include <stdexcept>
4#include <sstream> 2#include <sstream>
5#include "query.h" 3#include "query.h"
6#include "version.h" 4#include "version.h"
7 5
8namespace verbly { 6namespace verbly {
9 7
10 database::database(std::string path) 8 database::database(
9 std::string path) :
10 ppdb_(std::move(path), hatkirby::dbmode::read)
11 { 11 {
12 if (sqlite3_open_v2(path.c_str(), &ppdb_, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) 12 hatkirby::row version =
13 { 13 ppdb_.queryFirst("SELECT major, minor FROM version");
14 // We still have to free the resources allocated. In the event that
15 // allocation failed, ppdb will be null and sqlite3_close_v2 will just
16 // ignore it.
17 std::string errmsg(sqlite3_errmsg(ppdb_));
18 sqlite3_close_v2(ppdb_);
19
20 throw database_error("Could not open verbly datafile", errmsg);
21 }
22
23 std::string queryString = "SELECT major, minor FROM version";
24
25 sqlite3_stmt* ppstmt;
26 if (sqlite3_prepare_v2(ppdb_, queryString.c_str(), queryString.length(), &ppstmt, NULL) != SQLITE_OK)
27 {
28 std::string errorMsg = sqlite3_errmsg(ppdb_);
29 sqlite3_finalize(ppstmt);
30
31 throw database_error("Error reading database version", errorMsg);
32 }
33
34 if (sqlite3_step(ppstmt) != SQLITE_ROW)
35 {
36 std::string errorMsg = sqlite3_errmsg(ppdb_);
37 sqlite3_finalize(ppstmt);
38
39 throw database_error("Error reading database version", errorMsg);
40 }
41 14
42 major_ = sqlite3_column_int(ppstmt, 0); 15 major_ = mpark::get<int>(version[0]);
43 minor_ = sqlite3_column_int(ppstmt, 1); 16 minor_ = mpark::get<int>(version[1]);
44
45 sqlite3_finalize(ppstmt);
46 17
47 if (major_ != DATABASE_MAJOR_VERSION) 18 if (major_ != DATABASE_MAJOR_VERSION)
48 { 19 {
@@ -50,28 +21,6 @@ namespace verbly {
50 } 21 }
51 } 22 }
52 23
53 database::database(database&& other) : database()
54 {
55 swap(*this, other);
56 }
57
58 database& database::operator=(database&& other)
59 {
60 swap(*this, other);
61
62 return *this;
63 }
64
65 void swap(database& first, database& second)
66 {
67 std::swap(first.ppdb_, second.ppdb_);
68 }
69
70 database::~database()
71 {
72 sqlite3_close_v2(ppdb_);
73 }
74
75 query<notion> database::notions(filter where, order sortOrder, int limit) const 24 query<notion> database::notions(filter where, order sortOrder, int limit) const
76 { 25 {
77 return query<notion>(*this, ppdb_, std::move(where), std::move(sortOrder), limit); 26 return query<notion>(*this, ppdb_, std::move(where), std::move(sortOrder), limit);
@@ -104,65 +53,35 @@ namespace verbly {
104 53
105 std::set<std::string> database::selrestrs(int partId) const 54 std::set<std::string> database::selrestrs(int partId) const
106 { 55 {
107 std::string queryString = "SELECT selrestr FROM selrestrs WHERE part_id = ?"; 56 std::vector<hatkirby::row> rows =
108 57 ppdb_.queryAll(
109 sqlite3_stmt* ppstmt; 58 "SELECT selrestr FROM selrestrs WHERE part_id = ?",
110 if (sqlite3_prepare_v2(ppdb_, queryString.c_str(), queryString.length(), &ppstmt, NULL) != SQLITE_OK) 59 { partId });
111 {
112 std::string errorMsg = sqlite3_errmsg(ppdb_);
113 sqlite3_finalize(ppstmt);
114
115 throw database_error("Error preparing query", errorMsg);
116 }
117
118 if (sqlite3_bind_int(ppstmt, 1, partId) != SQLITE_OK)
119 {
120 std::string errorMsg = sqlite3_errmsg(ppdb_);
121 sqlite3_finalize(ppstmt);
122
123 throw database_error("Error binding value to query", errorMsg);
124 }
125 60
126 std::set<std::string> result; 61 std::set<std::string> result;
127 while (sqlite3_step(ppstmt) == SQLITE_ROW) 62
63 for (hatkirby::row& r : rows)
128 { 64 {
129 result.insert(reinterpret_cast<const char*>(sqlite3_column_blob(ppstmt, 0))); 65 result.emplace(std::move(mpark::get<std::string>(r[0])));
130 } 66 }
131 67
132 sqlite3_finalize(ppstmt);
133
134 return result; 68 return result;
135 } 69 }
136 70
137 std::set<std::string> database::synrestrs(int partId) const 71 std::set<std::string> database::synrestrs(int partId) const
138 { 72 {
139 std::string queryString = "SELECT synrestr FROM synrestrs WHERE part_id = ?"; 73 std::vector<hatkirby::row> rows =
140 74 ppdb_.queryAll(
141 sqlite3_stmt* ppstmt; 75 "SELECT synrestr FROM synrestrs WHERE part_id = ?",
142 if (sqlite3_prepare_v2(ppdb_, queryString.c_str(), queryString.length(), &ppstmt, NULL) != SQLITE_OK) 76 { partId });
143 {
144 std::string errorMsg = sqlite3_errmsg(ppdb_);
145 sqlite3_finalize(ppstmt);
146
147 throw database_error("Error preparing query", errorMsg);
148 }
149
150 if (sqlite3_bind_int(ppstmt, 1, partId) != SQLITE_OK)
151 {
152 std::string errorMsg = sqlite3_errmsg(ppdb_);
153 sqlite3_finalize(ppstmt);
154
155 throw database_error("Error binding value to query", errorMsg);
156 }
157 77
158 std::set<std::string> result; 78 std::set<std::string> result;
159 while (sqlite3_step(ppstmt) == SQLITE_ROW) 79
80 for (hatkirby::row& r : rows)
160 { 81 {
161 result.insert(reinterpret_cast<const char*>(sqlite3_column_blob(ppstmt, 0))); 82 result.emplace(std::move(mpark::get<std::string>(r[0])));
162 } 83 }
163 84
164 sqlite3_finalize(ppstmt);
165
166 return result; 85 return result;
167 } 86 }
168 87
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 @@
1#ifndef DATABASE_H_0B0A47D2 1#ifndef DATABASE_H_0B0A47D1
2#define DATABASE_H_0B0A47D2 2#define DATABASE_H_0B0A47D1
3 3
4#include <string> 4#include <string>
5#include <exception>
6#include <stdexcept> 5#include <stdexcept>
7#include <list>
8#include <set> 6#include <set>
7#include <hkutil/database.h>
9#include "notion.h" 8#include "notion.h"
10#include "word.h" 9#include "word.h"
11#include "frame.h" 10#include "frame.h"
@@ -14,8 +13,6 @@
14#include "pronunciation.h" 13#include "pronunciation.h"
15#include "order.h" 14#include "order.h"
16 15
17struct sqlite3;
18
19namespace verbly { 16namespace verbly {
20 17
21 template <typename Object> 18 template <typename Object>
@@ -28,24 +25,6 @@ namespace verbly {
28 25
29 explicit database(std::string path); 26 explicit database(std::string path);
30 27
31 // Disable copying
32
33 database(const database& other) = delete;
34 database& operator=(const database& other) = delete;
35
36 // Move constructor and move assignment
37
38 database(database&& other);
39 database& operator=(database&& other);
40
41 // Swap
42
43 friend void swap(database& first, database& second);
44
45 // Destructor
46
47 ~database();
48
49 // Information 28 // Information
50 29
51 int getMajorVersion() const 30 int getMajorVersion() const
@@ -60,17 +39,35 @@ namespace verbly {
60 39
61 // Queries 40 // Queries
62 41
63 query<notion> notions(filter where, order sortOrder = {}, int limit = 1) const; 42 query<notion> notions(
43 filter where,
44 order sortOrder = {},
45 int limit = 1) const;
64 46
65 query<word> words(filter where, order sortOrder = {}, int limit = 1) const; 47 query<word> words(
48 filter where,
49 order sortOrder = {},
50 int limit = 1) const;
66 51
67 query<frame> frames(filter where, order sortOrder = {}, int limit = 1) const; 52 query<frame> frames(
53 filter where,
54 order sortOrder = {},
55 int limit = 1) const;
68 56
69 query<part> parts(filter where, order sortOrder = {}, int limit = 1) const; 57 query<part> parts(
58 filter where,
59 order sortOrder = {},
60 int limit = 1) const;
70 61
71 query<form> forms(filter where, order sortOrder = {}, int limit = 1) const; 62 query<form> forms(
63 filter where,
64 order sortOrder = {},
65 int limit = 1) const;
72 66
73 query<pronunciation> pronunciations(filter where, order sortOrder = {}, int limit = 1) const; 67 query<pronunciation> pronunciations(
68 filter where,
69 order sortOrder = {},
70 int limit = 1) const;
74 71
75 std::set<std::string> selrestrs(int partId) const; 72 std::set<std::string> selrestrs(int partId) const;
76 73
@@ -78,9 +75,7 @@ namespace verbly {
78 75
79 private: 76 private:
80 77
81 database() = default; 78 mutable hatkirby::database ppdb_;
82
83 sqlite3* ppdb_ = nullptr;
84 79
85 int major_; 80 int major_;
86 int minor_; 81 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 @@
1#include "form.h" 1#include "form.h"
2#include <sqlite3.h>
3#include <algorithm> 2#include <algorithm>
4#include "filter.h" 3#include "filter.h"
5#include "database.h" 4#include "database.h"
@@ -24,29 +23,15 @@ namespace verbly {
24 return field::joinThroughWhere(object::form, "form_id", object::word, "lemmas_forms", "lemma_id", "category", static_cast<int>(category)); 23 return field::joinThroughWhere(object::form, "form_id", object::word, "lemmas_forms", "lemma_id", "category", static_cast<int>(category));
25 } 24 }
26 25
27 form::form(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) 26 form::form(const database& db, hatkirby::row row) : valid_(true)
28 { 27 {
29 id_ = sqlite3_column_int(row, 0); 28 id_ = mpark::get<int>(row[0]);
30 text_ = std::string(reinterpret_cast<const char*>(sqlite3_column_text(row, 1))); 29 text_ = mpark::get<std::string>(row[1]);
31 complexity_ = sqlite3_column_int(row, 2); 30 complexity_ = mpark::get<int>(row[2]);
32 proper_ = (sqlite3_column_int(row, 3) == 1); 31 proper_ = (mpark::get<int>(row[3]) == 1);
33 length_ = sqlite3_column_int(row, 4); 32 length_ = mpark::get<int>(row[4]);
34 }
35
36 const std::vector<pronunciation>& form::getPronunciations() const
37 {
38 if (!valid_)
39 {
40 throw std::domain_error("Bad access to uninitialized form");
41 }
42
43 if (!initializedPronunciations_)
44 {
45 pronunciations_ = db_->pronunciations(pronunciation::forms %= *this, pronunciation::id, -1).all();
46 initializedPronunciations_ = true;
47 }
48 33
49 return pronunciations_; 34 pronunciations_ = db.pronunciations(*this, pronunciation::id, -1).all();
50 } 35 }
51 36
52 bool form::startsWithVowelSound() const 37 bool form::startsWithVowelSound() const
@@ -56,17 +41,24 @@ namespace verbly {
56 throw std::domain_error("Bad access to uninitialized form"); 41 throw std::domain_error("Bad access to uninitialized form");
57 } 42 }
58 43
59 const std::vector<pronunciation>& pronunciations = getPronunciations(); 44 if (!pronunciations_.empty())
60 if (!pronunciations.empty())
61 { 45 {
62 return std::any_of(std::begin(pronunciations), std::end(pronunciations), [] (const pronunciation& p) { 46 return std::any_of(
63 return p.getPhonemes().front().find_first_of("012") != std::string::npos; 47 std::begin(pronunciations_),
64 }); 48 std::end(pronunciations_),
49 [] (const pronunciation& p) {
50 return p.getPhonemes().front().find_first_of("012") !=
51 std::string::npos;
52 });
65 } else { 53 } else {
66 // If the word is not in CMUDICT, fall back to checking whether the first letter is a vowel. 54 // If the word is not in CMUDICT, fall back to checking whether the first
67 // Not perfect but will work in most cases. 55 // letter is a vowel. Not perfect but will work in most cases.
68 char ch = std::tolower(text_.front()); 56 char ch = std::tolower(text_.front());
69 return (ch == 'a') || (ch == 'e') || (ch == 'i') || (ch == 'o') || (ch == 'u'); 57 return (ch == 'a') ||
58 (ch == 'e') ||
59 (ch == 'i') ||
60 (ch == 'o') ||
61 (ch == 'u');
70 } 62 }
71 } 63 }
72 64
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 @@
5#include <vector> 5#include <vector>
6#include <string> 6#include <string>
7#include <stdexcept> 7#include <stdexcept>
8#include <hkutil/database.h>
8#include "field.h" 9#include "field.h"
9#include "pronunciation.h" 10#include "pronunciation.h"
10#include "filter.h" 11#include "filter.h"
11 12
12struct sqlite3_stmt;
13
14namespace verbly { 13namespace verbly {
15 14
16 class database; 15 class database;
@@ -24,7 +23,7 @@ namespace verbly {
24 23
25 // Construct from database 24 // Construct from database
26 25
27 form(const database& db, sqlite3_stmt* row); 26 form(const database& db, hatkirby::row row);
28 27
29 // Accessors 28 // Accessors
30 29
@@ -43,7 +42,7 @@ namespace verbly {
43 return id_; 42 return id_;
44 } 43 }
45 44
46 std::string getText() const 45 const std::string& getText() const
47 { 46 {
48 if (!valid_) 47 if (!valid_)
49 { 48 {
@@ -83,7 +82,15 @@ namespace verbly {
83 return length_; 82 return length_;
84 } 83 }
85 84
86 const std::vector<pronunciation>& getPronunciations() const; 85 const std::vector<pronunciation>& getPronunciations() const
86 {
87 if (!valid_)
88 {
89 throw std::domain_error("Bad access to uninitialized form");
90 }
91
92 return pronunciations_;
93 }
87 94
88 // Convenience 95 // Convenience
89 96
@@ -130,19 +137,14 @@ namespace verbly {
130 static const field pronunciations; 137 static const field pronunciations;
131 138
132 private: 139 private:
133 bool valid_ = false;
134 140
141 bool valid_ = false;
135 int id_; 142 int id_;
136 std::string text_; 143 std::string text_;
137 int complexity_; 144 int complexity_;
138 bool proper_; 145 bool proper_;
139 int length_; 146 int length_;
140 147 std::vector<pronunciation> pronunciations_;
141 const database* db_;
142
143 mutable bool initializedPronunciations_ = false;
144 mutable std::vector<pronunciation> pronunciations_;
145
146 }; 148 };
147 149
148}; 150};
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 @@
1#include "frame.h" 1#include "frame.h"
2#include <sqlite3.h>
3#include "database.h" 2#include "database.h"
4#include "query.h" 3#include "query.h"
5 4
@@ -24,11 +23,11 @@ namespace verbly {
24 return field::joinWhere(object::frame, "frame_id", object::part, "part_index", index); 23 return field::joinWhere(object::frame, "frame_id", object::part, "part_index", index);
25 } 24 }
26 25
27 frame::frame(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) 26 frame::frame(const database& db, hatkirby::row row) : valid_(true)
28 { 27 {
29 id_ = sqlite3_column_int(row, 0); 28 id_ = mpark::get<int>(row[0]);
30 groupId_ = sqlite3_column_int(row, 1); 29 groupId_ = mpark::get<int>(row[1]);
31 length_ = sqlite3_column_int(row, 2); 30 length_ = mpark::get<int>(row[2]);
32 31
33 parts_ = db.parts(*this, verbly::part::index, -1).all(); 32 parts_ = db.parts(*this, verbly::part::index, -1).all();
34 } 33 }
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 @@
3 3
4#include <stdexcept> 4#include <stdexcept>
5#include <list> 5#include <list>
6#include <hkutil/database.h>
6#include "field.h" 7#include "field.h"
7#include "filter.h" 8#include "filter.h"
8#include "part.h" 9#include "part.h"
9 10
10struct sqlite3_stmt;
11
12namespace verbly { 11namespace verbly {
13 12
14 class database; 13 class database;
@@ -22,7 +21,7 @@ namespace verbly {
22 21
23 // Construct from database 22 // Construct from database
24 23
25 frame(const database& db, sqlite3_stmt* row); 24 frame(const database& db, hatkirby::row row);
26 25
27 // Accessors 26 // Accessors
28 27
@@ -101,15 +100,12 @@ namespace verbly {
101 static field parts(int index); 100 static field parts(int index);
102 101
103 private: 102 private:
104 bool valid_ = false;
105 103
104 bool valid_ = false;
106 int id_; 105 int id_;
107 int groupId_; 106 int groupId_;
108 int length_; 107 int length_;
109 std::vector<part> parts_; 108 std::vector<part> parts_;
110
111 const database* db_;
112
113 }; 109 };
114 110
115}; 111};
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 @@
1#include "notion.h" 1#include "notion.h"
2#include <sqlite3.h>
3#include <sstream> 2#include <sstream>
3#include <hkutil/database.h>
4 4
5namespace verbly { 5namespace verbly {
6 6
@@ -58,21 +58,21 @@ namespace verbly {
58 const field notion::preposition_group_field::isA = field::joinField(object::notion, "notion_id", "is_a"); 58 const field notion::preposition_group_field::isA = field::joinField(object::notion, "notion_id", "is_a");
59 const field notion::preposition_group_field::groupNameField = field::stringField("is_a", "groupname"); 59 const field notion::preposition_group_field::groupNameField = field::stringField("is_a", "groupname");
60 60
61 notion::notion(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) 61 notion::notion(const database& db, hatkirby::row row) : valid_(true)
62 { 62 {
63 id_ = sqlite3_column_int(row, 0); 63 id_ = mpark::get<int>(row[0]);
64 partOfSpeech_ = static_cast<part_of_speech>(sqlite3_column_int(row, 1)); 64 partOfSpeech_ = static_cast<part_of_speech>(mpark::get<int>(row[1]));
65 65
66 if (sqlite3_column_type(row, 2) != SQLITE_NULL) 66 if (!mpark::holds_alternative<std::nullptr_t>(row[2]))
67 { 67 {
68 hasWnid_ = true; 68 hasWnid_ = true;
69 wnid_ = sqlite3_column_int(row, 2); 69 wnid_ = mpark::get<int>(row[2]);
70 } 70 }
71 71
72 if (sqlite3_column_type(row, 3) != SQLITE_NULL) 72 if (!mpark::holds_alternative<std::nullptr_t>(row[3]))
73 { 73 {
74 hasNumOfImages_ = true; 74 hasNumOfImages_ = true;
75 numOfImages_ = sqlite3_column_int(row, 3); 75 numOfImages_ = mpark::get<int>(row[3]);
76 } 76 }
77 } 77 }
78 78
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 @@
3 3
4#include <stdexcept> 4#include <stdexcept>
5#include <string> 5#include <string>
6#include <hkutil/database.h>
6#include "field.h" 7#include "field.h"
7#include "filter.h" 8#include "filter.h"
8 9
9struct sqlite3_stmt;
10
11namespace verbly { 10namespace verbly {
12 11
13 class database; 12 class database;
14 13
15 class notion { 14 class notion {
16 public: 15 public:
17 16
18 // Default constructor 17 // Default constructor
19 18
20 notion() = default; 19 notion() = default;
21 20
22 // Construct from database 21 // Construct from database
23 22
24 notion(const database& db, sqlite3_stmt* row); 23 notion(const database& db, hatkirby::row row);
25 24
26 // Accessors 25 // Accessors
27 26
28 bool isValid() const 27 bool isValid() const
29 { 28 {
30 return valid_; 29 return valid_;
31 } 30 }
32 31
33 int getId() const 32 int getId() const
34 { 33 {
35 if (!valid_) 34 if (!valid_)
36 { 35 {
37 throw std::domain_error("Bad access to uninitialized notion"); 36 throw std::domain_error("Bad access to uninitialized notion");
38 } 37 }
39 38
40 return id_; 39 return id_;
41 } 40 }
42 41
43 part_of_speech getPartOfSpeech() const 42 part_of_speech getPartOfSpeech() const
44 { 43 {
45 if (!valid_) 44 if (!valid_)
46 { 45 {
47 throw std::domain_error("Bad access to uninitialized notion"); 46 throw std::domain_error("Bad access to uninitialized notion");
48 } 47 }
49 48
50 return partOfSpeech_; 49 return partOfSpeech_;
51 } 50 }
52 51
53 bool hasWnid() const 52 bool hasWnid() const
54 { 53 {
55 if (!valid_) 54 if (!valid_)
56 { 55 {
57 throw std::domain_error("Bad access to uninitialized notion"); 56 throw std::domain_error("Bad access to uninitialized notion");
58 } 57 }
59 58
60 return hasWnid_; 59 return hasWnid_;
61 } 60 }
62 61
63 int getWnid() const 62 int getWnid() const
64 { 63 {
65 if (!valid_) 64 if (!valid_)
66 { 65 {
67 throw std::domain_error("Bad access to uninitialized notion"); 66 throw std::domain_error("Bad access to uninitialized notion");
68 } 67 }
69 68
70 if (!hasWnid_) 69 if (!hasWnid_)
71 { 70 {
72 throw std::domain_error("Notion has no wnid"); 71 throw std::domain_error("Notion has no wnid");
73 } 72 }
74 73
75 return wnid_; 74 return wnid_;
76 } 75 }
77 76
78 bool hasNumOfImages() const 77 bool hasNumOfImages() const
79 { 78 {
80 if (!valid_) 79 if (!valid_)
81 { 80 {
82 throw std::domain_error("Bad access to uninitialized notion"); 81 throw std::domain_error("Bad access to uninitialized notion");
83 } 82 }
84 83
85 return hasNumOfImages_; 84 return hasNumOfImages_;
86 } 85 }
87 86
88 int getNumOfImages() const 87 int getNumOfImages() const
89 { 88 {
90 if (!valid_) 89 if (!valid_)
91 { 90 {
92 throw std::domain_error("Bad access to uninitialized notion"); 91 throw std::domain_error("Bad access to uninitialized notion");
93 } 92 }
94 93
95 if (!hasNumOfImages_) 94 if (!hasNumOfImages_)
96 { 95 {
97 throw std::domain_error("Notion does not have a number of images"); 96 throw std::domain_error("Notion does not have a number of images");
98 } 97 }
99 98
100 return numOfImages_; 99 return numOfImages_;
101 } 100 }
102 101
103 // Convenience 102 // Convenience
104 103
105 std::string getImageNetUrl() const; 104 std::string getImageNetUrl() const;
106 105
107 // Type info 106 // Type info
108 107
109 static const object objectType; 108 static const object objectType;
110 109
111 static const std::list<std::string> select; 110 static const std::list<std::string> select;
112 111
113 // Query fields 112 // Query fields
114 113
115 static const field id; 114 static const field id;
116 static const field partOfSpeech; 115 static const field partOfSpeech;
117 static const field wnid; 116 static const field wnid;
118 static const field numOfImages; 117 static const field numOfImages;
119 118
120 operator filter() const 119 operator filter() const
121 { 120 {
122 if (!valid_) 121 if (!valid_)
@@ -126,7 +125,7 @@ namespace verbly {
126 125
127 return (id == id_); 126 return (id == id_);
128 } 127 }
129 128
130 filter operator!() const 129 filter operator!() const
131 { 130 {
132 if (!valid_) 131 if (!valid_)
@@ -138,78 +137,75 @@ namespace verbly {
138 } 137 }
139 138
140 // Relationships with other objects 139 // Relationships with other objects
141 140
142 static const field words; 141 static const field words;
143 142
144 // Relationships with self 143 // Relationships with self
145 144
146 static const field hypernyms; 145 static const field hypernyms;
147 static const field hyponyms; 146 static const field hyponyms;
148 147
149 static const field fullHypernyms; 148 static const field fullHypernyms;
150 static const field fullHyponyms; 149 static const field fullHyponyms;
151 150
152 static const field instances; 151 static const field instances;
153 static const field classes; 152 static const field classes;
154 153
155 static const field memberMeronyms; 154 static const field memberMeronyms;
156 static const field memberHolonyms; 155 static const field memberHolonyms;
157 156
158 static const field fullMemberMeronyms; 157 static const field fullMemberMeronyms;
159 static const field fullMemberHolonyms; 158 static const field fullMemberHolonyms;
160 159
161 static const field partMeronyms; 160 static const field partMeronyms;
162 static const field partHolonyms; 161 static const field partHolonyms;
163 162
164 static const field fullPartMeronyms; 163 static const field fullPartMeronyms;
165 static const field fullPartHolonyms; 164 static const field fullPartHolonyms;
166 165
167 static const field substanceMeronyms; 166 static const field substanceMeronyms;
168 static const field substanceHolonyms; 167 static const field substanceHolonyms;
169 168
170 static const field fullSubstanceMeronyms; 169 static const field fullSubstanceMeronyms;
171 static const field fullSubstanceHolonyms; 170 static const field fullSubstanceHolonyms;
172 171
173 static const field variants; 172 static const field variants;
174 static const field attributes; 173 static const field attributes;
175 174
176 static const field similarAdjectives; 175 static const field similarAdjectives;
177 176
178 static const field entails; 177 static const field entails;
179 static const field entailedBy; 178 static const field entailedBy;
180 179
181 static const field causes; 180 static const field causes;
182 static const field effects; 181 static const field effects;
183 182
184 // Preposition group relationship 183 // Preposition group relationship
185 184
186 class preposition_group_field { 185 class preposition_group_field {
187 public: 186 public:
188 187
189 filter operator==(std::string groupName) const; 188 filter operator==(std::string groupName) const;
190 189
191 private: 190 private:
192 191
193 static const field isA; 192 static const field isA;
194 static const field groupNameField; 193 static const field groupNameField;
195 }; 194 };
196 195
197 static const preposition_group_field prepositionGroups; 196 static const preposition_group_field prepositionGroups;
198 197
199 private: 198 private:
199
200 bool valid_ = false; 200 bool valid_ = false;
201
202 int id_; 201 int id_;
203 part_of_speech partOfSpeech_; 202 part_of_speech partOfSpeech_;
204 bool hasWnid_ = false; 203 bool hasWnid_ = false;
205 int wnid_; 204 int wnid_;
206 bool hasNumOfImages_ = false; 205 bool hasNumOfImages_ = false;
207 int numOfImages_; 206 int numOfImages_;
208
209 const database* db_;
210
211 }; 207 };
212 208
213}; 209};
214 210
215#endif /* end of include guard: NOTION_H_FD1C7646 */ 211#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 @@
1#include "part.h" 1#include "part.h"
2#include <stdexcept> 2#include <stdexcept>
3#include <sqlite3.h>
4#include <hkutil/string.h> 3#include <hkutil/string.h>
5#include "database.h" 4#include "database.h"
6 5
@@ -26,15 +25,19 @@ namespace verbly {
26 const part::selrestr_field part::selrestrs = {}; 25 const part::selrestr_field part::selrestrs = {};
27 const part::synrestr_field part::synrestrs = {}; 26 const part::synrestr_field part::synrestrs = {};
28 27
29 part part::createNounPhrase(std::string role, std::set<std::string> selrestrs, std::set<std::string> synrestrs) 28 part part::createNounPhrase(
29 std::string role,
30 std::set<std::string> selrestrs,
31 std::set<std::string> synrestrs)
30 { 32 {
31 part p(part_type::noun_phrase); 33 return part {
32 34 part_type::noun_phrase,
33 new(&p.noun_phrase_.role) std::string(std::move(role)); 35 np_type {
34 new(&p.noun_phrase_.selrestrs) std::set<std::string>(std::move(selrestrs)); 36 std::move(role),
35 new(&p.noun_phrase_.synrestrs) std::set<std::string>(std::move(synrestrs)); 37 std::move(selrestrs),
36 38 std::move(synrestrs)
37 return p; 39 }
40 };
38 } 41 }
39 42
40 part part::createVerb() 43 part part::createVerb()
@@ -44,12 +47,13 @@ namespace verbly {
44 47
45 part part::createPreposition(std::vector<std::string> choices, bool literal) 48 part part::createPreposition(std::vector<std::string> choices, bool literal)
46 { 49 {
47 part p(part_type::preposition); 50 return part {
48 51 part_type::preposition,
49 new(&p.preposition_.choices) std::vector<std::string>(std::move(choices)); 52 prep_type {
50 p.preposition_.literal = literal; 53 std::move(choices),
51 54 literal
52 return p; 55 }
56 };
53 } 57 }
54 58
55 part part::createAdjective() 59 part part::createAdjective()
@@ -64,83 +68,53 @@ namespace verbly {
64 68
65 part part::createLiteral(std::string value) 69 part part::createLiteral(std::string value)
66 { 70 {
67 part p(part_type::literal); 71 return part {
68 72 part_type::literal,
69 new(&p.literal_) std::string(std::move(value)); 73 std::move(value)
70 74 };
71 return p;
72 } 75 }
73 76
74 part::part(const database& db, sqlite3_stmt* row) 77 part::part(const database& db, hatkirby::row row)
75 { 78 {
76 int id = sqlite3_column_int(row, 0); 79 int id = mpark::get<int>(row[0]);
77 80
78 type_ = static_cast<part_type>(sqlite3_column_int(row, 3)); 81 type_ = static_cast<part_type>(mpark::get<int>(row[3]));
79 82
80 switch (type_) 83 switch (type_)
81 { 84 {
82 case part_type::noun_phrase: 85 case part_type::noun_phrase:
83 { 86 {
84 new(&noun_phrase_.role) std::string(reinterpret_cast<const char*>(sqlite3_column_blob(row, 4))); 87 variant_ = np_type {
85 new(&noun_phrase_.selrestrs) std::set<std::string>(db.selrestrs(id)); 88 mpark::get<std::string>(row[4]),
86 new(&noun_phrase_.synrestrs) std::set<std::string>(db.synrestrs(id)); 89 db.selrestrs(id),
90 db.synrestrs(id)
91 };
87 92
88 break; 93 break;
89 } 94 }
90 95
91 case part_type::preposition: 96 case part_type::preposition:
92 { 97 {
93 std::string serializedChoices(reinterpret_cast<const char*>(sqlite3_column_blob(row, 5))); 98 hatkirby::blob_type raw =
94 new(&preposition_.choices) std::vector<std::string>(hatkirby::split<std::vector<std::string>>(serializedChoices, ",")); 99 mpark::get<hatkirby::blob_type>(row[5]);
95
96 preposition_.literal = (sqlite3_column_int(row, 6) == 1);
97
98 break;
99 }
100
101 case part_type::literal:
102 {
103 new(&literal_) std::string(reinterpret_cast<const char*>(sqlite3_column_blob(row, 7)));
104 100
105 break; 101 std::string serializedChoices(
106 } 102 std::begin(raw),
103 std::end(raw));
107 104
108 case part_type::verb: 105 variant_ = prep_type {
109 case part_type::adjective: 106 hatkirby::split<std::vector<std::string>>(
110 case part_type::adverb: 107 std::move(serializedChoices),
111 case part_type::invalid: 108 ","),
112 { 109 (mpark::get<int>(row[6]) == 1)
113 break; 110 };
114 }
115 }
116 }
117
118 part::part(const part& other)
119 {
120 type_ = other.type_;
121
122 switch (type_)
123 {
124 case part_type::noun_phrase:
125 {
126 new(&noun_phrase_.role) std::string(other.noun_phrase_.role);
127 new(&noun_phrase_.selrestrs) std::set<std::string>(other.noun_phrase_.selrestrs);
128 new(&noun_phrase_.synrestrs) std::set<std::string>(other.noun_phrase_.synrestrs);
129
130 break;
131 }
132
133 case part_type::preposition:
134 {
135 new(&preposition_.choices) std::vector<std::string>(other.preposition_.choices);
136 preposition_.literal = other.preposition_.literal;
137 111
138 break; 112 break;
139 } 113 }
140 114
141 case part_type::literal: 115 case part_type::literal:
142 { 116 {
143 new(&literal_) std::string(other.literal_); 117 variant_ = mpark::get<std::string>(row[7]);
144 118
145 break; 119 break;
146 } 120 }
@@ -155,256 +129,74 @@ namespace verbly {
155 } 129 }
156 } 130 }
157 131
158 part::part(part&& other) : part() 132 const std::string& part::getNounRole() const
159 { 133 {
160 swap(*this, other); 134 if (type_ != part_type::noun_phrase)
161 }
162
163 part& part::operator=(part other)
164 {
165 swap(*this, other);
166
167 return *this;
168 }
169
170 void swap(part& first, part& second)
171 {
172 using type = part_type;
173
174 type tempType = first.type_;
175 std::string tempRole;
176 std::set<std::string> tempSelrestrs;
177 std::set<std::string> tempSynrestrs;
178 std::vector<std::string> tempChoices;
179 bool tempPrepLiteral;
180 std::string tempLiteralValue;
181
182 switch (tempType)
183 {
184 case type::noun_phrase:
185 {
186 tempRole = std::move(first.noun_phrase_.role);
187 tempSelrestrs = std::move(first.noun_phrase_.selrestrs);
188 tempSynrestrs = std::move(first.noun_phrase_.synrestrs);
189
190 break;
191 }
192
193 case type::preposition:
194 {
195 tempChoices = std::move(first.preposition_.choices);
196 tempPrepLiteral = first.preposition_.literal;
197
198 break;
199 }
200
201 case type::literal:
202 {
203 tempLiteralValue = std::move(first.literal_);
204
205 break;
206 }
207
208 case type::verb:
209 case type::adjective:
210 case type::adverb:
211 case type::invalid:
212 {
213 break;
214 }
215 }
216
217 first.~part();
218
219 first.type_ = second.type_;
220
221 switch (first.type_)
222 { 135 {
223 case type::noun_phrase: 136 throw std::domain_error("part is not a noun phrase");
224 {
225 new(&first.noun_phrase_.role) std::string(std::move(second.noun_phrase_.role));
226 new(&first.noun_phrase_.selrestrs) std::set<std::string>(std::move(second.noun_phrase_.selrestrs));
227 new(&first.noun_phrase_.synrestrs) std::set<std::string>(std::move(second.noun_phrase_.synrestrs));
228
229 break;
230 }
231
232 case type::preposition:
233 {
234 new(&first.preposition_.choices) std::vector<std::string>(std::move(second.preposition_.choices));
235 first.preposition_.literal = second.preposition_.literal;
236
237 break;
238 }
239
240 case type::literal:
241 {
242 new(&first.literal_) std::string(std::move(second.literal_));
243
244 break;
245 }
246
247 case type::verb:
248 case type::adjective:
249 case type::adverb:
250 case type::invalid:
251 {
252 break;
253 }
254 } 137 }
255 138
256 second.~part(); 139 return mpark::get<np_type>(variant_).role;
257
258 second.type_ = tempType;
259
260 switch (second.type_)
261 {
262 case type::noun_phrase:
263 {
264 new(&second.noun_phrase_.role) std::string(std::move(tempRole));
265 new(&second.noun_phrase_.selrestrs) std::set<std::string>(std::move(tempSelrestrs));
266 new(&second.noun_phrase_.synrestrs) std::set<std::string>(std::move(tempSynrestrs));
267
268 break;
269 }
270
271 case type::preposition:
272 {
273 new(&second.preposition_.choices) std::vector<std::string>(std::move(tempChoices));
274 second.preposition_.literal = tempPrepLiteral;
275
276 break;
277 }
278
279 case type::literal:
280 {
281 new(&second.literal_) std::string(std::move(tempLiteralValue));
282
283 break;
284 }
285
286 case type::verb:
287 case type::adjective:
288 case type::adverb:
289 case type::invalid:
290 {
291 break;
292 }
293 }
294 } 140 }
295 141
296 part::~part() 142 const std::set<std::string>& part::getNounSelrestrs() const
297 { 143 {
298 switch (type_) 144 if (type_ != part_type::noun_phrase)
299 { 145 {
300 case part_type::noun_phrase: 146 throw std::domain_error("part is not a noun phrase");
301 {
302 using string_type = std::string;
303 using set_type = std::set<std::string>;
304
305 noun_phrase_.role.~string_type();
306 noun_phrase_.selrestrs.~set_type();
307 noun_phrase_.synrestrs.~set_type();
308
309 break;
310 }
311
312 case part_type::preposition:
313 {
314 using vector_type = std::vector<std::string>;
315
316 preposition_.choices.~vector_type();
317
318 break;
319 }
320
321 case part_type::literal:
322 {
323 using string_type = std::string;
324
325 literal_.~string_type();
326
327 break;
328 }
329
330 case part_type::verb:
331 case part_type::adjective:
332 case part_type::adverb:
333 case part_type::invalid:
334 {
335 break;
336 }
337 } 147 }
338 }
339 148
340 std::string part::getNounRole() const 149 return mpark::get<np_type>(variant_).selrestrs;
341 {
342 if (type_ == part_type::noun_phrase)
343 {
344 return noun_phrase_.role;
345 } else {
346 throw std::domain_error("part::getNounRole is only valid for noun phrase parts");
347 }
348 } 150 }
349 151
350 std::set<std::string> part::getNounSelrestrs() const 152 const std::set<std::string>& part::getNounSynrestrs() const
351 { 153 {
352 if (type_ == part_type::noun_phrase) 154 if (type_ != part_type::noun_phrase)
353 { 155 {
354 return noun_phrase_.selrestrs; 156 throw std::domain_error("part is not a noun phrase");
355 } else {
356 throw std::domain_error("part::getNounSelrestrs is only valid for noun phrase parts");
357 } 157 }
358 }
359 158
360 std::set<std::string> part::getNounSynrestrs() const 159 return mpark::get<np_type>(variant_).synrestrs;
361 {
362 if (type_ == part_type::noun_phrase)
363 {
364 return noun_phrase_.synrestrs;
365 } else {
366 throw std::domain_error("part::getNounSynrestrs is only valid for noun phrase parts");
367 }
368 } 160 }
369 161
370 bool part::nounHasSynrestr(std::string synrestr) const 162 bool part::nounHasSynrestr(std::string synrestr) const
371 { 163 {
372 if (type_ != part_type::noun_phrase) 164 if (type_ != part_type::noun_phrase)
373 { 165 {
374 throw std::domain_error("part::nounHasSynrestr is only valid for noun phrase parts"); 166 throw std::domain_error("part is not a noun phrase");
375 } 167 }
376 168
377 return (noun_phrase_.synrestrs.count(synrestr) == 1); 169 return mpark::get<np_type>(variant_).synrestrs.count(synrestr);
378 } 170 }
379 171
380 std::vector<std::string> part::getPrepositionChoices() const 172 const std::vector<std::string>& part::getPrepositionChoices() const
381 { 173 {
382 if (type_ == part_type::preposition) 174 if (type_ != part_type::preposition)
383 { 175 {
384 return preposition_.choices; 176 throw std::domain_error("part is not a preposition");
385 } else {
386 throw std::domain_error("part::getPrepositionChoices is only valid for preposition parts");
387 } 177 }
178
179 return mpark::get<prep_type>(variant_).choices;
388 } 180 }
389 181
390 bool part::isPrepositionLiteral() const 182 bool part::isPrepositionLiteral() const
391 { 183 {
392 if (type_ == part_type::preposition) 184 if (type_ != part_type::preposition)
393 { 185 {
394 return preposition_.literal; 186 throw std::domain_error("part is not a preposition");
395 } else {
396 throw std::domain_error("part::isPrepositionLiteral is only valid for preposition parts");
397 } 187 }
188
189 return mpark::get<prep_type>(variant_).literal;
398 } 190 }
399 191
400 std::string part::getLiteralValue() const 192 const std::string& part::getLiteralValue() const
401 { 193 {
402 if (type_ == part_type::literal) 194 if (type_ != part_type::literal)
403 { 195 {
404 return literal_; 196 throw std::domain_error("part is not a literal");
405 } else {
406 throw std::domain_error("part::getLiteralValue is only valid for literal parts");
407 } 197 }
198
199 return mpark::get<std::string>(variant_);
408 } 200 }
409 201
410 filter part::synrestr_field::operator%=(std::string synrestr) const 202 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 @@
5#include <vector> 5#include <vector>
6#include <set> 6#include <set>
7#include <list> 7#include <list>
8#include <hkutil/database.h>
9#include <variant.hpp>
8#include "field.h" 10#include "field.h"
9#include "filter.h" 11#include "filter.h"
10#include "enums.h" 12#include "enums.h"
11 13
12struct sqlite3_stmt;
13
14namespace verbly { 14namespace verbly {
15 15
16 class database; 16 class database;
@@ -20,11 +20,16 @@ namespace verbly {
20 20
21 // Static factories 21 // Static factories
22 22
23 static part createNounPhrase(std::string role, std::set<std::string> selrestrs, std::set<std::string> synrestrs); 23 static part createNounPhrase(
24 std::string role,
25 std::set<std::string> selrestrs,
26 std::set<std::string> synrestrs);
24 27
25 static part createVerb(); 28 static part createVerb();
26 29
27 static part createPreposition(std::vector<std::string> choices, bool literal); 30 static part createPreposition(
31 std::vector<std::string> choices,
32 bool literal);
28 33
29 static part createAdjective(); 34 static part createAdjective();
30 35
@@ -32,41 +37,12 @@ namespace verbly {
32 37
33 static part createLiteral(std::string value); 38 static part createLiteral(std::string value);
34 39
35 // Default constructor
36
37 part()
38 {
39 }
40
41 // Construct from database 40 // Construct from database
42 41
43 part(const database& db, sqlite3_stmt* row); 42 part(const database& db, hatkirby::row row);
44
45 // Copy and move constructors
46
47 part(const part& other);
48
49 part(part&& other);
50
51 // Assignment
52
53 part& operator=(part other);
54
55 // Swap
56
57 friend void swap(part& first, part& second);
58
59 // Destructor
60
61 ~part();
62 43
63 // General accessors 44 // General accessors
64 45
65 bool isValid() const
66 {
67 return (type_ != part_type::invalid);
68 }
69
70 part_type getType() const 46 part_type getType() const
71 { 47 {
72 return type_; 48 return type_;
@@ -74,23 +50,23 @@ namespace verbly {
74 50
75 // Noun phrase accessors 51 // Noun phrase accessors
76 52
77 std::string getNounRole() const; 53 const std::string& getNounRole() const;
78 54
79 std::set<std::string> getNounSelrestrs() const; 55 const std::set<std::string>& getNounSelrestrs() const;
80 56
81 std::set<std::string> getNounSynrestrs() const; 57 const std::set<std::string>& getNounSynrestrs() const;
82 58
83 bool nounHasSynrestr(std::string synrestr) const; 59 bool nounHasSynrestr(std::string synrestr) const;
84 60
85 // Preposition accessors 61 // Preposition accessors
86 62
87 std::vector<std::string> getPrepositionChoices() const; 63 const std::vector<std::string>& getPrepositionChoices() const;
88 64
89 bool isPrepositionLiteral() const; 65 bool isPrepositionLiteral() const;
90 66
91 // Literal accessors 67 // Literal accessors
92 68
93 std::string getLiteralValue() const; 69 const std::string& getLiteralValue() const;
94 70
95 // Type info 71 // Type info
96 72
@@ -123,7 +99,7 @@ namespace verbly {
123 }; 99 };
124 100
125 static const selrestr_field selrestrs; 101 static const selrestr_field selrestrs;
126 102
127 class synrestr_field { 103 class synrestr_field {
128 public: 104 public:
129 105
@@ -139,29 +115,36 @@ namespace verbly {
139 115
140 private: 116 private:
141 117
142 // Private constructors
143
144 part(part_type t) : type_(t)
145 {
146 }
147
148 // Data 118 // Data
149 119
150 union { 120 struct np_type {
151 struct { 121 std::string role;
152 std::string role; 122 std::set<std::string> selrestrs;
153 std::set<std::string> selrestrs; 123 std::set<std::string> synrestrs;
154 std::set<std::string> synrestrs; 124 };
155 } noun_phrase_; 125
156 struct { 126 struct prep_type {
157 std::vector<std::string> choices; 127 std::vector<std::string> choices;
158 bool literal; 128 bool literal;
159 } preposition_;
160 std::string literal_;
161 }; 129 };
162 130
131 using variant_type =
132 mpark::variant<
133 mpark::monostate,
134 np_type,
135 prep_type,
136 std::string>;
137
138 variant_type variant_;
139
163 part_type type_ = part_type::invalid; 140 part_type type_ = part_type::invalid;
164 141
142 // Private constructors
143
144 part(part_type t, variant_type v = {}) : type_(t), variant_(v)
145 {
146 }
147
165 }; 148 };
166 149
167}; 150};
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 @@
1#include "pronunciation.h" 1#include "pronunciation.h"
2#include <sqlite3.h>
3#include <hkutil/string.h> 2#include <hkutil/string.h>
4#include "form.h" 3#include "form.h"
5#include "word.h" 4#include "word.h"
@@ -22,22 +21,27 @@ namespace verbly {
22 const field pronunciation::rhymes_field::rhymeJoin = field::joinField(object::pronunciation, "rhyme", object::pronunciation); 21 const field pronunciation::rhymes_field::rhymeJoin = field::joinField(object::pronunciation, "rhyme", object::pronunciation);
23 const pronunciation::rhymes_field pronunciation::rhymes = {}; 22 const pronunciation::rhymes_field pronunciation::rhymes = {};
24 23
25 pronunciation::pronunciation(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) 24 pronunciation::pronunciation(
25 const database& db,
26 hatkirby::row row) :
27 valid_(true)
26 { 28 {
27 id_ = sqlite3_column_int(row, 0); 29 id_ = mpark::get<int>(row[0]);
28 30
29 std::string phonemesStr(reinterpret_cast<const char*>(sqlite3_column_text(row, 1))); 31 phonemes_ =
30 phonemes_ = hatkirby::split<std::vector<std::string>>(phonemesStr, " "); 32 hatkirby::split<std::vector<std::string>>(
33 mpark::get<std::string>(row[1]),
34 " ");
31 35
32 syllables_ = sqlite3_column_int(row, 2); 36 syllables_ = mpark::get<int>(row[2]);
33 stress_ = std::string(reinterpret_cast<const char*>(sqlite3_column_text(row, 3))); 37 stress_ = mpark::get<std::string>(row[3]);
34 38
35 if (sqlite3_column_type(row, 5) != SQLITE_NULL) 39 if (!mpark::holds_alternative<std::nullptr_t>(row[5]))
36 { 40 {
37 hasRhyme_ = true; 41 hasRhyme_ = true;
38 42
39 prerhyme_ = std::string(reinterpret_cast<const char*>(sqlite3_column_text(row, 4))); 43 prerhyme_ = mpark::get<std::string>(row[4]);
40 rhyme_ = std::string(reinterpret_cast<const char*>(sqlite3_column_text(row, 5))); 44 rhyme_ = mpark::get<std::string>(row[5]);
41 } 45 }
42 } 46 }
43 47
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 @@
4#include <stdexcept> 4#include <stdexcept>
5#include <vector> 5#include <vector>
6#include <string> 6#include <string>
7#include <hkutil/database.h>
7#include "field.h" 8#include "field.h"
8#include "filter.h" 9#include "filter.h"
9 10
10struct sqlite3_stmt;
11
12namespace verbly { 11namespace verbly {
13 12
14 class form; 13 class form;
@@ -24,7 +23,7 @@ namespace verbly {
24 23
25 // Construct from database 24 // Construct from database
26 25
27 pronunciation(const database& db, sqlite3_stmt* row); 26 pronunciation(const database& db, hatkirby::row row);
28 27
29 // Accessors 28 // Accessors
30 29
@@ -63,7 +62,7 @@ namespace verbly {
63 return syllables_; 62 return syllables_;
64 } 63 }
65 64
66 std::string getStress() const 65 const std::string& getStress() const
67 { 66 {
68 if (!valid_) 67 if (!valid_)
69 { 68 {
@@ -83,7 +82,7 @@ namespace verbly {
83 return hasRhyme_; 82 return hasRhyme_;
84 } 83 }
85 84
86 std::string getPrerhyme() const 85 const std::string& getPrerhyme() const
87 { 86 {
88 if (!valid_) 87 if (!valid_)
89 { 88 {
@@ -98,7 +97,7 @@ namespace verbly {
98 return prerhyme_; 97 return prerhyme_;
99 } 98 }
100 99
101 std::string getRhyme() const 100 const std::string& getRhyme() const
102 { 101 {
103 if (!valid_) 102 if (!valid_)
104 { 103 {
@@ -167,8 +166,11 @@ namespace verbly {
167 static const rhymes_field rhymes; 166 static const rhymes_field rhymes;
168 167
169 private: 168 private:
170 bool valid_ = false;
171 169
170 static const field prerhyme;
171 static const field rhyme;
172
173 bool valid_ = false;
172 int id_; 174 int id_;
173 std::vector<std::string> phonemes_; 175 std::vector<std::string> phonemes_;
174 int syllables_; 176 int syllables_;
@@ -176,12 +178,6 @@ namespace verbly {
176 bool hasRhyme_ = false; 178 bool hasRhyme_ = false;
177 std::string prerhyme_; 179 std::string prerhyme_;
178 std::string rhyme_; 180 std::string rhyme_;
179
180 const database* db_;
181
182 static const field prerhyme;
183 static const field rhyme;
184
185 }; 181 };
186 182
187}; 183};
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 @@
5#include <stdexcept> 5#include <stdexcept>
6#include <string> 6#include <string>
7#include <list> 7#include <list>
8#include <sqlite3.h> 8#include <hkutil/database.h>
9#include <iostream>
10#include "statement.h" 9#include "statement.h"
11#include "binding.h"
12#include "order.h" 10#include "order.h"
13 11
14namespace verbly { 12namespace verbly {
@@ -16,7 +14,10 @@ namespace verbly {
16 class database_error : public std::logic_error { 14 class database_error : public std::logic_error {
17 public: 15 public:
18 16
19 database_error(std::string msg, std::string sqlMsg) : std::logic_error(msg + " (" + sqlMsg + ")") 17 database_error(
18 std::string msg,
19 std::string sqlMsg) :
20 std::logic_error(msg + " (" + sqlMsg + ")")
20 { 21 {
21 } 22 }
22 }; 23 };
@@ -25,107 +26,57 @@ namespace verbly {
25 class query { 26 class query {
26 public: 27 public:
27 28
28 query(const database& db, sqlite3* ppdb, filter queryFilter, order sortOrder, int limit) : db_(&db) 29 query(
30 const database& db,
31 hatkirby::database& ppdb,
32 filter queryFilter,
33 order sortOrder,
34 int limit) :
35 db_(db),
36 ppdb_(ppdb)
29 { 37 {
30 if ((sortOrder.getType() == order::type::field) 38 if ((sortOrder.getType() == order::type::field)
31 && (sortOrder.getSortField().getObject() != Object::objectType)) 39 && (sortOrder.getSortField().getObject() != Object::objectType))
32 { 40 {
33 throw std::invalid_argument("Can only sort query by a field in the result table"); 41 throw std::invalid_argument(
42 "Can only sort query by a field in the result table");
34 } 43 }
35 44
36 statement stmt(Object::objectType, std::move(queryFilter)); 45 statement stmt(Object::objectType, std::move(queryFilter));
37 46
38 std::string queryString = stmt.getQueryString(Object::select, std::move(sortOrder), limit); 47 queryString_ =
39 std::list<binding> bindings = stmt.getBindings(); 48 stmt.getQueryString(Object::select, std::move(sortOrder), limit);
40 49
41 if (sqlite3_prepare_v2(ppdb, queryString.c_str(), queryString.length(), &ppstmt_, NULL) != SQLITE_OK) 50 bindings_ = stmt.getBindings();
42 {
43 std::string errorMsg = sqlite3_errmsg(ppdb);
44 sqlite3_finalize(ppstmt_);
45
46 throw database_error("Error preparing query", errorMsg);
47 }
48
49 int i = 1;
50 for (const binding& value : bindings)
51 {
52 switch (value.getType())
53 {
54 case binding::type::integer:
55 {
56 if (sqlite3_bind_int(ppstmt_, i, value.getInteger()) != SQLITE_OK)
57 {
58 std::string errorMsg = sqlite3_errmsg(ppdb);
59 sqlite3_finalize(ppstmt_);
60
61 throw database_error("Error binding value to query", errorMsg);
62 }
63
64 break;
65 }
66
67 case binding::type::string:
68 {
69 if (sqlite3_bind_text(ppstmt_, i, value.getString().c_str(), value.getString().length(), SQLITE_TRANSIENT) != SQLITE_OK)
70 {
71 std::string errorMsg = sqlite3_errmsg(ppdb);
72 sqlite3_finalize(ppstmt_);
73
74 throw database_error("Error binding value to query", errorMsg);
75 }
76
77 break;
78 }
79
80 case binding::type::invalid:
81 {
82 throw std::logic_error("Cannot use invalid bindings");
83 }
84
85 case binding::type::field:
86 {
87 throw std::logic_error("Compare field binding made it past statement generation");
88 }
89 }
90
91 i++;
92 }
93 }
94
95 ~query()
96 {
97 sqlite3_finalize(ppstmt_);
98 } 51 }
99 52
100 std::vector<Object> all() const 53 std::vector<Object> all() const
101 { 54 {
55 std::vector<hatkirby::row> rows =
56 ppdb_.queryAll(queryString_, bindings_);
57
102 std::vector<Object> result; 58 std::vector<Object> result;
103 59
104 while (sqlite3_step(ppstmt_) == SQLITE_ROW) 60 for (hatkirby::row& r : rows)
105 { 61 {
106 result.emplace_back(*db_, ppstmt_); 62 result.emplace_back(db_, std::move(r));
107 } 63 }
108 64
109 sqlite3_reset(ppstmt_);
110
111 return result; 65 return result;
112 } 66 }
113 67
114 Object first() const 68 Object first() const
115 { 69 {
116 std::vector<Object> results = all(); 70 return { db_, ppdb_.queryFirst(queryString_, bindings_) };
117 if (!results.empty())
118 {
119 return results.front();
120 } else {
121 throw std::logic_error("query returned empty dataset");
122 }
123 } 71 }
124 72
125 private: 73 private:
126 const database* db_;
127 sqlite3_stmt* ppstmt_;
128 74
75 const database& db_;
76 hatkirby::database& ppdb_;
77
78 std::string queryString_;
79 std::list<hatkirby::binding> bindings_;
129 }; 80 };
130 81
131}; 82};
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 {
133 return queryStream.str(); 133 return queryStream.str();
134 } 134 }
135 135
136 std::list<binding> statement::getBindings() const 136 std::list<hatkirby::binding> statement::getBindings() const
137 { 137 {
138 std::list<binding> result; 138 std::list<hatkirby::binding> result;
139 139
140 for (const with& w : withs_) 140 for (const with& w : withs_)
141 { 141 {
142 for (binding value : w.getCondition().flattenBindings()) 142 for (hatkirby::binding value : w.getCondition().flattenBindings())
143 { 143 {
144 result.push_back(std::move(value)); 144 result.push_back(std::move(value));
145 } 145 }
146 } 146 }
147 147
148 for (binding value : topCondition_.flattenBindings()) 148 for (hatkirby::binding value : topCondition_.flattenBindings())
149 { 149 {
150 result.push_back(std::move(value)); 150 result.push_back(std::move(value));
151 } 151 }
@@ -203,77 +203,152 @@ namespace verbly {
203 { 203 {
204 case filter::comparison::is_null: 204 case filter::comparison::is_null:
205 { 205 {
206 return condition(topTable_, clause.getField().getColumn(), true); 206 return {
207 topTable_,
208 clause.getField().getColumn(),
209 true
210 };
207 } 211 }
208 212
209 case filter::comparison::is_not_null: 213 case filter::comparison::is_not_null:
210 { 214 {
211 return condition(topTable_, clause.getField().getColumn(), false); 215 return {
216 topTable_,
217 clause.getField().getColumn(),
218 false
219 };
212 } 220 }
213 221
214 case filter::comparison::int_equals: 222 case filter::comparison::int_equals:
215 { 223 {
216 return condition(topTable_, clause.getField().getColumn(), condition::comparison::equals, clause.getIntegerArgument()); 224 return {
225 topTable_,
226 clause.getField().getColumn(),
227 condition::comparison::equals,
228 clause.getIntegerArgument()
229 };
217 } 230 }
218 231
219 case filter::comparison::int_does_not_equal: 232 case filter::comparison::int_does_not_equal:
220 { 233 {
221 return condition(topTable_, clause.getField().getColumn(), condition::comparison::does_not_equal, clause.getIntegerArgument()); 234 return {
235 topTable_,
236 clause.getField().getColumn(),
237 condition::comparison::does_not_equal,
238 clause.getIntegerArgument()
239 };
222 } 240 }
223 241
224 case filter::comparison::int_is_at_least: 242 case filter::comparison::int_is_at_least:
225 { 243 {
226 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_at_least, clause.getIntegerArgument()); 244 return {
245 topTable_,
246 clause.getField().getColumn(),
247 condition::comparison::is_at_least,
248 clause.getIntegerArgument()
249 };
227 } 250 }
228 251
229 case filter::comparison::int_is_greater_than: 252 case filter::comparison::int_is_greater_than:
230 { 253 {
231 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_greater_than, clause.getIntegerArgument()); 254 return {
255 topTable_,
256 clause.getField().getColumn(),
257 condition::comparison::is_greater_than,
258 clause.getIntegerArgument()
259 };
232 } 260 }
233 261
234 case filter::comparison::int_is_at_most: 262 case filter::comparison::int_is_at_most:
235 { 263 {
236 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_at_most, clause.getIntegerArgument()); 264 return {
265 topTable_,
266 clause.getField().getColumn(),
267 condition::comparison::is_at_most,
268 clause.getIntegerArgument()
269 };
237 } 270 }
238 271
239 case filter::comparison::int_is_less_than: 272 case filter::comparison::int_is_less_than:
240 { 273 {
241 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_less_than, clause.getIntegerArgument()); 274 return {
275 topTable_,
276 clause.getField().getColumn(),
277 condition::comparison::is_less_than,
278 clause.getIntegerArgument()
279 };
242 } 280 }
243 281
244 case filter::comparison::boolean_equals: 282 case filter::comparison::boolean_equals:
245 { 283 {
246 return condition(topTable_, clause.getField().getColumn(), condition::comparison::equals, clause.getBooleanArgument() ? 1 : 0); 284 return {
285 topTable_,
286 clause.getField().getColumn(),
287 condition::comparison::equals,
288 clause.getBooleanArgument() ? 1 : 0
289 };
247 } 290 }
248 291
249 case filter::comparison::string_equals: 292 case filter::comparison::string_equals:
250 { 293 {
251 return condition(topTable_, clause.getField().getColumn(), condition::comparison::equals, clause.getStringArgument()); 294 return {
295 topTable_,
296 clause.getField().getColumn(),
297 condition::comparison::equals,
298 clause.getStringArgument()
299 };
252 } 300 }
253 301
254 case filter::comparison::string_does_not_equal: 302 case filter::comparison::string_does_not_equal:
255 { 303 {
256 return condition(topTable_, clause.getField().getColumn(), condition::comparison::does_not_equal, clause.getStringArgument()); 304 return {
305 topTable_,
306 clause.getField().getColumn(),
307 condition::comparison::does_not_equal,
308 clause.getStringArgument()
309 };
257 } 310 }
258 311
259 case filter::comparison::string_is_like: 312 case filter::comparison::string_is_like:
260 { 313 {
261 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_like, clause.getStringArgument()); 314 return {
315 topTable_,
316 clause.getField().getColumn(),
317 condition::comparison::is_like,
318 clause.getStringArgument()
319 };
262 } 320 }
263 321
264 case filter::comparison::string_is_not_like: 322 case filter::comparison::string_is_not_like:
265 { 323 {
266 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_not_like, clause.getStringArgument()); 324 return {
325 topTable_,
326 clause.getField().getColumn(),
327 condition::comparison::is_not_like,
328 clause.getStringArgument()
329 };
267 } 330 }
268 331
269 case filter::comparison::field_equals: 332 case filter::comparison::field_equals:
270 { 333 {
271 return condition(topTable_, clause.getField().getColumn(), condition::comparison::equals, {"", clause.getCompareField().getColumn()}, clause.getCompareField().getObject()); 334 return {
335 topTable_,
336 clause.getField().getColumn(),
337 condition::comparison::equals,
338 field_binding {"", clause.getCompareField().getColumn()},
339 clause.getCompareField().getObject()
340 };
272 } 341 }
273 342
274 case filter::comparison::field_does_not_equal: 343 case filter::comparison::field_does_not_equal:
275 { 344 {
276 return condition(topTable_, clause.getField().getColumn(), condition::comparison::does_not_equal, {"", clause.getCompareField().getColumn()}, clause.getCompareField().getObject()); 345 return {
346 topTable_,
347 clause.getField().getColumn(),
348 condition::comparison::does_not_equal,
349 field_binding {"", clause.getCompareField().getColumn()},
350 clause.getCompareField().getObject()
351 };
277 } 352 }
278 353
279 case filter::comparison::matches: 354 case filter::comparison::matches:
@@ -680,206 +755,19 @@ namespace verbly {
680 << j.getJoinColumn(); 755 << j.getJoinColumn();
681 } 756 }
682 757
683 statement::condition::condition(const condition& other)
684 {
685 type_ = other.type_;
686
687 switch (type_)
688 {
689 case type::empty:
690 {
691 break;
692 }
693
694 case type::singleton:
695 {
696 new(&singleton_.table_) std::string(other.singleton_.table_);
697 new(&singleton_.column_) std::string(other.singleton_.column_);
698 singleton_.comparison_ = other.singleton_.comparison_;
699 new(&singleton_.value_) binding(other.singleton_.value_);
700 singleton_.parentObject_ = other.singleton_.parentObject_;
701
702 break;
703 }
704
705 case type::group:
706 {
707 new(&group_.children_) std::list<condition>(other.group_.children_);
708 group_.orlogic_ = other.group_.orlogic_;
709
710 break;
711 }
712 }
713 }
714
715 statement::condition::condition(condition&& other) : condition()
716 {
717 swap(*this, other);
718 }
719
720 statement::condition& statement::condition::operator=(condition other)
721 {
722 swap(*this, other);
723
724 return *this;
725 }
726
727 void swap(statement::condition& first, statement::condition& second)
728 {
729 using type = statement::condition::type;
730 using condition = statement::condition;
731
732 type tempType = first.type_;
733 std::string tempTable;
734 std::string tempColumn;
735 condition::comparison tempComparison;
736 binding tempBinding;
737 object tempParentObject;
738 std::list<condition> tempChildren;
739 bool tempOrlogic;
740
741 switch (tempType)
742 {
743 case type::empty:
744 {
745 break;
746 }
747
748 case type::singleton:
749 {
750 tempTable = std::move(first.singleton_.table_);
751 tempColumn = std::move(first.singleton_.column_);
752 tempComparison = first.singleton_.comparison_;
753 tempBinding = std::move(first.singleton_.value_);
754 tempParentObject = first.singleton_.parentObject_;
755
756 break;
757 }
758
759 case type::group:
760 {
761 tempChildren = std::move(first.group_.children_);
762 tempOrlogic = first.group_.orlogic_;
763
764 break;
765 }
766 }
767
768 first.~condition();
769
770 first.type_ = second.type_;
771
772 switch (first.type_)
773 {
774 case type::empty:
775 {
776 break;
777 }
778
779 case type::singleton:
780 {
781 new(&first.singleton_.table_) std::string(std::move(second.singleton_.table_));
782 new(&first.singleton_.column_) std::string(std::move(second.singleton_.column_));
783 first.singleton_.comparison_ = second.singleton_.comparison_;
784 new(&first.singleton_.value_) binding(std::move(second.singleton_.value_));
785 first.singleton_.parentObject_ = second.singleton_.parentObject_;
786
787 break;
788 }
789
790 case type::group:
791 {
792 new(&first.group_.children_) std::list<condition>(std::move(second.group_.children_));
793 first.group_.orlogic_ = second.group_.orlogic_;
794
795 break;
796 }
797 }
798
799 second.~condition();
800
801 second.type_ = tempType;
802
803 switch (second.type_)
804 {
805 case type::empty:
806 {
807 break;
808 }
809
810 case type::singleton:
811 {
812 new(&second.singleton_.table_) std::string(std::move(tempTable));
813 new(&second.singleton_.column_) std::string(std::move(tempColumn));
814 second.singleton_.comparison_ = tempComparison;
815 new(&second.singleton_.value_) binding(std::move(tempBinding));
816 second.singleton_.parentObject_ = tempParentObject;
817
818 break;
819 }
820
821 case type::group:
822 {
823 new(&second.group_.children_) std::list<condition>(std::move(tempChildren));
824 second.group_.orlogic_ = tempOrlogic;
825
826 break;
827 }
828 }
829 }
830
831 statement::condition::~condition()
832 {
833 switch (type_)
834 {
835 case type::empty:
836 {
837 break;
838 }
839
840 case type::singleton:
841 {
842 using string_type = std::string;
843
844 singleton_.table_.~string_type();
845 singleton_.column_.~string_type();
846 singleton_.value_.~binding();
847
848 break;
849 }
850
851 case type::group:
852 {
853 using list_type = std::list<condition>;
854
855 group_.children_.~list_type();
856
857 break;
858 }
859 }
860 }
861
862 statement::condition::condition() : type_(type::empty)
863 {
864 }
865
866 statement::condition::condition( 758 statement::condition::condition(
867 std::string table, 759 std::string table,
868 std::string column, 760 std::string column,
869 bool isNull) : 761 bool isNull) :
870 type_(type::singleton) 762 type_(type::singleton),
763 variant_(singleton_type {
764 std::move(table),
765 std::move(column),
766 isNull ? comparison::is_null : comparison::is_not_null,
767 {},
768 object::undefined
769 })
871 { 770 {
872 new(&singleton_.table_) std::string(std::move(table));
873 new(&singleton_.column_) std::string(std::move(column));
874
875 if (isNull)
876 {
877 singleton_.comparison_ = comparison::is_null;
878 } else {
879 singleton_.comparison_ = comparison::is_not_null;
880 }
881
882 singleton_.parentObject_ = object::undefined;
883 } 771 }
884 772
885 statement::condition::condition( 773 statement::condition::condition(
@@ -888,201 +776,210 @@ namespace verbly {
888 comparison comp, 776 comparison comp,
889 binding value, 777 binding value,
890 object parentObject) : 778 object parentObject) :
891 type_(type::singleton) 779 type_(type::singleton),
780 variant_(singleton_type {
781 std::move(table),
782 std::move(column),
783 comp,
784 std::move(value),
785 parentObject
786 })
892 { 787 {
893 new(&singleton_.table_) std::string(std::move(table));
894 new(&singleton_.column_) std::string(std::move(column));
895 singleton_.comparison_ = comp;
896 new(&singleton_.value_) binding(std::move(value));
897 singleton_.parentObject_ = parentObject;
898 } 788 }
899 789
900 std::string statement::condition::toSql(bool toplevel, bool debug) const 790 std::string statement::condition::toSql(bool toplevel, bool debug) const
901 { 791 {
792 std::ostringstream sql;
793
902 switch (type_) 794 switch (type_)
903 { 795 {
904 case type::empty: 796 case type::empty:
905 { 797 {
906 return ""; 798 break;
907 } 799 }
908 800
909 case type::singleton: 801 case type::singleton:
910 { 802 {
911 switch (singleton_.comparison_) 803 const singleton_type& singleton = mpark::get<singleton_type>(variant_);
804
805 sql << singleton.table << "." << singleton.column;
806
807 switch (singleton.comparison)
912 { 808 {
913 case comparison::equals: 809 case comparison::equals:
810 case comparison::does_not_equal:
914 { 811 {
915 if (debug) 812 if (singleton.comparison == comparison::equals)
916 { 813 {
917 switch (singleton_.value_.getType()) 814 sql << " = ";
918 {
919 case binding::type::string:
920 {
921 return singleton_.table_ + "." + singleton_.column_ + " = \"" + singleton_.value_.getString() + "\"";
922 }
923
924 case binding::type::integer:
925 {
926 return singleton_.table_ + "." + singleton_.column_ + " = " + std::to_string(singleton_.value_.getInteger());
927 }
928
929 case binding::type::field:
930 {
931 return singleton_.table_ + "." + singleton_.column_ + " = " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn();
932 }
933
934 case binding::type::invalid:
935 {
936 throw std::logic_error("Invalid binding in statement generation");
937 }
938 }
939 } else { 815 } else {
940 if (singleton_.value_.getType() == binding::type::field) 816 sql << " != ";
941 {
942 return singleton_.table_ + "." + singleton_.column_ + " = " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn();
943 } else {
944 return singleton_.table_ + "." + singleton_.column_ + " = ?";
945 }
946 } 817 }
947 }
948 818
949 case comparison::does_not_equal: 819 if (mpark::holds_alternative<field_binding>(singleton.value))
950 { 820 {
951 if (debug) 821 sql << std::get<0>(mpark::get<field_binding>(singleton.value))
822 << "."
823 << std::get<1>(mpark::get<field_binding>(singleton.value));
824 } else if (debug)
952 { 825 {
953 switch (singleton_.value_.getType()) 826 if (mpark::holds_alternative<std::string>(singleton.value))
954 { 827 {
955 case binding::type::string: 828 sql << "\"" << mpark::get<std::string>(singleton.value) << "\"";
956 {
957 return singleton_.table_ + "." + singleton_.column_ + " != \"" + singleton_.value_.getString() + "\"";
958 }
959
960 case binding::type::integer:
961 {
962 return singleton_.table_ + "." + singleton_.column_ + " != " + std::to_string(singleton_.value_.getInteger());
963 }
964
965 case binding::type::field:
966 {
967 return singleton_.table_ + "." + singleton_.column_ + " != " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn();
968 }
969
970 case binding::type::invalid:
971 {
972 throw std::logic_error("Invalid binding in statement generation");
973 }
974 } 829 }
975 } else { 830 else if (mpark::holds_alternative<int>(singleton.value))
976 if (singleton_.value_.getType() == binding::type::field)
977 { 831 {
978 return singleton_.table_ + "." + singleton_.column_ + " != " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn(); 832 sql << mpark::get<int>(singleton.value);
979 } else {
980 return singleton_.table_ + "." + singleton_.column_ + " != ?";
981 } 833 }
834 } else {
835 sql << "?";
982 } 836 }
837
838 break;
983 } 839 }
984 840
985 case comparison::is_greater_than: 841 case comparison::is_greater_than:
986 { 842 {
843 sql << " > ";
844
987 if (debug) 845 if (debug)
988 { 846 {
989 return singleton_.table_ + "." + singleton_.column_ + " > " + std::to_string(singleton_.value_.getInteger()); 847 sql << mpark::get<int>(singleton.value);
990 } else { 848 } else {
991 return singleton_.table_ + "." + singleton_.column_ + " > ?"; 849 sql << "?";
992 } 850 }
851
852 break;
993 } 853 }
994 854
995 case comparison::is_at_most: 855 case comparison::is_at_most:
996 { 856 {
857 sql << " <= ";
858
997 if (debug) 859 if (debug)
998 { 860 {
999 return singleton_.table_ + "." + singleton_.column_ + " <= " + std::to_string(singleton_.value_.getInteger()); 861 sql << mpark::get<int>(singleton.value);
1000 } else { 862 } else {
1001 return singleton_.table_ + "." + singleton_.column_ + " <= ?"; 863 sql << "?";
1002 } 864 }
865
866 break;
1003 } 867 }
1004 868
1005 case comparison::is_less_than: 869 case comparison::is_less_than:
1006 { 870 {
871 sql << " < ";
872
1007 if (debug) 873 if (debug)
1008 { 874 {
1009 return singleton_.table_ + "." + singleton_.column_ + " < " + std::to_string(singleton_.value_.getInteger()); 875 sql << mpark::get<int>(singleton.value);
1010 } else { 876 } else {
1011 return singleton_.table_ + "." + singleton_.column_ + " < ?"; 877 sql << "?";
1012 } 878 }
879
880 break;
1013 } 881 }
1014 882
1015 case comparison::is_at_least: 883 case comparison::is_at_least:
1016 { 884 {
885 sql << " >= ";
886
1017 if (debug) 887 if (debug)
1018 { 888 {
1019 return singleton_.table_ + "." + singleton_.column_ + " >= " + std::to_string(singleton_.value_.getInteger()); 889 sql << mpark::get<int>(singleton.value);
1020 } else { 890 } else {
1021 return singleton_.table_ + "." + singleton_.column_ + " >= ?"; 891 sql << "?";
1022 } 892 }
893
894 break;
1023 } 895 }
1024 896
1025 case comparison::is_like: 897 case comparison::is_like:
1026 { 898 {
899 sql << " LIKE ";
900
1027 if (debug) 901 if (debug)
1028 { 902 {
1029 return singleton_.table_ + "." + singleton_.column_ + " LIKE \"" + singleton_.value_.getString() + "\""; 903 sql << "\"" << mpark::get<std::string>(singleton.value) << "\"";
1030 } else { 904 } else {
1031 return singleton_.table_ + "." + singleton_.column_ + " LIKE ?"; 905 sql << "?";
1032 } 906 }
907
908 break;
1033 } 909 }
1034 910
1035 case comparison::is_not_like: 911 case comparison::is_not_like:
1036 { 912 {
913 sql << " NOT LIKE ";
914
1037 if (debug) 915 if (debug)
1038 { 916 {
1039 return singleton_.table_ + "." + singleton_.column_ + " NOT LIKE \"" + singleton_.value_.getString() + "\""; 917 sql << "\"" << mpark::get<std::string>(singleton.value) << "\"";
1040 } else { 918 } else {
1041 return singleton_.table_ + "." + singleton_.column_ + " NOT LIKE ?"; 919 sql << "?";
1042 } 920 }
921
922 break;
1043 } 923 }
1044 924
1045 case comparison::is_not_null: 925 case comparison::is_not_null:
1046 { 926 {
1047 return singleton_.table_ + "." + singleton_.column_ + " IS NOT NULL"; 927 sql << " IS NOT NULL";
928
929 break;
1048 } 930 }
1049 931
1050 case comparison::is_null: 932 case comparison::is_null:
1051 { 933 {
1052 return singleton_.table_ + "." + singleton_.column_ + " IS NULL"; 934 sql << " IS NULL";
935
936 break;
1053 } 937 }
1054 } 938 }
939
940 break;
1055 } 941 }
1056 942
1057 case type::group: 943 case type::group:
1058 { 944 {
945 const group_type& group = mpark::get<group_type>(variant_);
946
1059 std::list<std::string> clauses; 947 std::list<std::string> clauses;
1060 for (const condition& cond : group_.children_) 948 for (const condition& cond : group.children)
1061 { 949 {
1062 clauses.push_back(cond.toSql(false, debug)); 950 clauses.push_back(cond.toSql(false, debug));
1063 } 951 }
1064 952
1065 if (clauses.empty()) 953 if (clauses.size() == 1)
1066 { 954 {
1067 return ""; 955 sql << clauses.front();
1068 } else if (clauses.size() == 1) 956 } else if (!clauses.empty())
1069 { 957 {
1070 return clauses.front(); 958 if (!toplevel)
1071 } else { 959 {
1072 std::string result = hatkirby::implode(std::begin(clauses), std::end(clauses), group_.orlogic_ ? " OR " : " AND "); 960 sql << "(";
961 }
962
963 sql <<
964 hatkirby::implode(
965 std::begin(clauses),
966 std::end(clauses),
967 group.orlogic ? " OR " : " AND ");
1073 968
1074 if (toplevel) 969 if (!toplevel)
1075 { 970 {
1076 return result; 971 sql << ")";
1077 } else {
1078 return "(" + result + ")";
1079 } 972 }
1080 } 973 }
974
975 break;
1081 } 976 }
1082 } 977 }
978
979 return sql.str();
1083 } 980 }
1084 981
1085 std::list<binding> statement::condition::flattenBindings() const 982 std::list<hatkirby::binding> statement::condition::flattenBindings() const
1086 { 983 {
1087 switch (type_) 984 switch (type_)
1088 { 985 {
@@ -1093,39 +990,27 @@ namespace verbly {
1093 990
1094 case type::singleton: 991 case type::singleton:
1095 { 992 {
1096 if (singleton_.value_.getType() == binding::type::field) 993 const singleton_type& singleton = mpark::get<singleton_type>(variant_);
994
995 if (mpark::holds_alternative<std::string>(singleton.value))
1097 { 996 {
1098 return {}; 997 return {{ mpark::get<std::string>(singleton.value) }};
998 } else if (mpark::holds_alternative<int>(singleton.value))
999 {
1000 return {{ mpark::get<int>(singleton.value) }};
1099 } else { 1001 } else {
1100 switch (singleton_.comparison_) 1002 return {};
1101 {
1102 case comparison::equals:
1103 case comparison::does_not_equal:
1104 case comparison::is_greater_than:
1105 case comparison::is_at_most:
1106 case comparison::is_less_than:
1107 case comparison::is_at_least:
1108 case comparison::is_like:
1109 case comparison::is_not_like:
1110 {
1111 return {singleton_.value_};
1112 }
1113
1114 case comparison::is_not_null:
1115 case comparison::is_null:
1116 {
1117 return {};
1118 }
1119 }
1120 } 1003 }
1121 } 1004 }
1122 1005
1123 case type::group: 1006 case type::group:
1124 { 1007 {
1125 std::list<binding> bindings; 1008 const group_type& group = mpark::get<group_type>(variant_);
1126 for (const condition& cond : group_.children_) 1009
1010 std::list<hatkirby::binding> bindings;
1011 for (const condition& cond : group.children)
1127 { 1012 {
1128 for (binding value : cond.flattenBindings()) 1013 for (hatkirby::binding value : cond.flattenBindings())
1129 { 1014 {
1130 bindings.push_back(std::move(value)); 1015 bindings.push_back(std::move(value));
1131 } 1016 }
@@ -1136,22 +1021,24 @@ namespace verbly {
1136 } 1021 }
1137 } 1022 }
1138 1023
1139 statement::condition::condition(bool orlogic) : type_(type::group) 1024 statement::condition::condition(
1025 bool orlogic) :
1026 type_(type::group),
1027 variant_(group_type { {}, orlogic })
1140 { 1028 {
1141 new(&group_.children_) std::list<condition>();
1142 group_.orlogic_ = orlogic;
1143 } 1029 }
1144 1030
1145 statement::condition& statement::condition::operator+=(condition n) 1031 statement::condition& statement::condition::operator+=(condition n)
1146 { 1032 {
1147 if (type_ == type::group) 1033 if (type_ != type::group)
1148 { 1034 {
1149 group_.children_.push_back(std::move(n));
1150
1151 return *this;
1152 } else {
1153 throw std::domain_error("Cannot add condition to non-group condition"); 1035 throw std::domain_error("Cannot add condition to non-group condition");
1154 } 1036 }
1037
1038 group_type& group = mpark::get<group_type>(variant_);
1039 group.children.emplace_back(std::move(n));
1040
1041 return *this;
1155 } 1042 }
1156 1043
1157 statement::condition& statement::condition::operator&=(condition n) 1044 statement::condition& statement::condition::operator&=(condition n)
@@ -1187,14 +1074,17 @@ namespace verbly {
1187 return *this; 1074 return *this;
1188 } 1075 }
1189 1076
1190 const std::list<statement::condition>& statement::condition::getChildren() const 1077 const std::list<statement::condition>& statement::condition::getChildren()
1078 const
1191 { 1079 {
1192 if (type_ == type::group) 1080 if (type_ != type::group)
1193 { 1081 {
1194 return group_.children_;
1195 } else {
1196 throw std::domain_error("Cannot get children of non-group condition"); 1082 throw std::domain_error("Cannot get children of non-group condition");
1197 } 1083 }
1084
1085 const group_type& group = mpark::get<group_type>(variant_);
1086
1087 return group.children;
1198 } 1088 }
1199 1089
1200 statement::condition statement::condition::flatten() const 1090 statement::condition statement::condition::flatten() const
@@ -1209,17 +1099,27 @@ namespace verbly {
1209 1099
1210 case type::group: 1100 case type::group:
1211 { 1101 {
1212 condition result(group_.orlogic_); 1102 const group_type& group = mpark::get<group_type>(variant_);
1213 1103
1214 for (const condition& child : group_.children_) 1104 condition result(group.orlogic);
1105
1106 for (const condition& child : group.children)
1215 { 1107 {
1216 condition newChild = child.flatten(); 1108 condition newChild = child.flatten();
1217 1109
1218 if ((newChild.type_ == type::group) && (newChild.group_.orlogic_ == group_.orlogic_)) 1110 if (newChild.type_ == type::group)
1219 { 1111 {
1220 for (condition subChild : std::move(newChild.group_.children_)) 1112 group_type& childGroup =
1113 mpark::get<group_type>(newChild.variant_);
1114
1115 if (childGroup.orlogic == group.orlogic)
1221 { 1116 {
1222 result += std::move(subChild); 1117 for (condition subChild : std::move(childGroup.children))
1118 {
1119 result += std::move(subChild);
1120 }
1121 } else {
1122 result += std::move(newChild);
1223 } 1123 }
1224 } else { 1124 } else {
1225 result += std::move(newChild); 1125 result += std::move(newChild);
@@ -1231,7 +1131,9 @@ namespace verbly {
1231 } 1131 }
1232 } 1132 }
1233 1133
1234 statement::condition statement::condition::resolveCompareFields(object context, std::string tableName) const 1134 statement::condition statement::condition::resolveCompareFields(
1135 object context,
1136 std::string tableName) const
1235 { 1137 {
1236 switch (type_) 1138 switch (type_)
1237 { 1139 {
@@ -1242,9 +1144,20 @@ namespace verbly {
1242 1144
1243 case type::singleton: 1145 case type::singleton:
1244 { 1146 {
1245 if ((singleton_.parentObject_ != object::undefined) && (singleton_.parentObject_ == context)) 1147 const singleton_type& singleton = mpark::get<singleton_type>(variant_);
1148
1149 if (singleton.parentObject != object::undefined &&
1150 singleton.parentObject == context)
1246 { 1151 {
1247 return condition(singleton_.table_, singleton_.column_, singleton_.comparison_, {tableName, singleton_.value_.getColumn()}); 1152 return {
1153 singleton.table,
1154 singleton.column,
1155 singleton.comparison,
1156 field_binding {
1157 tableName,
1158 std::get<1>(mpark::get<field_binding>(singleton.value))
1159 }
1160 };
1248 } else { 1161 } else {
1249 return *this; 1162 return *this;
1250 } 1163 }
@@ -1252,8 +1165,10 @@ namespace verbly {
1252 1165
1253 case type::group: 1166 case type::group:
1254 { 1167 {
1255 condition result(group_.orlogic_); 1168 const group_type& group = mpark::get<group_type>(variant_);
1256 for (const condition& cond : group_.children_) 1169
1170 condition result(group.orlogic);
1171 for (const condition& cond : group.children)
1257 { 1172 {
1258 result += cond.resolveCompareFields(context, tableName); 1173 result += cond.resolveCompareFields(context, tableName);
1259 } 1174 }
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 @@
4#include <string> 4#include <string>
5#include <list> 5#include <list>
6#include <map> 6#include <map>
7#include <set> 7#include <hkutil/database.h>
8#include "binding.h" 8#include <variant.hpp>
9#include "enums.h" 9#include "enums.h"
10#include "field.h" 10#include "field.h"
11#include "filter.h" 11#include "filter.h"
@@ -15,14 +15,28 @@ namespace verbly {
15 class filter; 15 class filter;
16 class order; 16 class order;
17 17
18 using field_binding =
19 std::tuple<std::string, std::string>;
20
21 using binding =
22 mpark::variant<
23 mpark::monostate,
24 std::string,
25 int,
26 field_binding>;
27
18 class statement { 28 class statement {
19 public: 29 public:
20 30
21 statement(object context, filter queryFilter); 31 statement(object context, filter queryFilter);
22 32
23 std::string getQueryString(std::list<std::string> select, order sortOrder, int limit, bool debug = false) const; 33 std::string getQueryString(
34 std::list<std::string> select,
35 order sortOrder,
36 int limit,
37 bool debug = false) const;
24 38
25 std::list<binding> getBindings() const; 39 std::list<hatkirby::binding> getBindings() const;
26 40
27 private: 41 private:
28 42
@@ -108,23 +122,6 @@ namespace verbly {
108 is_null 122 is_null
109 }; 123 };
110 124
111 // Copy and move constructors
112
113 condition(const condition& other);
114 condition(condition&& other);
115
116 // Assignment
117
118 condition& operator=(condition other);
119
120 // Swap
121
122 friend void swap(condition& first, condition& second);
123
124 // Destructor
125
126 ~condition();
127
128 // Accessors 125 // Accessors
129 126
130 type getType() const 127 type getType() const
@@ -134,13 +131,21 @@ namespace verbly {
134 131
135 // Empty 132 // Empty
136 133
137 condition(); 134 condition() = default;
138 135
139 // Singleton 136 // Singleton
140 137
141 condition(std::string table, std::string column, bool isNull); 138 condition(
139 std::string table,
140 std::string column,
141 bool isNull);
142 142
143 condition(std::string table, std::string column, comparison comp, binding value, object parentObject = object::undefined); 143 condition(
144 std::string table,
145 std::string column,
146 comparison comp,
147 binding value,
148 object parentObject = object::undefined);
144 149
145 // Group 150 // Group
146 151
@@ -156,30 +161,39 @@ namespace verbly {
156 161
157 std::string toSql(bool toplevel, bool debug = false) const; 162 std::string toSql(bool toplevel, bool debug = false) const;
158 163
159 std::list<binding> flattenBindings() const; 164 std::list<hatkirby::binding> flattenBindings() const;
160 165
161 condition flatten() const; 166 condition flatten() const;
162 167
163 condition resolveCompareFields(object context, std::string tableName) const; 168 condition resolveCompareFields(
169 object context,
170 std::string tableName) const;
164 171
165 private: 172 private:
166 union { 173
167 struct { 174 struct singleton_type {
168 std::string table_; 175 std::string table;
169 std::string column_; 176 std::string column;
170 comparison comparison_; 177 comparison comparison;
171 binding value_; 178 binding value;
172 object parentObject_; 179 object parentObject;
173 } singleton_;
174 struct {
175 std::list<condition> children_;
176 bool orlogic_;
177 } group_;
178 }; 180 };
179 type type_;
180 };
181 181
182 friend void swap(condition& first, condition& second); 182 struct group_type {
183 std::list<condition> children;
184 bool orlogic;
185 };
186
187 using variant_type =
188 mpark::variant<
189 mpark::monostate,
190 singleton_type,
191 group_type>;
192
193 variant_type variant_;
194
195 type type_ = type::empty;
196 };
183 197
184 class with { 198 class with {
185 public: 199 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 @@
5 5
6namespace verbly { 6namespace verbly {
7 7
8 token::token(const token& other) 8 bool token::isComplete() const
9 {
10 type_ = other.type_;
11
12 switch (type_)
13 {
14 case type::word:
15 {
16 new(&word_.word_) word(other.word_.word_);
17 word_.category_ = other.word_.category_;
18
19 break;
20 }
21
22 case type::literal:
23 {
24 new(&literal_) std::string(other.literal_);
25
26 break;
27 }
28
29 case type::part:
30 {
31 new(&part_) part(other.part_);
32
33 break;
34 }
35
36 case type::fillin:
37 {
38 new(&fillin_) std::set<std::string>(other.fillin_);
39
40 break;
41 }
42
43 case type::utterance:
44 {
45 new(&utterance_) std::list<token>(other.utterance_);
46
47 break;
48 }
49
50 case type::transform:
51 {
52 transform_.type_ = other.transform_.type_;
53 new(&transform_.strParam_) std::string(other.transform_.strParam_);
54 new(&transform_.strParam2_) std::string(other.transform_.strParam2_);
55 transform_.casingParam_ = other.transform_.casingParam_;
56 new(&transform_.inner_) std::unique_ptr<token>(new token(*other.transform_.inner_));
57
58 break;
59 }
60 }
61 }
62
63 token::token(token&& other) : token()
64 {
65 swap(*this, other);
66 }
67
68 token& token::operator=(token other)
69 {
70 swap(*this, other);
71
72 return *this;
73 }
74
75 void swap(token& first, token& second)
76 {
77 using type = token::type;
78 using transform_type = token::transform_type;
79 using casing = token::casing;
80
81 type tempType = first.type_;
82 word tempWord;
83 inflection tempCategory;
84 std::string tempLiteral;
85 part tempPart;
86 std::set<std::string> tempFillin;
87 std::list<token> tempUtterance;
88 transform_type tempTransformType;
89 std::string tempTransformStrParam;
90 std::string tempTransformStrParam2;
91 casing tempTransformCasingParam;
92 std::unique_ptr<token> tempTransformInner;
93
94 switch (tempType)
95 {
96 case type::word:
97 {
98 tempWord = std::move(first.word_.word_);
99 tempCategory = first.word_.category_;
100
101 break;
102 }
103
104 case type::literal:
105 {
106 tempLiteral = std::move(first.literal_);
107
108 break;
109 }
110
111 case type::part:
112 {
113 tempPart = std::move(first.part_);
114
115 break;
116 }
117
118 case type::fillin:
119 {
120 tempFillin = std::move(first.fillin_);
121
122 break;
123 }
124
125 case type::utterance:
126 {
127 tempUtterance = std::move(first.utterance_);
128
129 break;
130 }
131
132 case type::transform:
133 {
134 tempTransformType = first.transform_.type_;
135 tempTransformStrParam = std::move(first.transform_.strParam_);
136 tempTransformStrParam2 = std::move(first.transform_.strParam2_);
137 tempTransformCasingParam = first.transform_.casingParam_;
138 tempTransformInner = std::move(first.transform_.inner_);
139
140 break;
141 }
142 }
143
144 first.~token();
145
146 first.type_ = second.type_;
147
148 switch (first.type_)
149 {
150 case type::word:
151 {
152 new(&first.word_.word_) word(std::move(second.word_.word_));
153 first.word_.category_ = second.word_.category_;
154
155 break;
156 }
157
158 case type::literal:
159 {
160 new(&first.literal_) std::string(std::move(second.literal_));
161
162 break;
163 }
164
165 case type::part:
166 {
167 new(&first.part_) part(std::move(second.part_));
168
169 break;
170 }
171
172 case type::fillin:
173 {
174 new(&first.fillin_) std::set<std::string>(std::move(second.fillin_));
175
176 break;
177 }
178
179 case type::utterance:
180 {
181 new(&first.utterance_) std::list<token>(std::move(second.utterance_));
182
183 break;
184 }
185
186 case type::transform:
187 {
188 first.transform_.type_ = second.transform_.type_;
189 new(&first.transform_.strParam_) std::string(std::move(second.transform_.strParam_));
190 new(&first.transform_.strParam2_) std::string(std::move(second.transform_.strParam2_));
191 first.transform_.casingParam_ = second.transform_.casingParam_;
192 new(&first.transform_.inner_) std::unique_ptr<token>(std::move(second.transform_.inner_));
193
194 break;
195 }
196 }
197
198 second.~token();
199
200 second.type_ = tempType;
201
202 switch (second.type_)
203 {
204 case type::word:
205 {
206 new(&second.word_.word_) word(std::move(tempWord));
207 second.word_.category_ = tempCategory;
208
209 break;
210 }
211
212 case type::literal:
213 {
214 new(&second.literal_) std::string(std::move(tempLiteral));
215
216 break;
217 }
218
219 case type::part:
220 {
221 new(&second.part_) part(std::move(tempPart));
222
223 break;
224 }
225
226 case type::fillin:
227 {
228 new(&second.fillin_) std::set<std::string>(std::move(tempFillin));
229
230 break;
231 }
232
233 case type::utterance:
234 {
235 new(&second.utterance_) std::list<token>(std::move(tempUtterance));
236
237 break;
238 }
239
240 case type::transform:
241 {
242 second.transform_.type_ = tempTransformType;
243 new(&second.transform_.strParam_) std::string(std::move(tempTransformStrParam));
244 new(&second.transform_.strParam2_) std::string(std::move(tempTransformStrParam2));
245 second.transform_.casingParam_ = tempTransformCasingParam;
246 new(&second.transform_.inner_) std::unique_ptr<token>(std::move(tempTransformInner));
247
248 break;
249 }
250 }
251 }
252
253 token::~token()
254 { 9 {
255 switch (type_) 10 switch (type_)
256 { 11 {
257 case type::word: 12 case type::word:
258 {
259 word_.word_.~word();
260
261 break;
262 }
263
264 case type::literal: 13 case type::literal:
265 { 14 {
266 using string_type = std::string; 15 return true;
267 literal_.~string_type();
268
269 break;
270 } 16 }
271 17
272 case type::part: 18 case type::part:
273 {
274 part_.~part();
275
276 break;
277 }
278
279 case type::fillin: 19 case type::fillin:
280 { 20 {
281 using set_type = std::set<std::string>; 21 return false;
282 fillin_.~set_type();
283
284 break;
285 } 22 }
286 23
287 case type::utterance: 24 case type::utterance:
288 { 25 {
289 using list_type = std::list<token>; 26 const utterance_type& utterance = mpark::get<utterance_type>(variant_);
290 utterance_.~list_type();
291 27
292 break; 28 return std::all_of(
29 std::begin(utterance),
30 std::end(utterance),
31 [] (const token& tkn) {
32 return tkn.isComplete();
33 });
293 } 34 }
294 35
295 case type::transform: 36 case type::transform:
296 { 37 {
297 using string_type = std::string; 38 const transform_type& transform = mpark::get<transform_type>(variant_);
298 using ptr_type = std::unique_ptr<token>;
299
300 transform_.strParam_.~string_type();
301 transform_.strParam2_.~string_type();
302 transform_.inner_.~ptr_type();
303 39
304 break; 40 return transform.inner->isComplete();
305 } 41 }
306 } 42 }
307 } 43 }
308 44
309 bool token::isComplete() const
310 {
311 switch (type_)
312 {
313 case type::word: return true;
314 case type::literal: return true;
315 case type::part: return false;
316 case type::fillin: return false;
317 case type::utterance: return std::all_of(std::begin(utterance_), std::end(utterance_), [] (const token& tkn) {
318 return tkn.isComplete();
319 });
320 case type::transform: return transform_.inner_->isComplete();
321 }
322 }
323
324 std::string token::compile() const 45 std::string token::compile() const
325 { 46 {
326 return compileHelper(" ", false, casing::normal); 47 return compileHelper(" ", false, casing::normal);
@@ -335,8 +56,9 @@ namespace verbly {
335 { 56 {
336 case type::word: 57 case type::word:
337 { 58 {
338 const form& wordForm = word_.word_.getInflections(word_.category_) 59 const word_type& w = mpark::get<word_type>(variant_);
339 .front(); 60
61 const form& wordForm = w.value.getInflections(w.category).front();
340 62
341 std::string result = wordForm.getText(); 63 std::string result = wordForm.getText();
342 64
@@ -381,13 +103,12 @@ namespace verbly {
381 } 103 }
382 } 104 }
383 105
384
385 return result; 106 return result;
386 } 107 }
387 108
388 case type::literal: 109 case type::literal:
389 { 110 {
390 std::string result = literal_; 111 std::string result = mpark::get<literal_type>(variant_);
391 112
392 if (indefiniteArticle && std::isalpha(result[0])) 113 if (indefiniteArticle && std::isalpha(result[0]))
393 { 114 {
@@ -435,14 +156,19 @@ namespace verbly {
435 return result; 156 return result;
436 } 157 }
437 158
438 case type::part: throw std::domain_error("Cannot compile incomplete token"); 159 case type::part:
439 case type::fillin: throw std::domain_error("Cannot compile incomplete token"); 160 case type::fillin:
161 {
162 throw std::domain_error("Cannot compile incomplete token");
163 }
440 164
441 case type::utterance: 165 case type::utterance:
442 { 166 {
167 const utterance_type& utterance = mpark::get<utterance_type>(variant_);
168
443 bool first = true; 169 bool first = true;
444 std::list<std::string> compiled; 170 std::list<std::string> compiled;
445 for (const token& tkn : utterance_) 171 for (const token& tkn : utterance)
446 { 172 {
447 casing propagateCasing = capitalization; 173 casing propagateCasing = capitalization;
448 if ((capitalization == casing::capitalize) && (!first)) 174 if ((capitalization == casing::capitalize) && (!first))
@@ -458,58 +184,70 @@ namespace verbly {
458 first = false; 184 first = false;
459 } 185 }
460 186
461 return hatkirby::implode(std::begin(compiled), std::end(compiled), separator); 187 return hatkirby::implode(
188 std::begin(compiled),
189 std::end(compiled),
190 separator);
462 } 191 }
463 192
464 case type::transform: 193 case type::transform:
465 { 194 {
466 switch (transform_.type_) 195 const transform_type& transform = mpark::get<transform_type>(variant_);
196
197 switch (transform.type)
467 { 198 {
468 case transform_type::separator: 199 case transform_mode::separator:
469 { 200 {
470 return transform_.inner_->compileHelper( 201 return transform.inner->compileHelper(
471 transform_.strParam_, indefiniteArticle, capitalization); 202 transform.strParam,
203 indefiniteArticle,
204 capitalization);
472 } 205 }
473 206
474 case transform_type::punctuation: 207 case transform_mode::punctuation:
475 { 208 {
476 return transform_.inner_->compileHelper( 209 return transform.inner->compileHelper(
477 separator, indefiniteArticle, capitalization) 210 separator,
478 + transform_.strParam_; 211 indefiniteArticle,
212 capitalization) + transform.strParam;
479 } 213 }
480 214
481 case transform_type::indefinite_article: 215 case transform_mode::indefinite_article:
482 { 216 {
483 return transform_.inner_->compileHelper( 217 return transform.inner->compileHelper(
484 separator, true, capitalization); 218 separator,
219 true,
220 capitalization);
485 } 221 }
486 222
487 case transform_type::capitalize: 223 case transform_mode::capitalize:
488 { 224 {
489 return transform_.inner_->compileHelper( 225 return transform.inner->compileHelper(
490 separator, 226 separator,
491 indefiniteArticle, 227 indefiniteArticle,
492 transform_.casingParam_); 228 transform.casingParam);
493 } 229 }
494 230
495 case transform_type::quote: 231 case transform_mode::quote:
496 { 232 {
497 return transform_.strParam_ + 233 return transform.strParam +
498 transform_.inner_->compileHelper( 234 transform.inner->compileHelper(
499 separator, 235 separator,
500 indefiniteArticle, 236 indefiniteArticle,
501 capitalization) + 237 capitalization) +
502 transform_.strParam2_; 238 transform.strParam2;
503 } 239 }
504 } 240 }
505 } 241 }
506 } 242 }
507 } 243 }
508 244
509 token::token(word arg, inflection category) : type_(type::word) 245 token::token(
246 word arg,
247 inflection category) :
248 type_(type::word),
249 variant_(word_type { std::move(arg), category })
510 { 250 {
511 new(&word_.word_) word(std::move(arg));
512 word_.category_ = category;
513 } 251 }
514 252
515 const word& token::getWord() const 253 const word& token::getWord() const
@@ -519,7 +257,7 @@ namespace verbly {
519 throw std::domain_error("Token is not a word"); 257 throw std::domain_error("Token is not a word");
520 } 258 }
521 259
522 return word_.word_; 260 return mpark::get<word_type>(variant_).value;
523 } 261 }
524 262
525 token token::inflect(inflection category) const 263 token token::inflect(inflection category) const
@@ -529,46 +267,57 @@ namespace verbly {
529 throw std::domain_error("Token is not a word"); 267 throw std::domain_error("Token is not a word");
530 } 268 }
531 269
532 return token(word_.word_, category); 270 return {
271 mpark::get<word_type>(variant_).value,
272 category
273 };
533 } 274 }
534 275
535 token::token(std::string arg) : type_(type::literal) 276 token::token(
277 std::string arg) :
278 type_(type::literal),
279 variant_(std::move(arg))
536 { 280 {
537 new(&literal_) std::string(std::move(arg));
538 } 281 }
539 282
540 token::token(const char* arg) : token(std::string(arg)) 283 token::token(
284 const char* arg) :
285 token(std::string(arg))
541 { 286 {
542 } 287 }
543 288
544 std::string token::getLiteral() const 289 const std::string& token::getLiteral() const
545 { 290 {
546 if (type_ != type::literal) 291 if (type_ != type::literal)
547 { 292 {
548 throw std::domain_error("Token is not a literal"); 293 throw std::domain_error("Token is not a literal");
549 } 294 }
550 295
551 return literal_; 296 return mpark::get<literal_type>(variant_);
552 } 297 }
553 298
554 token::token(part arg) : type_(type::part) 299 token::token(
300 part arg) :
301 type_(type::part),
302 variant_(std::move(arg))
555 { 303 {
556 new(&part_) part(std::move(arg));
557 } 304 }
558 305
559 part token::getPart() const 306 const part& token::getPart() const
560 { 307 {
561 if (type_ != type::part) 308 if (type_ != type::part)
562 { 309 {
563 throw std::domain_error("Token is not a part"); 310 throw std::domain_error("Token is not a part");
564 } 311 }
565 312
566 return part_; 313 return mpark::get<part>(variant_);
567 } 314 }
568 315
569 token::token(std::set<std::string> synrestrs) : type_(type::fillin) 316 token::token(
317 std::set<std::string> synrestrs) :
318 type_(type::fillin),
319 variant_(std::move(synrestrs))
570 { 320 {
571 new(&fillin_) std::set<std::string>(std::move(synrestrs));
572 } 321 }
573 322
574 const std::set<std::string>& token::getSynrestrs() const 323 const std::set<std::string>& token::getSynrestrs() const
@@ -578,7 +327,7 @@ namespace verbly {
578 throw std::domain_error("Token is not a fillin"); 327 throw std::domain_error("Token is not a fillin");
579 } 328 }
580 329
581 return fillin_; 330 return mpark::get<fillin_type>(variant_);
582 } 331 }
583 332
584 bool token::hasSynrestr(std::string synrestr) const 333 bool token::hasSynrestr(std::string synrestr) const
@@ -588,7 +337,7 @@ namespace verbly {
588 throw std::domain_error("Token is not a fillin"); 337 throw std::domain_error("Token is not a fillin");
589 } 338 }
590 339
591 return (fillin_.count(synrestr) == 1); 340 return mpark::get<fillin_type>(variant_).count(synrestr);
592 } 341 }
593 342
594 void token::addSynrestr(std::string synrestr) 343 void token::addSynrestr(std::string synrestr)
@@ -598,22 +347,28 @@ namespace verbly {
598 throw std::domain_error("Token is not a fillin"); 347 throw std::domain_error("Token is not a fillin");
599 } 348 }
600 349
601 fillin_.insert(std::move(synrestr)); 350 fillin_type& fillin = mpark::get<fillin_type>(variant_);
351 fillin.insert(std::move(synrestr));
602 } 352 }
603 353
604 token::token() : type_(type::utterance) 354 token::token() :
355 type_(type::utterance),
356 variant_(utterance_type {})
605 { 357 {
606 new(&utterance_) std::list<token>();
607 } 358 }
608 359
609 token::token(std::vector<part> parts) : type_(type::utterance) 360 token::token(
361 std::vector<part> parts) :
362 type_(type::utterance),
363 variant_(utterance_type { std::begin(parts), std::end(parts) })
610 { 364 {
611 new(&utterance_) std::list<token>(std::begin(parts), std::end(parts));
612 } 365 }
613 366
614 token::token(std::initializer_list<token> parts) : type_(type::utterance) 367 token::token(
368 std::initializer_list<token> parts) :
369 type_(type::utterance),
370 variant_(utterance_type { std::move(parts) })
615 { 371 {
616 new(&utterance_) std::list<token>(std::move(parts));
617 } 372 }
618 373
619 token::iterator token::begin() 374 token::iterator token::begin()
@@ -623,7 +378,7 @@ namespace verbly {
623 throw std::domain_error("Token is not an utterance"); 378 throw std::domain_error("Token is not an utterance");
624 } 379 }
625 380
626 return std::begin(utterance_); 381 return std::begin(mpark::get<utterance_type>(variant_));
627 } 382 }
628 383
629 token::const_iterator token::begin() const 384 token::const_iterator token::begin() const
@@ -633,7 +388,7 @@ namespace verbly {
633 throw std::domain_error("Token is not an utterance"); 388 throw std::domain_error("Token is not an utterance");
634 } 389 }
635 390
636 return std::begin(utterance_); 391 return std::begin(mpark::get<utterance_type>(variant_));
637 } 392 }
638 393
639 token::iterator token::end() 394 token::iterator token::end()
@@ -643,7 +398,7 @@ namespace verbly {
643 throw std::domain_error("Token is not an utterance"); 398 throw std::domain_error("Token is not an utterance");
644 } 399 }
645 400
646 return std::end(utterance_); 401 return std::end(mpark::get<utterance_type>(variant_));
647 } 402 }
648 403
649 token::const_iterator token::end() const 404 token::const_iterator token::end() const
@@ -653,7 +408,7 @@ namespace verbly {
653 throw std::domain_error("Token is not an utterance"); 408 throw std::domain_error("Token is not an utterance");
654 } 409 }
655 410
656 return std::end(utterance_); 411 return std::end(mpark::get<utterance_type>(variant_));
657 } 412 }
658 413
659 token& token::operator<<(token arg) 414 token& token::operator<<(token arg)
@@ -663,35 +418,36 @@ namespace verbly {
663 throw std::domain_error("Token is not an utterance"); 418 throw std::domain_error("Token is not an utterance");
664 } 419 }
665 420
666 utterance_.push_back(std::move(arg)); 421 utterance_type& utterance = mpark::get<utterance_type>(variant_);
422 utterance.push_back(std::move(arg));
667 423
668 return *this; 424 return *this;
669 } 425 }
670 426
671 token token::separator(std::string param, token inner) 427 token token::separator(std::string param, token inner)
672 { 428 {
673 return token(transform_type::separator, std::move(param), "", std::move(inner)); 429 return token(transform_mode::separator, std::move(param), "", std::move(inner));
674 } 430 }
675 431
676 token token::punctuation(std::string param, token inner) 432 token token::punctuation(std::string param, token inner)
677 { 433 {
678 return token(transform_type::punctuation, std::move(param), "", std::move(inner)); 434 return token(transform_mode::punctuation, std::move(param), "", std::move(inner));
679 } 435 }
680 436
681 token token::indefiniteArticle(token inner) 437 token token::indefiniteArticle(token inner)
682 { 438 {
683 return token(transform_type::indefinite_article, "", "", std::move(inner)); 439 return token(transform_mode::indefinite_article, "", "", std::move(inner));
684 } 440 }
685 441
686 token token::capitalize(casing param, token inner) 442 token token::capitalize(casing param, token inner)
687 { 443 {
688 return token(transform_type::capitalize, param, std::move(inner)); 444 return token(transform_mode::capitalize, param, std::move(inner));
689 } 445 }
690 446
691 token token::quote(std::string opening, std::string closing, token inner) 447 token token::quote(std::string opening, std::string closing, token inner)
692 { 448 {
693 return token( 449 return token(
694 transform_type::quote, 450 transform_mode::quote,
695 std::move(opening), 451 std::move(opening),
696 std::move(closing), 452 std::move(closing),
697 std::move(inner)); 453 std::move(inner));
@@ -704,7 +460,7 @@ namespace verbly {
704 throw std::domain_error("Invalid access on non-tranform token"); 460 throw std::domain_error("Invalid access on non-tranform token");
705 } 461 }
706 462
707 return *transform_.inner_; 463 return *mpark::get<transform_type>(variant_).inner;
708 } 464 }
709 465
710 const token& token::getInnerToken() const 466 const token& token::getInnerToken() const
@@ -714,33 +470,38 @@ namespace verbly {
714 throw std::domain_error("Invalid access on non-tranform token"); 470 throw std::domain_error("Invalid access on non-tranform token");
715 } 471 }
716 472
717 return *transform_.inner_; 473 return *mpark::get<transform_type>(variant_).inner;
718 } 474 }
719 475
720 token::token( 476 token::token(
721 transform_type type, 477 transform_mode type,
722 std::string param1, 478 std::string param1,
723 std::string param2, 479 std::string param2,
724 token inner) : 480 token inner) :
725 type_(type::transform) 481 type_(type::transform),
482 variant_(transform_type {
483 type,
484 std::move(param1),
485 std::move(param2),
486 casing::normal,
487 new token(std::move(inner))
488 })
726 { 489 {
727 transform_.type_ = type;
728 new(&transform_.strParam_) std::string(std::move(param1));
729 new(&transform_.strParam2_) std::string(std::move(param2));
730 new(&transform_.inner_) std::unique_ptr<token>(new token(std::move(inner)));
731 } 490 }
732 491
733 token::token( 492 token::token(
734 transform_type type, 493 transform_mode type,
735 casing param, 494 casing param,
736 token inner) : 495 token inner) :
737 type_(type::transform) 496 type_(type::transform),
497 variant_(transform_type {
498 type,
499 {},
500 {},
501 param,
502 new token(std::move(inner))
503 })
738 { 504 {
739 transform_.type_ = type;
740 new(&transform_.strParam_) std::string();
741 new(&transform_.strParam2_) std::string();
742 transform_.casingParam_ = param;
743 new(&transform_.inner_) std::unique_ptr<token>(new token(std::move(inner)));
744 } 505 }
745 506
746 std::ostream& operator<<(std::ostream& os, token::type type) 507 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 @@
5#include <string> 5#include <string>
6#include <list> 6#include <list>
7#include <set> 7#include <set>
8#include <variant.hpp>
9#include <hkutil/recptr.h>
8#include "enums.h" 10#include "enums.h"
9#include "word.h" 11#include "word.h"
10#include "part.h" 12#include "part.h"
@@ -22,23 +24,6 @@ namespace verbly {
22 transform 24 transform
23 }; 25 };
24 26
25 // Copy & move constructors
26
27 token(const token& other);
28 token(token&& other);
29
30 // Assignment operator
31
32 token& operator=(token other);
33
34 // Swap
35
36 friend void swap(token& first, token& second);
37
38 // Destructor
39
40 ~token();
41
42 // Accessors 27 // Accessors
43 28
44 type getType() const 29 type getType() const
@@ -52,7 +37,8 @@ namespace verbly {
52 37
53 bool isEmpty() const 38 bool isEmpty() const
54 { 39 {
55 return ((type_ == type::utterance) && (utterance_.empty())); 40 return (type_ == type::utterance &&
41 mpark::get<utterance_type>(variant_).empty());
56 } 42 }
57 43
58 // Word 44 // Word
@@ -68,13 +54,13 @@ namespace verbly {
68 token(std::string arg); 54 token(std::string arg);
69 token(const char* arg); 55 token(const char* arg);
70 56
71 std::string getLiteral() const; 57 const std::string& getLiteral() const;
72 58
73 // Part 59 // Part
74 60
75 token(part arg); 61 token(part arg);
76 62
77 part getPart() const; 63 const part& getPart() const;
78 64
79 // Fillin 65 // Fillin
80 66
@@ -128,7 +114,7 @@ namespace verbly {
128 bool indefiniteArticle, 114 bool indefiniteArticle,
129 casing capitalization) const; 115 casing capitalization) const;
130 116
131 enum class transform_type { 117 enum class transform_mode {
132 separator, 118 separator,
133 punctuation, 119 punctuation,
134 indefinite_article, 120 indefinite_article,
@@ -137,34 +123,46 @@ namespace verbly {
137 }; 123 };
138 124
139 token( 125 token(
140 transform_type type, 126 transform_mode type,
141 std::string param1, 127 std::string param1,
142 std::string param2, 128 std::string param2,
143 token inner); 129 token inner);
144 130
145 token( 131 token(
146 transform_type type, 132 transform_mode type,
147 casing param, 133 casing param,
148 token inner); 134 token inner);
149 135
150 union { 136 struct word_type {
151 struct { 137 word value;
152 word word_; 138 inflection category;
153 inflection category_;
154 } word_;
155 std::string literal_;
156 part part_;
157 std::set<std::string> fillin_;
158 std::list<token> utterance_;
159 struct {
160 transform_type type_;
161 std::string strParam_;
162 std::string strParam2_;
163 casing casingParam_;
164 std::unique_ptr<token> inner_;
165 } transform_;
166 }; 139 };
140
141 using literal_type = std::string;
142
143 using fillin_type = std::set<std::string>;
144
145 using utterance_type = std::list<token>;
146
147 struct transform_type {
148 transform_mode type;
149 std::string strParam;
150 std::string strParam2;
151 casing casingParam;
152 hatkirby::recptr<token> inner;
153 };
154
155 using variant_type =
156 mpark::variant<
157 word_type,
158 literal_type,
159 part,
160 fillin_type,
161 utterance_type,
162 transform_type>;
163
167 type type_; 164 type type_;
165 variant_type variant_;
168 }; 166 };
169 167
170 std::ostream& operator<<(std::ostream& os, token::type type); 168 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 @@
1#include "word.h" 1#include "word.h"
2#include <sqlite3.h>
3#include "form.h" 2#include "form.h"
4#include "util.h" 3#include "util.h"
5#include "database.h" 4#include "database.h"
@@ -45,89 +44,27 @@ namespace verbly {
45 return field::joinThroughWhere(object::word, "lemma_id", object::form, "lemmas_forms", "form_id", "category", static_cast<int>(category)); 44 return field::joinThroughWhere(object::word, "lemma_id", object::form, "lemmas_forms", "form_id", "category", static_cast<int>(category));
46 } 45 }
47 46
48 word::word(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) 47 word::word(const database& db, hatkirby::row row) : db_(db), valid_(true)
49 { 48 {
50 id_ = sqlite3_column_int(row, 0); 49 id_ = mpark::get<int>(row[0]);
51 notionId_ = sqlite3_column_int(row, 1);
52 lemmaId_ = sqlite3_column_int(row, 2);
53 50
54 if (sqlite3_column_type(row, 3) != SQLITE_NULL) 51 notion_ = db.notions(notion::id == mpark::get<int>(row[1])).first();
55 {
56 hasTagCount_ = true;
57 tagCount_ = sqlite3_column_int(row, 3);
58 }
59
60 if (sqlite3_column_type(row, 4) != SQLITE_NULL)
61 {
62 adjectivePosition_ = static_cast<positioning>(sqlite3_column_int(row, 4));
63 }
64
65 if (sqlite3_column_type(row, 5) != SQLITE_NULL)
66 {
67 hasGroup_ = true;
68 groupId_ = sqlite3_column_int(row, 5);
69 }
70 }
71
72 const notion& word::getNotion() const
73 {
74 if (!valid_)
75 {
76 throw std::domain_error("Bad access to uninitialized word");
77 }
78 52
79 if (!notion_.isValid()) 53 if (!mpark::holds_alternative<std::nullptr_t>(row[3]))
80 { 54 {
81 notion_ = db_->notions(notion::id == notionId_).first(); 55 hasTagCount_ = true;
82 } 56 tagCount_ = mpark::get<int>(row[3]);
83
84 return notion_;
85 }
86
87 bool word::hasFrames() const
88 {
89 if (!valid_)
90 {
91 throw std::domain_error("Bad access to uninitialized word");
92 }
93
94 if (!hasGroup_)
95 {
96 return false;
97 }
98
99 if (!initializedFrames_)
100 {
101 initializeFrames();
102 }
103
104 return !frames_.empty();
105 }
106
107 const std::vector<frame>& word::getFrames() const
108 {
109 if (!valid_)
110 {
111 throw std::domain_error("Bad access to uninitialized word");
112 } 57 }
113 58
114 if (!hasGroup_) 59 if (!mpark::holds_alternative<std::nullptr_t>(row[4]))
115 { 60 {
116 throw std::domain_error("Word does not have a group"); 61 adjectivePosition_ = static_cast<positioning>(mpark::get<int>(row[4]));
117 } 62 }
118 63
119 if (!initializedFrames_) 64 if (!mpark::holds_alternative<std::nullptr_t>(row[5]))
120 { 65 {
121 initializeFrames(); 66 frames_ = db.frames(*this).all();
122 } 67 }
123
124 return frames_;
125 }
126
127 void word::initializeFrames() const
128 {
129 initializedFrames_ = true;
130 frames_ = db_->frames(*this, {}, -1).all();
131 } 68 }
132 69
133 const form& word::getBaseForm() const 70 const form& word::getBaseForm() const
@@ -167,7 +104,7 @@ namespace verbly {
167 104
168 void word::initializeForm(inflection infl) const 105 void word::initializeForm(inflection infl) const
169 { 106 {
170 forms_[infl] = db_->forms(form::words(infl) %= *this, verbly::form::id, -1).all(); 107 forms_[infl] = db_.forms(form::words(infl) %= *this, verbly::form::id, -1).all();
171 } 108 }
172 109
173 filter word::synonyms_field::operator%=(filter joinCondition) const 110 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 @@
3 3
4#include <stdexcept> 4#include <stdexcept>
5#include <map> 5#include <map>
6#include <hkutil/database.h>
6#include "field.h" 7#include "field.h"
7#include "filter.h" 8#include "filter.h"
8#include "notion.h" 9#include "notion.h"
9#include "frame.h" 10#include "frame.h"
10#include "form.h" 11#include "form.h"
11 12
12struct sqlite3_stmt;
13
14namespace verbly { 13namespace verbly {
15 14
16 class database; 15 class database;
@@ -24,7 +23,7 @@ namespace verbly {
24 23
25 // Construct from database 24 // Construct from database
26 25
27 word(const database& db, sqlite3_stmt* row); 26 word(const database& db, hatkirby::row row);
28 27
29 // Accessors 28 // Accessors
30 29
@@ -93,11 +92,35 @@ namespace verbly {
93 return adjectivePosition_; 92 return adjectivePosition_;
94 } 93 }
95 94
96 const notion& getNotion() const; 95 const notion& getNotion() const
96 {
97 if (!valid_)
98 {
99 throw std::domain_error("Bad access to uninitialized word");
100 }
97 101
98 bool hasFrames() const; 102 return notion_;
103 }
99 104
100 const std::vector<frame>& getFrames() const; 105 bool hasFrames() const
106 {
107 if (!valid_)
108 {
109 throw std::domain_error("Bad access to uninitialized word");
110 }
111
112 return !frames_.empty();
113 }
114
115 const std::vector<frame>& getFrames() const
116 {
117 if (!valid_)
118 {
119 throw std::domain_error("Bad access to uninitialized word");
120 }
121
122 return frames_;
123 }
101 124
102 const form& getBaseForm() const; 125 const form& getBaseForm() const;
103 126
@@ -181,26 +204,17 @@ namespace verbly {
181 private: 204 private:
182 205
183 void initializeForm(inflection category) const; 206 void initializeForm(inflection category) const;
184 void initializeFrames() const;
185 207
186 bool valid_ = false; 208 bool valid_ = false;
187
188 int id_; 209 int id_;
189 bool hasTagCount_ = false; 210 bool hasTagCount_ = false;
190 int tagCount_; 211 int tagCount_;
191 positioning adjectivePosition_ = positioning::undefined; 212 positioning adjectivePosition_ = positioning::undefined;
192 int notionId_; 213 notion notion_;
193 int lemmaId_; 214 std::vector<frame> frames_;
194 bool hasGroup_ = false;
195 int groupId_;
196
197 const database* db_;
198
199 mutable notion notion_;
200 mutable bool initializedFrames_ = false;
201 mutable std::vector<frame> frames_;
202 mutable std::map<inflection, std::vector<form>> forms_; 215 mutable std::map<inflection, std::vector<form>> forms_;
203 216
217 const database& db_;
204 }; 218 };
205 219
206}; 220};
diff --git a/vendor/hkutil b/vendor/hkutil
Subproject b430eb58654298d17492a36c7bcda9f803a327f Subproject a9a5996310bb33207d3338f353aab97b9ed3a5e