diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/binding.cpp | 249 | ||||
-rw-r--r-- | lib/binding.h | 82 | ||||
-rw-r--r-- | lib/database.cpp | 123 | ||||
-rw-r--r-- | lib/database.h | 61 | ||||
-rw-r--r-- | lib/form.cpp | 52 | ||||
-rw-r--r-- | lib/form.h | 26 | ||||
-rw-r--r-- | lib/frame.cpp | 9 | ||||
-rw-r--r-- | lib/frame.h | 10 | ||||
-rw-r--r-- | lib/notion.cpp | 16 | ||||
-rw-r--r-- | lib/notion.h | 118 | ||||
-rw-r--r-- | lib/part.cpp | 350 | ||||
-rw-r--r-- | lib/part.h | 97 | ||||
-rw-r--r-- | lib/pronunciation.cpp | 24 | ||||
-rw-r--r-- | lib/pronunciation.h | 22 | ||||
-rw-r--r-- | lib/query.h | 107 | ||||
-rw-r--r-- | lib/statement.cpp | 621 | ||||
-rw-r--r-- | lib/statement.h | 96 | ||||
-rw-r--r-- | lib/token.cpp | 495 | ||||
-rw-r--r-- | lib/token.h | 76 | ||||
-rw-r--r-- | lib/word.cpp | 85 | ||||
-rw-r--r-- | lib/word.h | 50 |
21 files changed, 851 insertions, 1918 deletions
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 | |||
5 | namespace 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 | |||
6 | namespace 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 | ||
8 | namespace verbly { | 6 | namespace 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 | ||
17 | struct sqlite3; | ||
18 | |||
19 | namespace verbly { | 16 | namespace 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 | ||
12 | struct sqlite3_stmt; | ||
13 | |||
14 | namespace verbly { | 13 | namespace 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 | ||
10 | struct sqlite3_stmt; | ||
11 | |||
12 | namespace verbly { | 11 | namespace 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 | ||
5 | namespace verbly { | 5 | namespace 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 | ||
9 | struct sqlite3_stmt; | ||
10 | |||
11 | namespace verbly { | 10 | namespace 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 | ||
12 | struct sqlite3_stmt; | ||
13 | |||
14 | namespace verbly { | 14 | namespace 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 | ||
10 | struct sqlite3_stmt; | ||
11 | |||
12 | namespace verbly { | 11 | namespace 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 | ||
14 | namespace verbly { | 12 | namespace 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 | ||
6 | namespace verbly { | 6 | namespace 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 | ||
12 | struct sqlite3_stmt; | ||
13 | |||
14 | namespace verbly { | 13 | namespace 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 | }; |