summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/database.cpp60
-rw-r--r--lib/database.h20
-rw-r--r--lib/enums.h14
-rw-r--r--lib/field.cpp20
-rw-r--r--lib/field.h78
-rw-r--r--lib/filter.cpp35
-rw-r--r--lib/form.cpp16
-rw-r--r--lib/form.h31
-rw-r--r--lib/frame.cpp95
-rw-r--r--lib/frame.h21
-rw-r--r--lib/group.cpp61
-rw-r--r--lib/group.h91
-rw-r--r--lib/lemma.cpp15
-rw-r--r--lib/lemma.h27
-rw-r--r--lib/order.h69
-rw-r--r--lib/part.cpp128
-rw-r--r--lib/part.h64
-rw-r--r--lib/query.h11
-rw-r--r--lib/role.h60
-rw-r--r--lib/statement.cpp164
-rw-r--r--lib/statement.h9
-rw-r--r--lib/verbly.h5
-rw-r--r--lib/word.cpp36
-rw-r--r--lib/word.h23
24 files changed, 644 insertions, 509 deletions
diff --git a/lib/database.cpp b/lib/database.cpp index fb00ef3..563ec31 100644 --- a/lib/database.cpp +++ b/lib/database.cpp
@@ -41,39 +41,71 @@ namespace verbly {
41 sqlite3_close_v2(ppdb_); 41 sqlite3_close_v2(ppdb_);
42 } 42 }
43 43
44 query<notion> database::notions(filter where, bool random, int limit) const 44 query<notion> database::notions(filter where, order sortOrder, int limit) const
45 { 45 {
46 return query<notion>(*this, ppdb_, std::move(where), random, limit); 46 return query<notion>(*this, ppdb_, std::move(where), std::move(sortOrder), limit);
47 } 47 }
48 48
49 query<word> database::words(filter where, bool random, int limit) const 49 query<word> database::words(filter where, order sortOrder, int limit) const
50 { 50 {
51 return query<word>(*this, ppdb_, std::move(where), random, limit); 51 return query<word>(*this, ppdb_, std::move(where), std::move(sortOrder), limit);
52 } 52 }
53 53
54 query<group> database::groups(filter where, bool random, int limit) const 54 query<frame> database::frames(filter where, order sortOrder, int limit) const
55 { 55 {
56 return query<group>(*this, ppdb_, std::move(where), random, limit); 56 return query<frame>(*this, ppdb_, std::move(where), std::move(sortOrder), limit);
57 } 57 }
58 58
59 query<frame> database::frames(filter where, bool random, int limit) const 59 query<part> database::parts(filter where, order sortOrder, int limit) const
60 { 60 {
61 return query<frame>(*this, ppdb_, std::move(where), random, limit); 61 return query<part>(*this, ppdb_, std::move(where), std::move(sortOrder), limit);
62 } 62 }
63 63
64 query<lemma> database::lemmas(filter where, bool random, int limit) const 64 query<lemma> database::lemmas(filter where, order sortOrder, int limit) const
65 { 65 {
66 return query<lemma>(*this, ppdb_, std::move(where), random, limit); 66 return query<lemma>(*this, ppdb_, std::move(where), std::move(sortOrder), limit);
67 } 67 }
68 68
69 query<form> database::forms(filter where, bool random, int limit) const 69 query<form> database::forms(filter where, order sortOrder, int limit) const
70 { 70 {
71 return query<form>(*this, ppdb_, std::move(where), random, limit); 71 return query<form>(*this, ppdb_, std::move(where), std::move(sortOrder), limit);
72 } 72 }
73 73
74 query<pronunciation> database::pronunciations(filter where, bool random, int limit) const 74 query<pronunciation> database::pronunciations(filter where, order sortOrder, int limit) const
75 { 75 {
76 return query<pronunciation>(*this, ppdb_, std::move(where), random, limit); 76 return query<pronunciation>(*this, ppdb_, std::move(where), std::move(sortOrder), limit);
77 }
78
79 std::set<std::string> database::synrestrs(int partId) const
80 {
81 std::string queryString = "SELECT synrestr FROM synrestrs WHERE part_id = ?";
82
83 sqlite3_stmt* ppstmt;
84 if (sqlite3_prepare_v2(ppdb_, queryString.c_str(), queryString.length(), &ppstmt, NULL) != SQLITE_OK)
85 {
86 std::string errorMsg = sqlite3_errmsg(ppdb_);
87 sqlite3_finalize(ppstmt);
88
89 throw database_error("Error preparing query", errorMsg);
90 }
91
92 if (sqlite3_bind_int(ppstmt, 1, partId) != SQLITE_OK)
93 {
94 std::string errorMsg = sqlite3_errmsg(ppdb_);
95 sqlite3_finalize(ppstmt);
96
97 throw database_error("Error binding value to query", errorMsg);
98 }
99
100 std::set<std::string> result;
101 while (sqlite3_step(ppstmt) == SQLITE_ROW)
102 {
103 result.insert(reinterpret_cast<const char*>(sqlite3_column_blob(ppstmt, 0)));
104 }
105
106 sqlite3_finalize(ppstmt);
107
108 return result;
77 } 109 }
78 110
79}; 111};
diff --git a/lib/database.h b/lib/database.h index ef50739..0b10eba 100644 --- a/lib/database.h +++ b/lib/database.h
@@ -4,13 +4,15 @@
4#include <string> 4#include <string>
5#include <exception> 5#include <exception>
6#include <list> 6#include <list>
7#include <set>
7#include "notion.h" 8#include "notion.h"
8#include "word.h" 9#include "word.h"
9#include "group.h"
10#include "frame.h" 10#include "frame.h"
11#include "part.h"
11#include "lemma.h" 12#include "lemma.h"
12#include "form.h" 13#include "form.h"
13#include "pronunciation.h" 14#include "pronunciation.h"
15#include "order.h"
14 16
15struct sqlite3; 17struct sqlite3;
16 18
@@ -46,19 +48,21 @@ namespace verbly {
46 48
47 // Queries 49 // Queries
48 50
49 query<notion> notions(filter where, bool random = true, int limit = 1) const; 51 query<notion> notions(filter where, order sortOrder = {}, int limit = 1) const;
50 52
51 query<word> words(filter where, bool random = true, int limit = 1) const; 53 query<word> words(filter where, order sortOrder = {}, int limit = 1) const;
52 54
53 query<group> groups(filter where, bool random = true, int limit = 1) const; 55 query<frame> frames(filter where, order sortOrder = {}, int limit = 1) const;
54 56
55 query<frame> frames(filter where, bool random = true, int limit = 1) const; 57 query<part> parts(filter where, order sortOrder = {}, int limit = 1) const;
56 58
57 query<lemma> lemmas(filter where, bool random = true, int limit = 1) const; 59 query<lemma> lemmas(filter where, order sortOrder = {}, int limit = 1) const;
58 60
59 query<form> forms(filter where, bool random = true, int limit = 1) const; 61 query<form> forms(filter where, order sortOrder = {}, int limit = 1) const;
60 62
61 query<pronunciation> pronunciations(filter where, bool random = true, int limit = 1) const; 63 query<pronunciation> pronunciations(filter where, order sortOrder = {}, int limit = 1) const;
64
65 std::set<std::string> synrestrs(int partId) const;
62 66
63 private: 67 private:
64 68
diff --git a/lib/enums.h b/lib/enums.h index e634959..2646fa4 100644 --- a/lib/enums.h +++ b/lib/enums.h
@@ -33,13 +33,23 @@ namespace verbly {
33 undefined = -1, 33 undefined = -1,
34 notion = 0, 34 notion = 0,
35 word = 1, 35 word = 1,
36 group = 2, 36 frame = 2,
37 frame = 3, 37 part = 3,
38 lemma = 4, 38 lemma = 4,
39 form = 5, 39 form = 5,
40 pronunciation = 6 40 pronunciation = 6
41 }; 41 };
42 42
43 enum class part_type {
44 invalid = -1,
45 noun_phrase = 0,
46 verb = 1,
47 preposition = 2,
48 adjective = 3,
49 adverb = 4,
50 literal = 5
51 };
52
43}; 53};
44 54
45#endif /* end of include guard: ENUMS_H_260BA847 */ 55#endif /* end of include guard: ENUMS_H_260BA847 */
diff --git a/lib/field.cpp b/lib/field.cpp index deecb06..5b51ef4 100644 --- a/lib/field.cpp +++ b/lib/field.cpp
@@ -48,6 +48,11 @@ namespace verbly {
48 return filter(*this, filter::comparison::int_equals, static_cast<int>(value)); 48 return filter(*this, filter::comparison::int_equals, static_cast<int>(value));
49 } 49 }
50 50
51 filter field::operator==(part_type value) const
52 {
53 return filter(*this, filter::comparison::int_equals, static_cast<int>(value));
54 }
55
51 filter field::operator==(bool value) const 56 filter field::operator==(bool value) const
52 { 57 {
53 return filter(*this, filter::comparison::boolean_equals, value); 58 return filter(*this, filter::comparison::boolean_equals, value);
@@ -68,6 +73,21 @@ namespace verbly {
68 return filter(*this, filter::comparison::string_is_like, std::move(value)); 73 return filter(*this, filter::comparison::string_is_like, std::move(value));
69 } 74 }
70 75
76 filter field::operator==(const char* value) const
77 {
78 return filter(*this, filter::comparison::string_equals, std::string(value));
79 }
80
81 filter field::operator!=(const char* value) const
82 {
83 return filter(*this, filter::comparison::string_does_not_equal, std::string(value));
84 }
85
86 filter field::operator%=(const char* value) const
87 {
88 return filter(*this, filter::comparison::string_is_like, std::string(value));
89 }
90
71 field::operator filter() const 91 field::operator filter() const
72 { 92 {
73 if (isJoin()) 93 if (isJoin())
diff --git a/lib/field.h b/lib/field.h index f61e038..b4bf02d 100644 --- a/lib/field.h +++ b/lib/field.h
@@ -17,6 +17,7 @@ namespace verbly {
17 integer, 17 integer,
18 boolean, 18 boolean,
19 join, 19 join,
20 join_where,
20 join_through, 21 join_through,
21 hierarchal_join 22 hierarchal_join
22 }; 23 };
@@ -95,6 +96,17 @@ namespace verbly {
95 return field(obj, type::join, name, nullable, table); 96 return field(obj, type::join, name, nullable, table);
96 } 97 }
97 98
99 static field joinWhere(
100 object obj,
101 const char* name,
102 object joinWith,
103 const field& conditionField,
104 int conditionValue,
105 bool nullable = false)
106 {
107 return field(obj, type::join_where, name, nullable, 0, joinWith, 0, 0, 0, &conditionField, conditionValue);
108 }
109
98 static field joinThrough( 110 static field joinThrough(
99 object obj, 111 object obj,
100 const char* name, 112 const char* name,
@@ -151,7 +163,10 @@ namespace verbly {
151 163
152 bool isJoin() const 164 bool isJoin() const
153 { 165 {
154 return ((type_ == type::join) || (type_ == type::join_through) || (type_ == type::hierarchal_join)); 166 return ((type_ == type::join)
167 || (type_ == type::join_where)
168 || (type_ == type::join_through)
169 || (type_ == type::hierarchal_join));
155 } 170 }
156 171
157 const char* getColumn() const 172 const char* getColumn() const
@@ -180,7 +195,7 @@ namespace verbly {
180 { 195 {
181 return (type_ == type::hierarchal_join) 196 return (type_ == type::hierarchal_join)
182 ? object_ 197 ? object_
183 : ((type_ == type::join) || (type_ == type::join_through)) 198 : ((type_ == type::join) || (type_ == type::join_where) || (type_ == type::join_through))
184 ? joinObject_ 199 ? joinObject_
185 : throw std::domain_error("Non-join fields don't have join objects"); 200 : throw std::domain_error("Non-join fields don't have join objects");
186 } 201 }
@@ -209,6 +224,22 @@ namespace verbly {
209 : throw std::domain_error("Only many-to-many join fields have a foreign join column"); 224 : throw std::domain_error("Only many-to-many join fields have a foreign join column");
210 } 225 }
211 226
227 // Condition joins
228
229 const field& getConditionField() const
230 {
231 return (type_ == type::join_where)
232 ? *conditionField_
233 : throw std::domain_error("Only condition join fields have a condition field");
234 }
235
236 int getConditionValue() const
237 {
238 return (type_ == type::join_where)
239 ? conditionValue_
240 : throw std::domain_error("Only condition join fields have a condition value");
241 }
242
212 // Ordering 243 // Ordering
213 244
214 bool operator<(const field& other) const 245 bool operator<(const field& other) const
@@ -217,20 +248,30 @@ namespace verbly {
217 // However, there do exist a number of relationships from an object to 248 // However, there do exist a number of relationships from an object to
218 // itself, such as notion hypernymy/hyponymy. Hypernymy and hyponymy have 249 // itself, such as notion hypernymy/hyponymy. Hypernymy and hyponymy have
219 // the same object (notion), the same column (notion_id), and the same 250 // the same object (notion), the same column (notion_id), and the same
220 // table (hypernymy); however, they have different join columns. 251 // table (hypernymy); however, they have different join columns. For
221 return std::tie(object_, column_, table_, joinColumn_) < std::tie(other.object_, other.column_, other.table_, other.joinColumn_); 252 // condition joins, the condition field and condition value are also
253 // significant.
254 if (conditionField_)
255 {
256 return std::tie(object_, column_, table_, joinColumn_, *conditionField_, conditionValue_)
257 < std::tie(other.object_, other.column_, other.table_, other.joinColumn_, *other.conditionField_, other.conditionValue_);
258 } else {
259 return std::tie(object_, column_, table_, joinColumn_) < std::tie(other.object_, other.column_, other.table_, other.joinColumn_);
260 }
222 } 261 }
223 262
224 // Equality 263 // Equality
225 264
226 bool operator==(const field& other) const 265 bool operator==(const field& other) const
227 { 266 {
228 // For the most part, (object, column) uniquely identifies fields. 267 // See operator<() for documentation.
229 // However, there do exist a number of relationships from an object to 268 if (conditionField_)
230 // itself, such as notion hypernymy/hyponymy. Hypernymy and hyponymy have 269 {
231 // the same object (notion), the same column (notion_id), and the same 270 return std::tie(object_, column_, table_, joinColumn_, *conditionField_, conditionValue_)
232 // table (hypernymy); however, they have different join columns. 271 == std::tie(other.object_, other.column_, other.table_, other.joinColumn_, *other.conditionField_, other.conditionValue_);
233 return std::tie(object_, column_, table_, joinColumn_) == std::tie(other.object_, other.column_, other.table_, other.joinColumn_); 272 } else {
273 return std::tie(object_, column_, table_, joinColumn_) == std::tie(other.object_, other.column_, other.table_, other.joinColumn_);
274 }
234 } 275 }
235 276
236 // Filter construction 277 // Filter construction
@@ -245,6 +286,7 @@ namespace verbly {
245 filter operator==(part_of_speech value) const; // Part of speech equality 286 filter operator==(part_of_speech value) const; // Part of speech equality
246 filter operator==(positioning value) const; // Adjective positioning equality 287 filter operator==(positioning value) const; // Adjective positioning equality
247 filter operator==(inflection value) const; // Inflection category equality 288 filter operator==(inflection value) const; // Inflection category equality
289 filter operator==(part_type value) const; // Verb frame part type equality
248 290
249 filter operator==(bool value) const; // Boolean equality 291 filter operator==(bool value) const; // Boolean equality
250 292
@@ -252,6 +294,10 @@ namespace verbly {
252 filter operator!=(std::string value) const; // String inequality 294 filter operator!=(std::string value) const; // String inequality
253 filter operator%=(std::string value) const; // String matching 295 filter operator%=(std::string value) const; // String matching
254 296
297 filter operator==(const char* value) const; // String equality
298 filter operator!=(const char* value) const; // String inequality
299 filter operator%=(const char* value) const; // String matching
300
255 operator filter() const; // Non-nullity 301 operator filter() const; // Non-nullity
256 filter operator!() const; // Nullity 302 filter operator!() const; // Nullity
257 303
@@ -270,7 +316,9 @@ namespace verbly {
270 object joinObject = object::undefined, 316 object joinObject = object::undefined,
271 const char* foreignColumn = 0, 317 const char* foreignColumn = 0,
272 const char* joinColumn = 0, 318 const char* joinColumn = 0,
273 const char* foreignJoinColumn = 0) : 319 const char* foreignJoinColumn = 0,
320 const field* conditionField = 0,
321 int conditionValue = 0) :
274 object_(obj), 322 object_(obj),
275 type_(datatype), 323 type_(datatype),
276 column_(column), 324 column_(column),
@@ -279,7 +327,9 @@ namespace verbly {
279 joinObject_(joinObject), 327 joinObject_(joinObject),
280 foreignColumn_(foreignColumn), 328 foreignColumn_(foreignColumn),
281 joinColumn_(joinColumn), 329 joinColumn_(joinColumn),
282 foreignJoinColumn_(foreignJoinColumn) 330 foreignJoinColumn_(foreignJoinColumn),
331 conditionField_(conditionField),
332 conditionValue_(conditionValue)
283 { 333 {
284 } 334 }
285 335
@@ -300,6 +350,10 @@ namespace verbly {
300 const char* joinColumn_ = 0; 350 const char* joinColumn_ = 0;
301 const char* foreignJoinColumn_ = 0; 351 const char* foreignJoinColumn_ = 0;
302 352
353 // Condition joins
354 const field* conditionField_ = 0;
355 int conditionValue_ = 0;
356
303 }; 357 };
304 358
305}; 359};
diff --git a/lib/filter.cpp b/lib/filter.cpp index ceb9327..ab46df2 100644 --- a/lib/filter.cpp +++ b/lib/filter.cpp
@@ -3,8 +3,8 @@
3#include <map> 3#include <map>
4#include "notion.h" 4#include "notion.h"
5#include "word.h" 5#include "word.h"
6#include "group.h"
7#include "frame.h" 6#include "frame.h"
7#include "part.h"
8#include "lemma.h" 8#include "lemma.h"
9#include "form.h" 9#include "form.h"
10#include "pronunciation.h" 10#include "pronunciation.h"
@@ -594,6 +594,7 @@ namespace verbly {
594 switch (joinOn.getType()) 594 switch (joinOn.getType())
595 { 595 {
596 case field::type::join: 596 case field::type::join:
597 case field::type::join_where:
597 case field::type::join_through: 598 case field::type::join_through:
598 { 599 {
599 switch (filterType) 600 switch (filterType)
@@ -1108,8 +1109,8 @@ namespace verbly {
1108 } 1109 }
1109 1110
1110 case object::word: 1111 case object::word:
1111 case object::group:
1112 case object::frame: 1112 case object::frame:
1113 case object::part:
1113 case object::lemma: 1114 case object::lemma:
1114 case object::form: 1115 case object::form:
1115 case object::pronunciation: 1116 case object::pronunciation:
@@ -1134,10 +1135,10 @@ namespace verbly {
1134 return *this; 1135 return *this;
1135 } 1136 }
1136 1137
1137 case object::group:
1138 case object::frame: 1138 case object::frame:
1139 case object::part:
1139 { 1140 {
1140 return (verbly::word::group %= *this); 1141 return (verbly::word::frame %= *this);
1141 } 1142 }
1142 1143
1143 case object::lemma: 1144 case object::lemma:
@@ -1148,12 +1149,12 @@ namespace verbly {
1148 } 1149 }
1149 } 1150 }
1150 1151
1151 case object::group: 1152 case object::frame:
1152 { 1153 {
1153 switch (singleton_.filterField.getObject()) 1154 switch (singleton_.filterField.getObject())
1154 { 1155 {
1155 case object::undefined: 1156 case object::undefined:
1156 case object::group: 1157 case object::frame:
1157 { 1158 {
1158 return *this; 1159 return *this;
1159 } 1160 }
@@ -1164,34 +1165,34 @@ namespace verbly {
1164 case object::form: 1165 case object::form:
1165 case object::pronunciation: 1166 case object::pronunciation:
1166 { 1167 {
1167 return (verbly::group::word %= *this); 1168 return (verbly::frame::word %= *this);
1168 } 1169 }
1169 1170
1170 case object::frame: 1171 case object::part:
1171 { 1172 {
1172 return (verbly::group::frame %= *this); 1173 return (verbly::frame::part() %= *this);
1173 } 1174 }
1174 } 1175 }
1175 } 1176 }
1176 1177
1177 case object::frame: 1178 case object::part:
1178 { 1179 {
1179 switch (singleton_.filterField.getObject()) 1180 switch (singleton_.filterField.getObject())
1180 { 1181 {
1181 case object::undefined: 1182 case object::undefined:
1182 case object::frame: 1183 case object::part:
1183 { 1184 {
1184 return *this; 1185 return *this;
1185 } 1186 }
1186 1187
1187 case object::notion: 1188 case object::notion:
1188 case object::word: 1189 case object::word:
1189 case object::group: 1190 case object::frame:
1190 case object::lemma: 1191 case object::lemma:
1191 case object::form: 1192 case object::form:
1192 case object::pronunciation: 1193 case object::pronunciation:
1193 { 1194 {
1194 return (verbly::frame::group %= *this); 1195 return (verbly::part::frame %= *this);
1195 } 1196 }
1196 } 1197 }
1197 } 1198 }
@@ -1202,8 +1203,8 @@ namespace verbly {
1202 { 1203 {
1203 case object::notion: 1204 case object::notion:
1204 case object::word: 1205 case object::word:
1205 case object::group:
1206 case object::frame: 1206 case object::frame:
1207 case object::part:
1207 { 1208 {
1208 return verbly::lemma::word %= *this; 1209 return verbly::lemma::word %= *this;
1209 } 1210 }
@@ -1228,11 +1229,11 @@ namespace verbly {
1228 { 1229 {
1229 case object::notion: 1230 case object::notion:
1230 case object::word: 1231 case object::word:
1231 case object::group:
1232 case object::frame: 1232 case object::frame:
1233 case object::part:
1233 case object::lemma: 1234 case object::lemma:
1234 { 1235 {
1235 return verbly::form::lemma(inflection::base) %= *this; 1236 return verbly::form::lemma %= *this;
1236 } 1237 }
1237 1238
1238 case object::undefined: 1239 case object::undefined:
@@ -1254,8 +1255,8 @@ namespace verbly {
1254 { 1255 {
1255 case object::notion: 1256 case object::notion:
1256 case object::word: 1257 case object::word:
1257 case object::group:
1258 case object::frame: 1258 case object::frame:
1259 case object::part:
1259 case object::lemma: 1260 case object::lemma:
1260 case object::form: 1261 case object::form:
1261 { 1262 {
diff --git a/lib/form.cpp b/lib/form.cpp index 5d4c343..4811f14 100644 --- a/lib/form.cpp +++ b/lib/form.cpp
@@ -16,11 +16,9 @@ namespace verbly {
16 const field form::complexity = field::integerField(object::form, "complexity"); 16 const field form::complexity = field::integerField(object::form, "complexity");
17 const field form::proper = field::booleanField(object::form, "proper"); 17 const field form::proper = field::booleanField(object::form, "proper");
18 18
19 const field form::lemma = field::joinField(object::form, "form_id", object::lemma);
19 const field form::pronunciation = field::joinThrough(object::form, "form_id", object::pronunciation, "forms_pronunciations", "pronunciation_id"); 20 const field form::pronunciation = field::joinThrough(object::form, "form_id", object::pronunciation, "forms_pronunciations", "pronunciation_id");
20 21
21 const field form::lemmaJoin = field::joinField(object::form, "form_id", object::lemma);
22 const field form::inflectionCategory = field::integerField("lemmas_forms", "category");
23
24 form::form(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) 22 form::form(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true)
25 { 23 {
26 id_ = sqlite3_column_int(row, 0); 24 id_ = sqlite3_column_int(row, 0);
@@ -29,16 +27,6 @@ namespace verbly {
29 proper_ = (sqlite3_column_int(row, 3) == 1); 27 proper_ = (sqlite3_column_int(row, 3) == 1);
30 } 28 }
31 29
32 filter operator%=(form::inflection_field check, filter joinCondition)
33 {
34 return (form::lemmaJoin %= (joinCondition && (form::inflectionCategory == check.getCategory())));
35 }
36
37 form::inflection_field::operator filter() const
38 {
39 return (form::lemmaJoin %= (form::inflectionCategory == category_));
40 }
41
42 const std::vector<pronunciation>& form::getPronunciations() const 30 const std::vector<pronunciation>& form::getPronunciations() const
43 { 31 {
44 if (!valid_) 32 if (!valid_)
@@ -48,7 +36,7 @@ namespace verbly {
48 36
49 if (!initializedPronunciations_) 37 if (!initializedPronunciations_)
50 { 38 {
51 pronunciations_ = db_->pronunciations(pronunciation::form %= *this, false, -1).all(); 39 pronunciations_ = db_->pronunciations(pronunciation::form %= *this, verbly::pronunciation::id, -1).all();
52 initializedPronunciations_ = true; 40 initializedPronunciations_ = true;
53 } 41 }
54 42
diff --git a/lib/form.h b/lib/form.h index aca5b2f..cf64117 100644 --- a/lib/form.h +++ b/lib/form.h
@@ -104,33 +104,9 @@ namespace verbly {
104 104
105 // Relationships to other objects 105 // Relationships to other objects
106 106
107 static const field pronunciation; 107 static const field lemma;
108
109 class inflection_field {
110 public:
111
112 inflection_field(inflection category) : category_(category)
113 {
114 }
115
116 const inflection getCategory() const
117 {
118 return category_;
119 }
120 108
121 operator filter() const; 109 static const field pronunciation;
122
123 private:
124
125 const inflection category_;
126 };
127
128 static const inflection_field lemma(inflection category)
129 {
130 return inflection_field(category);
131 }
132
133 friend filter operator%=(form::inflection_field check, filter joinCondition);
134 110
135 private: 111 private:
136 bool valid_ = false; 112 bool valid_ = false;
@@ -145,9 +121,6 @@ namespace verbly {
145 mutable bool initializedPronunciations_ = false; 121 mutable bool initializedPronunciations_ = false;
146 mutable std::vector<class pronunciation> pronunciations_; 122 mutable std::vector<class pronunciation> pronunciations_;
147 123
148 static const field lemmaJoin;
149 static const field inflectionCategory;
150
151 }; 124 };
152 125
153}; 126};
diff --git a/lib/frame.cpp b/lib/frame.cpp index 8cab56b..a73fbda 100644 --- a/lib/frame.cpp +++ b/lib/frame.cpp
@@ -1,95 +1,36 @@
1#include "frame.h" 1#include "frame.h"
2#include <sqlite3.h> 2#include <sqlite3.h>
3#include <json.hpp> 3#include "database.h"
4#include "query.h"
4 5
5namespace verbly { 6namespace verbly {
6 7
7 const object frame::objectType = object::frame; 8 const object frame::objectType = object::frame;
8 9
9 const std::list<std::string> frame::select = {"frame_id", "data"}; 10 const std::list<std::string> frame::select = {"frame_id", "group_id", "length"};
10 11
11 const field frame::id = field::integerField(object::frame, "frame_id"); 12 const field frame::id = field::integerField(object::frame, "frame_id");
13 const field frame::length = field::integerField(object::frame, "length");
12 14
13 const field frame::group = field::joinThrough(object::frame, "frame_id", object::group, "groups_frames", "group_id"); 15 const field frame::word = field::joinField(object::frame, "group_id", object::word);
14 16
15 frame::frame(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) 17 field frame::part()
16 { 18 {
17 id_ = sqlite3_column_int(row, 0); 19 return field::joinField(object::frame, "frame_id", object::part);
18 20 }
19 std::string partsJsonStr(reinterpret_cast<const char*>(sqlite3_column_blob(row, 1)));
20 nlohmann::json partsJson = nlohmann::json::parse(std::move(partsJsonStr));
21
22 for (const nlohmann::json& partJson : partsJson)
23 {
24 part::type partType = static_cast<part::type>(partJson["type"].get<int>());
25
26 switch (partType)
27 {
28 case part::type::noun_phrase:
29 {
30 std::set<std::string> synrestrs;
31 for (const nlohmann::json& synrestrJson : partJson["synrestrs"])
32 {
33 synrestrs.insert(synrestrJson.get<std::string>());
34 }
35
36 parts_.push_back(part::createNounPhrase(
37 partJson["role"].get<std::string>(),
38 selrestr(partJson["selrestrs"]),
39 std::move(synrestrs)));
40
41 break;
42 }
43
44 case part::type::preposition:
45 {
46 std::vector<std::string> choices;
47 for (const nlohmann::json& choiceJson : partJson["choices"])
48 {
49 choices.push_back(choiceJson.get<std::string>());
50 }
51
52 parts_.push_back(part::createPreposition(
53 std::move(choices),
54 partJson["literal"].get<bool>()));
55
56 break;
57 }
58
59 case part::type::verb:
60 {
61 parts_.push_back(part::createVerb());
62
63 break;
64 }
65
66 case part::type::adjective:
67 {
68 parts_.push_back(part::createAdjective());
69
70 break;
71 }
72
73 case part::type::adverb:
74 {
75 parts_.push_back(part::createAdverb());
76
77 break;
78 }
79 21
80 case part::type::literal: 22 field frame::part(int index)
81 { 23 {
82 parts_.push_back(part::createLiteral(partJson["value"].get<std::string>())); 24 return field::joinWhere(object::frame, "frame_id", object::part, part::index, index);
25 }
83 26
84 break; 27 frame::frame(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true)
85 } 28 {
29 id_ = sqlite3_column_int(row, 0);
30 groupId_ = sqlite3_column_int(row, 1);
31 length_ = sqlite3_column_int(row, 2);
86 32
87 case part::type::invalid: 33 parts_ = db.parts(*this, verbly::part::index, -1).all();
88 {
89 throw std::domain_error("Invalid part data");
90 }
91 }
92 }
93 } 34 }
94 35
95}; 36};
diff --git a/lib/frame.h b/lib/frame.h index 97473a0..36e179e 100644 --- a/lib/frame.h +++ b/lib/frame.h
@@ -41,6 +41,16 @@ namespace verbly {
41 return id_; 41 return id_;
42 } 42 }
43 43
44 int getLength() const
45 {
46 if (!valid_)
47 {
48 throw std::domain_error("Bad access to uninitialized frame");
49 }
50
51 return length_;
52 }
53
44 const std::vector<part>& getParts() const 54 const std::vector<part>& getParts() const
45 { 55 {
46 if (!valid_) 56 if (!valid_)
@@ -61,6 +71,8 @@ namespace verbly {
61 71
62 static const field id; 72 static const field id;
63 73
74 static const field length;
75
64 operator filter() const 76 operator filter() const
65 { 77 {
66 if (!valid_) 78 if (!valid_)
@@ -73,13 +85,18 @@ namespace verbly {
73 85
74 // Relationships to other objects 86 // Relationships to other objects
75 87
76 static const field group; 88 static const field word;
89
90 static field part();
91 static field part(int index);
77 92
78 private: 93 private:
79 bool valid_ = false; 94 bool valid_ = false;
80 95
81 int id_; 96 int id_;
82 std::vector<part> parts_; 97 int groupId_;
98 int length_;
99 std::vector<class part> parts_;
83 100
84 const database* db_; 101 const database* db_;
85 102
diff --git a/lib/group.cpp b/lib/group.cpp deleted file mode 100644 index d5790e9..0000000 --- a/lib/group.cpp +++ /dev/null
@@ -1,61 +0,0 @@
1#include "group.h"
2#include <sqlite3.h>
3#include <json.hpp>
4#include "database.h"
5#include "query.h"
6
7namespace verbly {
8
9 const object group::objectType = object::group;
10
11 const std::list<std::string> group::select = {"group_id", "data"};
12
13 const field group::id = field::integerField(object::group, "group_id");
14
15 const field group::frame = field::joinThrough(object::group, "group_id", object::frame, "groups_frames", "frame_id");
16 const field group::word = field::joinField(object::group, "group_id", object::word);
17
18 group::group(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true)
19 {
20 id_ = sqlite3_column_int(row, 0);
21
22 std::string rolesJsonStr(reinterpret_cast<const char*>(sqlite3_column_blob(row, 1)));
23 nlohmann::json rolesJson = nlohmann::json::parse(std::move(rolesJsonStr));
24 for (const nlohmann::json& roleJson : rolesJson)
25 {
26 std::string roleName = roleJson["type"];
27 selrestr roleSelrestr;
28
29 if (roleJson.find("selrestrs") != roleJson.end())
30 {
31 roleSelrestr = selrestr(roleJson["selrestrs"]);
32 }
33
34 roles_[roleName] = role(roleName, std::move(roleSelrestr));
35 }
36 }
37
38 const std::vector<frame>& group::getFrames() const
39 {
40 if (!valid_)
41 {
42 throw std::domain_error("Bad access to uninitialized group");
43 }
44
45 if (!initializedFrames_)
46 {
47 frames_ = db_->frames(frame::group %= *this, false, -1).all();
48
49 initializedFrames_ = true;
50 }
51
52 return frames_;
53 }
54
55 const role& group::getRole(std::string roleName) const
56 {
57 return roles_.at(roleName);
58 }
59
60};
61
diff --git a/lib/group.h b/lib/group.h deleted file mode 100644 index fe62d39..0000000 --- a/lib/group.h +++ /dev/null
@@ -1,91 +0,0 @@
1#ifndef GROUP_H_BD6933C0
2#define GROUP_H_BD6933C0
3
4#include <stdexcept>
5#include <list>
6#include <vector>
7#include "field.h"
8#include "filter.h"
9#include "frame.h"
10#include "role.h"
11
12struct sqlite3_stmt;
13
14namespace verbly {
15
16 class database;
17
18 class group {
19 public:
20
21 // Default constructor
22
23 group() = default;
24
25 // Construct from database
26
27 group(const database& db, sqlite3_stmt* row);
28
29 // Accessors
30
31 operator bool() const
32 {
33 return valid_;
34 }
35
36 int getId() const
37 {
38 if (!valid_)
39 {
40 throw std::domain_error("Bad access to uninitialized group");
41 }
42
43 return id_;
44 }
45
46 const std::vector<frame>& getFrames() const;
47
48 const role& getRole(std::string roleName) const;
49
50 // Type info
51
52 static const object objectType;
53
54 static const std::list<std::string> select;
55
56 // Query fields
57
58 static const field id;
59
60 operator filter() const
61 {
62 if (!valid_)
63 {
64 throw std::domain_error("Bad access to uninitialized group");
65 }
66
67 return (id == id_);
68 }
69
70 // Relationships to other objects
71
72 static const field frame;
73
74 static const field word;
75
76 private:
77 bool valid_ = false;
78
79 int id_;
80 std::map<std::string, role> roles_;
81
82 const database* db_;
83
84 mutable bool initializedFrames_ = false;
85 mutable std::vector<class frame> frames_;
86
87 };
88
89};
90
91#endif /* end of include guard: GROUP_H_BD6933C0 */
diff --git a/lib/lemma.cpp b/lib/lemma.cpp index 1601460..0c6e99e 100644 --- a/lib/lemma.cpp +++ b/lib/lemma.cpp
@@ -10,20 +10,13 @@ namespace verbly {
10 const std::list<std::string> lemma::select = {"lemma_id"}; 10 const std::list<std::string> lemma::select = {"lemma_id"};
11 11
12 const field lemma::id = field::integerField(object::lemma, "lemma_id"); 12 const field lemma::id = field::integerField(object::lemma, "lemma_id");
13
14 const field lemma::word = field::joinField(object::lemma, "lemma_id", object::word);
15
16 const field lemma::formJoin = field::joinField(object::lemma, "form_id", object::form);
17 const field lemma::inflectionCategory = field::integerField(object::lemma, "category"); 13 const field lemma::inflectionCategory = field::integerField(object::lemma, "category");
18 14
19 filter operator%=(lemma::inflection_field check, filter joinCondition) 15 const field lemma::word = field::joinField(object::lemma, "lemma_id", object::word);
20 {
21 return (lemma::formJoin %= joinCondition) && (lemma::inflectionCategory == check.getCategory());
22 }
23 16
24 lemma::inflection_field::operator filter() const 17 field lemma::form(inflection category)
25 { 18 {
26 return (lemma::inflectionCategory == category_); 19 return field::joinWhere(object::lemma, "form_id", object::form, inflectionCategory, static_cast<int>(category));
27 } 20 }
28 21
29 lemma::lemma(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) 22 lemma::lemma(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true)
@@ -68,7 +61,7 @@ namespace verbly {
68 61
69 void lemma::initializeForm(inflection infl) const 62 void lemma::initializeForm(inflection infl) const
70 { 63 {
71 forms_[infl] = db_->forms(form::lemma(infl) %= *this, false, -1).all(); 64 forms_[infl] = db_->forms(form::lemma %= ((inflectionCategory == infl) && *this), verbly::form::id, -1).all();
72 } 65 }
73 66
74}; 67};
diff --git a/lib/lemma.h b/lib/lemma.h index 407fa3c..56cfc56 100644 --- a/lib/lemma.h +++ b/lib/lemma.h
@@ -74,31 +74,7 @@ namespace verbly {
74 74
75 static const field word; 75 static const field word;
76 76
77 class inflection_field { 77 static field form(inflection category);
78 public:
79
80 inflection_field(inflection category) : category_(category)
81 {
82 }
83
84 const inflection getCategory() const
85 {
86 return category_;
87 }
88
89 operator filter() const;
90
91 private:
92
93 const inflection category_;
94 };
95
96 static const inflection_field form(inflection category)
97 {
98 return inflection_field(category);
99 }
100
101 friend filter operator%=(lemma::inflection_field check, filter joinCondition);
102 78
103 private: 79 private:
104 80
@@ -112,7 +88,6 @@ namespace verbly {
112 88
113 const database* db_; 89 const database* db_;
114 90
115 static const field formJoin;
116 static const field inflectionCategory; 91 static const field inflectionCategory;
117 92
118 }; 93 };
diff --git a/lib/order.h b/lib/order.h new file mode 100644 index 0000000..d2f0f92 --- /dev/null +++ b/lib/order.h
@@ -0,0 +1,69 @@
1#ifndef ORDER_H_0EC669D5
2#define ORDER_H_0EC669D5
3
4#include <stdexcept>
5#include "field.h"
6
7namespace verbly {
8
9 class order {
10 public:
11 enum class type {
12 random,
13 field
14 };
15
16 // Type
17
18 type getType() const
19 {
20 return type_;
21 }
22
23 // Random
24
25 order() : type_(type::random)
26 {
27 }
28
29 // Field
30
31 order(
32 field arg,
33 bool asc = true) :
34 type_(type::field),
35 sortField_(std::move(arg)),
36 ascending_(asc)
37 {
38 }
39
40 field getSortField() const
41 {
42 if (type_ != type::field)
43 {
44 throw std::domain_error("Invalid access to non-field order");
45 }
46
47 return sortField_;
48 }
49
50 bool isAscending() const
51 {
52 if (type_ != type::field)
53 {
54 throw std::domain_error("Invalid access to non-field order");
55 }
56
57 return ascending_;
58 }
59
60 private:
61 type type_;
62 field sortField_;
63 bool ascending_;
64
65 };
66
67};
68
69#endif /* end of include guard: ORDER_H_0EC669D5 */
diff --git a/lib/part.cpp b/lib/part.cpp index e66d151..1fbb24d 100644 --- a/lib/part.cpp +++ b/lib/part.cpp
@@ -1,12 +1,30 @@
1#include "part.h" 1#include "part.h"
2#include <stdexcept> 2#include <stdexcept>
3#include <sqlite3.h>
3#include "selrestr.h" 4#include "selrestr.h"
5#include "database.h"
4 6
5namespace verbly { 7namespace verbly {
6 8
9 const object part::objectType = object::part;
10
11 const std::list<std::string> part::select = {"part_id", "frame_id", "part_index", "type", "role", "selrestrs", "prepositions", "preposition_literality", "literal_value"};
12
13 const field part::index = field::integerField(object::part, "part_index");
14 const field part::type = field::integerField(object::part, "type");
15
16 const field part::role = field::stringField(object::part, "role", true);
17
18 const field part::frame = field::joinField(object::part, "frame_id", object::frame);
19
20 const field part::synrestr_field::synrestrJoin = field::joinField(object::part, "part_id", "synrestrs");
21 const field part::synrestr_field::synrestrField = field::stringField("synrestrs", "synrestr");
22
23 const part::synrestr_field part::synrestr = {};
24
7 part part::createNounPhrase(std::string role, selrestr selrestrs, std::set<std::string> synrestrs) 25 part part::createNounPhrase(std::string role, selrestr selrestrs, std::set<std::string> synrestrs)
8 { 26 {
9 part p(type::noun_phrase); 27 part p(part_type::noun_phrase);
10 28
11 new(&p.noun_phrase_.role) std::string(std::move(role)); 29 new(&p.noun_phrase_.role) std::string(std::move(role));
12 new(&p.noun_phrase_.selrestrs) selrestr(std::move(selrestrs)); 30 new(&p.noun_phrase_.selrestrs) selrestr(std::move(selrestrs));
@@ -17,12 +35,12 @@ namespace verbly {
17 35
18 part part::createVerb() 36 part part::createVerb()
19 { 37 {
20 return part(type::verb); 38 return part(part_type::verb);
21 } 39 }
22 40
23 part part::createPreposition(std::vector<std::string> choices, bool literal) 41 part part::createPreposition(std::vector<std::string> choices, bool literal)
24 { 42 {
25 part p(type::preposition); 43 part p(part_type::preposition);
26 44
27 new(&p.preposition_.choices) std::vector<std::string>(std::move(choices)); 45 new(&p.preposition_.choices) std::vector<std::string>(std::move(choices));
28 p.preposition_.literal = literal; 46 p.preposition_.literal = literal;
@@ -32,30 +50,79 @@ namespace verbly {
32 50
33 part part::createAdjective() 51 part part::createAdjective()
34 { 52 {
35 return part(type::adjective); 53 return part(part_type::adjective);
36 } 54 }
37 55
38 part part::createAdverb() 56 part part::createAdverb()
39 { 57 {
40 return part(type::adverb); 58 return part(part_type::adverb);
41 } 59 }
42 60
43 part part::createLiteral(std::string value) 61 part part::createLiteral(std::string value)
44 { 62 {
45 part p(type::literal); 63 part p(part_type::literal);
46 64
47 new(&p.literal_) std::string(std::move(value)); 65 new(&p.literal_) std::string(std::move(value));
48 66
49 return p; 67 return p;
50 } 68 }
51 69
70 part::part(const database& db, sqlite3_stmt* row)
71 {
72 int id = sqlite3_column_int(row, 0);
73
74 type_ = static_cast<part_type>(sqlite3_column_int(row, 3));
75
76 switch (type_)
77 {
78 case part_type::noun_phrase:
79 {
80 new(&noun_phrase_.role) std::string(reinterpret_cast<const char*>(sqlite3_column_blob(row, 4)));
81 new(&noun_phrase_.selrestrs) selrestr(nlohmann::json::parse(reinterpret_cast<const char*>(sqlite3_column_blob(row, 5))));
82 new(&noun_phrase_.synrestrs) std::set<std::string>(db.synrestrs(id));
83
84 break;
85 }
86
87 case part_type::preposition:
88 {
89 new(&preposition_.choices) std::vector<std::string>();
90 preposition_.literal = (sqlite3_column_int(row, 7) == 1);
91
92 std::string choicesJsonStr(reinterpret_cast<const char*>(sqlite3_column_blob(row, 6)));
93 nlohmann::json choicesJson = nlohmann::json::parse(std::move(choicesJsonStr));
94 for (const nlohmann::json& choiceJson : choicesJson)
95 {
96 preposition_.choices.push_back(choiceJson.get<std::string>());
97 }
98
99 break;
100 }
101
102 case part_type::literal:
103 {
104 new(&literal_) std::string(reinterpret_cast<const char*>(sqlite3_column_blob(row, 8)));
105
106 break;
107 }
108
109 case part_type::verb:
110 case part_type::adjective:
111 case part_type::adverb:
112 case part_type::invalid:
113 {
114 break;
115 }
116 }
117 }
118
52 part::part(const part& other) 119 part::part(const part& other)
53 { 120 {
54 type_ = other.type_; 121 type_ = other.type_;
55 122
56 switch (type_) 123 switch (type_)
57 { 124 {
58 case type::noun_phrase: 125 case part_type::noun_phrase:
59 { 126 {
60 new(&noun_phrase_.role) std::string(other.noun_phrase_.role); 127 new(&noun_phrase_.role) std::string(other.noun_phrase_.role);
61 new(&noun_phrase_.selrestrs) selrestr(other.noun_phrase_.selrestrs); 128 new(&noun_phrase_.selrestrs) selrestr(other.noun_phrase_.selrestrs);
@@ -64,7 +131,7 @@ namespace verbly {
64 break; 131 break;
65 } 132 }
66 133
67 case type::preposition: 134 case part_type::preposition:
68 { 135 {
69 new(&preposition_.choices) std::vector<std::string>(other.preposition_.choices); 136 new(&preposition_.choices) std::vector<std::string>(other.preposition_.choices);
70 preposition_.literal = other.preposition_.literal; 137 preposition_.literal = other.preposition_.literal;
@@ -72,17 +139,17 @@ namespace verbly {
72 break; 139 break;
73 } 140 }
74 141
75 case type::literal: 142 case part_type::literal:
76 { 143 {
77 new(&literal_) std::string(other.literal_); 144 new(&literal_) std::string(other.literal_);
78 145
79 break; 146 break;
80 } 147 }
81 148
82 case type::verb: 149 case part_type::verb:
83 case type::adjective: 150 case part_type::adjective:
84 case type::adverb: 151 case part_type::adverb:
85 case type::invalid: 152 case part_type::invalid:
86 { 153 {
87 break; 154 break;
88 } 155 }
@@ -103,7 +170,7 @@ namespace verbly {
103 170
104 void swap(part& first, part& second) 171 void swap(part& first, part& second)
105 { 172 {
106 using type = part::type; 173 using type = part_type;
107 174
108 type tempType = first.type_; 175 type tempType = first.type_;
109 std::string tempRole; 176 std::string tempRole;
@@ -231,7 +298,7 @@ namespace verbly {
231 { 298 {
232 switch (type_) 299 switch (type_)
233 { 300 {
234 case type::noun_phrase: 301 case part_type::noun_phrase:
235 { 302 {
236 using string_type = std::string; 303 using string_type = std::string;
237 using set_type = std::set<std::string>; 304 using set_type = std::set<std::string>;
@@ -243,7 +310,7 @@ namespace verbly {
243 break; 310 break;
244 } 311 }
245 312
246 case type::preposition: 313 case part_type::preposition:
247 { 314 {
248 using vector_type = std::vector<std::string>; 315 using vector_type = std::vector<std::string>;
249 316
@@ -252,7 +319,7 @@ namespace verbly {
252 break; 319 break;
253 } 320 }
254 321
255 case type::literal: 322 case part_type::literal:
256 { 323 {
257 using string_type = std::string; 324 using string_type = std::string;
258 325
@@ -261,10 +328,10 @@ namespace verbly {
261 break; 328 break;
262 } 329 }
263 330
264 case type::verb: 331 case part_type::verb:
265 case type::adjective: 332 case part_type::adjective:
266 case type::adverb: 333 case part_type::adverb:
267 case type::invalid: 334 case part_type::invalid:
268 { 335 {
269 break; 336 break;
270 } 337 }
@@ -273,7 +340,7 @@ namespace verbly {
273 340
274 std::string part::getNounRole() const 341 std::string part::getNounRole() const
275 { 342 {
276 if (type_ == type::noun_phrase) 343 if (type_ == part_type::noun_phrase)
277 { 344 {
278 return noun_phrase_.role; 345 return noun_phrase_.role;
279 } else { 346 } else {
@@ -283,7 +350,7 @@ namespace verbly {
283 350
284 selrestr part::getNounSelrestrs() const 351 selrestr part::getNounSelrestrs() const
285 { 352 {
286 if (type_ == type::noun_phrase) 353 if (type_ == part_type::noun_phrase)
287 { 354 {
288 return noun_phrase_.selrestrs; 355 return noun_phrase_.selrestrs;
289 } else { 356 } else {
@@ -293,7 +360,7 @@ namespace verbly {
293 360
294 std::set<std::string> part::getNounSynrestrs() const 361 std::set<std::string> part::getNounSynrestrs() const
295 { 362 {
296 if (type_ == type::noun_phrase) 363 if (type_ == part_type::noun_phrase)
297 { 364 {
298 return noun_phrase_.synrestrs; 365 return noun_phrase_.synrestrs;
299 } else { 366 } else {
@@ -303,7 +370,7 @@ namespace verbly {
303 370
304 bool part::nounHasSynrestr(std::string synrestr) const 371 bool part::nounHasSynrestr(std::string synrestr) const
305 { 372 {
306 if (type_ != type::noun_phrase) 373 if (type_ != part_type::noun_phrase)
307 { 374 {
308 throw std::domain_error("part::nounHasSynrestr is only valid for noun phrase parts"); 375 throw std::domain_error("part::nounHasSynrestr is only valid for noun phrase parts");
309 } 376 }
@@ -313,7 +380,7 @@ namespace verbly {
313 380
314 std::vector<std::string> part::getPrepositionChoices() const 381 std::vector<std::string> part::getPrepositionChoices() const
315 { 382 {
316 if (type_ == type::preposition) 383 if (type_ == part_type::preposition)
317 { 384 {
318 return preposition_.choices; 385 return preposition_.choices;
319 } else { 386 } else {
@@ -323,7 +390,7 @@ namespace verbly {
323 390
324 bool part::isPrepositionLiteral() const 391 bool part::isPrepositionLiteral() const
325 { 392 {
326 if (type_ == type::preposition) 393 if (type_ == part_type::preposition)
327 { 394 {
328 return preposition_.literal; 395 return preposition_.literal;
329 } else { 396 } else {
@@ -333,7 +400,7 @@ namespace verbly {
333 400
334 std::string part::getLiteralValue() const 401 std::string part::getLiteralValue() const
335 { 402 {
336 if (type_ == type::literal) 403 if (type_ == part_type::literal)
337 { 404 {
338 return literal_; 405 return literal_;
339 } else { 406 } else {
@@ -341,4 +408,9 @@ namespace verbly {
341 } 408 }
342 } 409 }
343 410
411 filter part::synrestr_field::operator%=(std::string synrestr) const
412 {
413 return (synrestrJoin %= (synrestrField == synrestr));
414 }
415
344}; 416};
diff --git a/lib/part.h b/lib/part.h index 3a15638..9a01312 100644 --- a/lib/part.h +++ b/lib/part.h
@@ -4,21 +4,20 @@
4#include <string> 4#include <string>
5#include <vector> 5#include <vector>
6#include <set> 6#include <set>
7#include <list>
7#include "selrestr.h" 8#include "selrestr.h"
9#include "field.h"
10#include "filter.h"
11#include "enums.h"
12
13struct sqlite3_stmt;
8 14
9namespace verbly { 15namespace verbly {
10 16
17 class database;
18
11 class part { 19 class part {
12 public: 20 public:
13 enum class type {
14 invalid = -1,
15 noun_phrase = 0,
16 verb = 1,
17 preposition = 2,
18 adjective = 3,
19 adverb = 4,
20 literal = 5
21 };
22 21
23 // Static factories 22 // Static factories
24 23
@@ -40,6 +39,10 @@ namespace verbly {
40 { 39 {
41 } 40 }
42 41
42 // Construct from database
43
44 part(const database& db, sqlite3_stmt* row);
45
43 // Copy and move constructors 46 // Copy and move constructors
44 47
45 part(const part& other); 48 part(const part& other);
@@ -60,7 +63,12 @@ namespace verbly {
60 63
61 // General accessors 64 // General accessors
62 65
63 type getType() const 66 operator bool() const
67 {
68 return (type_ != part_type::invalid);
69 }
70
71 part_type getType() const
64 { 72 {
65 return type_; 73 return type_;
66 } 74 }
@@ -85,11 +93,43 @@ namespace verbly {
85 93
86 std::string getLiteralValue() const; 94 std::string getLiteralValue() const;
87 95
96 // Type info
97
98 static const object objectType;
99
100 static const std::list<std::string> select;
101
102 // Query fields
103
104 static const field index;
105 static const field type;
106
107 static const field role;
108
109 // Relationships to other objects
110
111 static const field frame;
112
113 // Noun synrestr relationship
114
115 class synrestr_field {
116 public:
117
118 filter operator%=(std::string synrestr) const;
119
120 private:
121
122 static const field synrestrJoin;
123 static const field synrestrField;
124 };
125
126 static const synrestr_field synrestr;
127
88 private: 128 private:
89 129
90 // Private constructors 130 // Private constructors
91 131
92 part(type t) : type_(t) 132 part(part_type t) : type_(t)
93 { 133 {
94 } 134 }
95 135
@@ -108,7 +148,7 @@ namespace verbly {
108 std::string literal_; 148 std::string literal_;
109 }; 149 };
110 150
111 type type_ = type::invalid; 151 part_type type_ = part_type::invalid;
112 152
113 }; 153 };
114 154
diff --git a/lib/query.h b/lib/query.h index 214bf99..75651f6 100644 --- a/lib/query.h +++ b/lib/query.h
@@ -9,6 +9,7 @@
9#include <iostream> 9#include <iostream>
10#include "statement.h" 10#include "statement.h"
11#include "binding.h" 11#include "binding.h"
12#include "order.h"
12 13
13namespace verbly { 14namespace verbly {
14 15
@@ -24,11 +25,17 @@ namespace verbly {
24 class query { 25 class query {
25 public: 26 public:
26 27
27 query(const database& db, sqlite3* ppdb, filter queryFilter, bool random, int limit) : db_(&db) 28 query(const database& db, sqlite3* ppdb, filter queryFilter, order sortOrder, int limit) : db_(&db)
28 { 29 {
30 if ((sortOrder.getType() == order::type::field)
31 && (sortOrder.getSortField().getObject() != Object::objectType))
32 {
33 throw std::invalid_argument("Can only sort query by a field in the result table");
34 }
35
29 statement stmt(Object::objectType, std::move(queryFilter)); 36 statement stmt(Object::objectType, std::move(queryFilter));
30 37
31 std::string queryString = stmt.getQueryString(Object::select, random, limit); 38 std::string queryString = stmt.getQueryString(Object::select, std::move(sortOrder), limit);
32 std::list<binding> bindings = stmt.getBindings(); 39 std::list<binding> bindings = stmt.getBindings();
33 40
34 if (sqlite3_prepare_v2(ppdb, queryString.c_str(), queryString.length(), &ppstmt_, NULL) != SQLITE_OK) 41 if (sqlite3_prepare_v2(ppdb, queryString.c_str(), queryString.length(), &ppstmt_, NULL) != SQLITE_OK)
diff --git a/lib/role.h b/lib/role.h deleted file mode 100644 index 4884ef3..0000000 --- a/lib/role.h +++ /dev/null
@@ -1,60 +0,0 @@
1#ifndef ROLE_H_249F9A9C
2#define ROLE_H_249F9A9C
3
4#include <stdexcept>
5#include <string>
6#include "../lib/selrestr.h"
7
8namespace verbly {
9
10 class role {
11 public:
12
13 // Default constructor
14
15 role() = default;
16
17 // Constructor
18
19 role(
20 std::string name,
21 selrestr selrestrs = {}) :
22 valid_(true),
23 name_(name),
24 selrestrs_(selrestrs)
25 {
26 }
27
28 // Accessors
29
30 const std::string& getName() const
31 {
32 if (!valid_)
33 {
34 throw std::domain_error("Bad access to invalid role");
35 }
36
37 return name_;
38 }
39
40 const selrestr& getSelrestrs() const
41 {
42 if (!valid_)
43 {
44 throw std::domain_error("Bad access to invalid role");
45 }
46
47 return selrestrs_;
48 }
49
50 private:
51
52 bool valid_ = false;
53 std::string name_;
54 selrestr selrestrs_;
55
56 };
57
58};
59
60#endif /* end of include guard: ROLE_H_249F9A9C */
diff --git a/lib/statement.cpp b/lib/statement.cpp index 846b9de..1512aa5 100644 --- a/lib/statement.cpp +++ b/lib/statement.cpp
@@ -5,11 +5,12 @@
5#include "util.h" 5#include "util.h"
6#include "notion.h" 6#include "notion.h"
7#include "word.h" 7#include "word.h"
8#include "group.h"
9#include "frame.h" 8#include "frame.h"
9#include "part.h"
10#include "lemma.h" 10#include "lemma.h"
11#include "form.h" 11#include "form.h"
12#include "pronunciation.h" 12#include "pronunciation.h"
13#include "order.h"
13 14
14namespace verbly { 15namespace verbly {
15 16
@@ -20,7 +21,7 @@ namespace verbly {
20 { 21 {
21 } 22 }
22 23
23 std::string statement::getQueryString(std::list<std::string> select, bool random, int limit) const 24 std::string statement::getQueryString(std::list<std::string> select, order sortOrder, int limit, bool debug) const
24 { 25 {
25 std::stringstream queryStream; 26 std::stringstream queryStream;
26 27
@@ -49,7 +50,7 @@ namespace verbly {
49 if (cte.getCondition().getType() != condition::type::empty) 50 if (cte.getCondition().getType() != condition::type::empty)
50 { 51 {
51 cteStream << " WHERE "; 52 cteStream << " WHERE ";
52 cteStream << cte.getCondition().toSql(); 53 cteStream << cte.getCondition().flatten().toSql(true, debug);
53 } 54 }
54 55
55 if (cte.isRecursive()) 56 if (cte.isRecursive())
@@ -101,12 +102,28 @@ namespace verbly {
101 if (topCondition_.getType() != condition::type::empty) 102 if (topCondition_.getType() != condition::type::empty)
102 { 103 {
103 queryStream << " WHERE "; 104 queryStream << " WHERE ";
104 queryStream << topCondition_.toSql(); 105 queryStream << topCondition_.flatten().toSql(true, debug);
105 } 106 }
106 107
107 if (random) 108 queryStream << " ORDER BY ";
109
110 switch (sortOrder.getType())
108 { 111 {
109 queryStream << " ORDER BY RANDOM()"; 112 case order::type::random:
113 {
114 queryStream << "RANDOM()";
115
116 break;
117 }
118
119 case order::type::field:
120 {
121 queryStream << topTable_;
122 queryStream << ".";
123 queryStream << sortOrder.getSortField().getColumn();
124
125 break;
126 }
110 } 127 }
111 128
112 if (limit > 0) 129 if (limit > 0)
@@ -260,6 +277,7 @@ namespace verbly {
260 } 277 }
261 278
262 case field::type::join: 279 case field::type::join:
280 case field::type::join_where:
263 { 281 {
264 // First, figure out what table we need to join against. 282 // First, figure out what table we need to join against.
265 std::string joinTableName; 283 std::string joinTableName;
@@ -269,13 +287,22 @@ namespace verbly {
269 } else { 287 } else {
270 joinTableName = getTableForContext(clause.getField().getJoinObject()); 288 joinTableName = getTableForContext(clause.getField().getJoinObject());
271 } 289 }
290
291 filter joinCondition = clause.getJoinCondition();
292
293 // If this is a condition join, we need to add the field join
294 // condition to the clause.
295 if (clause.getField().getType() == field::type::join_where)
296 {
297 joinCondition &= (clause.getField().getConditionField() == clause.getField().getConditionValue());
298 }
272 299
273 // Recursively parse the subquery, and therefore obtain an 300 // Recursively parse the subquery, and therefore obtain an
274 // instantiated table to join against, as well as any joins or CTEs 301 // instantiated table to join against, as well as any joins or CTEs
275 // that the subquery may require to function. 302 // that the subquery may require to function.
276 statement joinStmt( 303 statement joinStmt(
277 joinTableName, 304 joinTableName,
278 clause.getJoinCondition().normalize(clause.getField().getJoinObject()), 305 std::move(joinCondition).normalize(clause.getField().getJoinObject()),
279 nextTableId_, 306 nextTableId_,
280 nextWithId_); 307 nextWithId_);
281 308
@@ -801,7 +828,7 @@ namespace verbly {
801 new(&singleton_.value_) binding(std::move(value)); 828 new(&singleton_.value_) binding(std::move(value));
802 } 829 }
803 830
804 std::string statement::condition::toSql() const 831 std::string statement::condition::toSql(bool toplevel, bool debug) const
805 { 832 {
806 switch (type_) 833 switch (type_)
807 { 834 {
@@ -816,42 +843,92 @@ namespace verbly {
816 { 843 {
817 case comparison::equals: 844 case comparison::equals:
818 { 845 {
819 return singleton_.table_ + "." + singleton_.column_ + " = ?"; 846 if (debug)
847 {
848 if (singleton_.value_.getType() == binding::type::string)
849 {
850 return singleton_.table_ + "." + singleton_.column_ + " = \"" + singleton_.value_.getString() + "\"";
851 } else {
852 return singleton_.table_ + "." + singleton_.column_ + " = " + std::to_string(singleton_.value_.getInteger());
853 }
854 } else {
855 return singleton_.table_ + "." + singleton_.column_ + " = ?";
856 }
820 } 857 }
821 858
822 case comparison::does_not_equal: 859 case comparison::does_not_equal:
823 { 860 {
824 return singleton_.table_ + "." + singleton_.column_ + " != ?"; 861 if (debug)
862 {
863 if (singleton_.value_.getType() == binding::type::string)
864 {
865 return singleton_.table_ + "." + singleton_.column_ + " != \"" + singleton_.value_.getString() + "\"";
866 } else {
867 return singleton_.table_ + "." + singleton_.column_ + " != " + std::to_string(singleton_.value_.getInteger());
868 }
869 } else {
870 return singleton_.table_ + "." + singleton_.column_ + " != ?";
871 }
825 } 872 }
826 873
827 case comparison::is_greater_than: 874 case comparison::is_greater_than:
828 { 875 {
829 return singleton_.table_ + "." + singleton_.column_ + " > ?"; 876 if (debug)
877 {
878 return singleton_.table_ + "." + singleton_.column_ + " > " + std::to_string(singleton_.value_.getInteger());
879 } else {
880 return singleton_.table_ + "." + singleton_.column_ + " > ?";
881 }
830 } 882 }
831 883
832 case comparison::is_at_most: 884 case comparison::is_at_most:
833 { 885 {
834 return singleton_.table_ + "." + singleton_.column_ + " <= ?"; 886 if (debug)
887 {
888 return singleton_.table_ + "." + singleton_.column_ + " <= " + std::to_string(singleton_.value_.getInteger());
889 } else {
890 return singleton_.table_ + "." + singleton_.column_ + " <= ?";
891 }
835 } 892 }
836 893
837 case comparison::is_less_than: 894 case comparison::is_less_than:
838 { 895 {
839 return singleton_.table_ + "." + singleton_.column_ + " < ?"; 896 if (debug)
897 {
898 return singleton_.table_ + "." + singleton_.column_ + " < " + std::to_string(singleton_.value_.getInteger());
899 } else {
900 return singleton_.table_ + "." + singleton_.column_ + " < ?";
901 }
840 } 902 }
841 903
842 case comparison::is_at_least: 904 case comparison::is_at_least:
843 { 905 {
844 return singleton_.table_ + "." + singleton_.column_ + " >= ?"; 906 if (debug)
907 {
908 return singleton_.table_ + "." + singleton_.column_ + " >= " + std::to_string(singleton_.value_.getInteger());
909 } else {
910 return singleton_.table_ + "." + singleton_.column_ + " >= ?";
911 }
845 } 912 }
846 913
847 case comparison::is_like: 914 case comparison::is_like:
848 { 915 {
849 return singleton_.table_ + "." + singleton_.column_ + " LIKE ?"; 916 if (debug)
917 {
918 return singleton_.table_ + "." + singleton_.column_ + " LIKE \"" + singleton_.value_.getString() + "\"";
919 } else {
920 return singleton_.table_ + "." + singleton_.column_ + " LIKE ?";
921 }
850 } 922 }
851 923
852 case comparison::is_not_like: 924 case comparison::is_not_like:
853 { 925 {
854 return singleton_.table_ + "." + singleton_.column_ + " NOT LIKE ?"; 926 if (debug)
927 {
928 return singleton_.table_ + "." + singleton_.column_ + " NOT LIKE \"" + singleton_.value_.getString() + "\"";
929 } else {
930 return singleton_.table_ + "." + singleton_.column_ + " NOT LIKE ?";
931 }
855 } 932 }
856 933
857 case comparison::is_not_null: 934 case comparison::is_not_null:
@@ -871,10 +948,25 @@ namespace verbly {
871 std::list<std::string> clauses; 948 std::list<std::string> clauses;
872 for (const condition& cond : group_.children_) 949 for (const condition& cond : group_.children_)
873 { 950 {
874 clauses.push_back(cond.toSql()); 951 clauses.push_back(cond.toSql(false, debug));
875 } 952 }
876 953
877 return implode(std::begin(clauses), std::end(clauses), group_.orlogic_ ? " OR " : " AND "); 954 if (clauses.empty())
955 {
956 return "";
957 } else if (clauses.size() == 1)
958 {
959 return clauses.front();
960 } else {
961 std::string result = implode(std::begin(clauses), std::end(clauses), group_.orlogic_ ? " OR " : " AND ");
962
963 if (toplevel)
964 {
965 return result;
966 } else {
967 return "(" + result + ")";
968 }
969 }
878 } 970 }
879 } 971 }
880 } 972 }
@@ -988,5 +1080,39 @@ namespace verbly {
988 throw std::domain_error("Cannot get children of non-group condition"); 1080 throw std::domain_error("Cannot get children of non-group condition");
989 } 1081 }
990 } 1082 }
1083
1084 statement::condition statement::condition::flatten() const
1085 {
1086 switch (type_)
1087 {
1088 case type::empty:
1089 case type::singleton:
1090 {
1091 return *this;
1092 }
1093
1094 case type::group:
1095 {
1096 condition result(group_.orlogic_);
1097
1098 for (const condition& child : group_.children_)
1099 {
1100 condition newChild = child.flatten();
1101
1102 if ((newChild.type_ == type::group) && (newChild.group_.orlogic_ == group_.orlogic_))
1103 {
1104 for (condition subChild : std::move(newChild.group_.children_))
1105 {
1106 result += std::move(subChild);
1107 }
1108 } else {
1109 result += std::move(newChild);
1110 }
1111 }
1112
1113 return result;
1114 }
1115 }
1116 }
991 1117
992}; 1118};
diff --git a/lib/statement.h b/lib/statement.h index aa56568..15c4ac3 100644 --- a/lib/statement.h +++ b/lib/statement.h
@@ -13,13 +13,14 @@
13namespace verbly { 13namespace verbly {
14 14
15 class filter; 15 class filter;
16 class order;
16 17
17 class statement { 18 class statement {
18 public: 19 public:
19 20
20 statement(object context, filter queryFilter); 21 statement(object context, filter queryFilter);
21 22
22 std::string getQueryString(std::list<std::string> select, bool random, int limit) const; 23 std::string getQueryString(std::list<std::string> select, order sortOrder, int limit, bool debug = false) const;
23 24
24 std::list<binding> getBindings() const; 25 std::list<binding> getBindings() const;
25 26
@@ -153,10 +154,12 @@ namespace verbly {
153 154
154 // Utility 155 // Utility
155 156
156 std::string toSql() const; 157 std::string toSql(bool toplevel, bool debug = false) const;
157 158
158 std::list<binding> flattenBindings() const; 159 std::list<binding> flattenBindings() const;
159 160
161 condition flatten() const;
162
160 private: 163 private:
161 union { 164 union {
162 struct { 165 struct {
@@ -246,8 +249,8 @@ namespace verbly {
246 { 249 {
247 return (context == object::notion) ? "notions" 250 return (context == object::notion) ? "notions"
248 : (context == object::word) ? "words" 251 : (context == object::word) ? "words"
249 : (context == object::group) ? "groups"
250 : (context == object::frame) ? "frames" 252 : (context == object::frame) ? "frames"
253 : (context == object::part) ? "parts"
251 : (context == object::lemma) ? "lemmas_forms" 254 : (context == object::lemma) ? "lemmas_forms"
252 : (context == object::form) ? "forms" 255 : (context == object::form) ? "forms"
253 : (context == object::pronunciation) ? "pronunciations" 256 : (context == object::pronunciation) ? "pronunciations"
diff --git a/lib/verbly.h b/lib/verbly.h index d8875b3..112907b 100644 --- a/lib/verbly.h +++ b/lib/verbly.h
@@ -6,16 +6,15 @@
6#include "filter.h" 6#include "filter.h"
7#include "field.h" 7#include "field.h"
8#include "query.h" 8#include "query.h"
9#include "order.h"
9#include "notion.h" 10#include "notion.h"
10#include "word.h" 11#include "word.h"
11#include "group.h"
12#include "frame.h" 12#include "frame.h"
13#include "part.h"
13#include "lemma.h" 14#include "lemma.h"
14#include "form.h" 15#include "form.h"
15#include "pronunciation.h" 16#include "pronunciation.h"
16#include "token.h" 17#include "token.h"
17#include "selrestr.h" 18#include "selrestr.h"
18#include "part.h"
19#include "role.h"
20 19
21#endif /* end of include guard: VERBLY_H_5B39CE50 */ 20#endif /* end of include guard: VERBLY_H_5B39CE50 */
diff --git a/lib/word.cpp b/lib/word.cpp index a928659..90eab1d 100644 --- a/lib/word.cpp +++ b/lib/word.cpp
@@ -17,7 +17,7 @@ namespace verbly {
17 17
18 const field word::notion = field::joinField(object::word, "notion_id", object::notion); 18 const field word::notion = field::joinField(object::word, "notion_id", object::notion);
19 const field word::lemma = field::joinField(object::word, "lemma_id", object::lemma); 19 const field word::lemma = field::joinField(object::word, "lemma_id", object::lemma);
20 const field word::group = field::joinField(object::word, "group_id", object::group, true); 20 const field word::frame = field::joinField(object::word, "group_id", object::frame, true);
21 21
22 const field word::antonyms = field::selfJoin(object::word, "word_id", "antonymy", "antonym_2_id", "antonym_1_id"); 22 const field word::antonyms = field::selfJoin(object::word, "word_id", "antonymy", "antonym_2_id", "antonym_1_id");
23 23
@@ -93,7 +93,27 @@ namespace verbly {
93 return lemma_; 93 return lemma_;
94 } 94 }
95 95
96 const group& word::getGroup() const 96 bool word::hasFrames() const
97 {
98 if (!valid_)
99 {
100 throw std::domain_error("Bad access to uninitialized word");
101 }
102
103 if (!hasGroup_)
104 {
105 return false;
106 }
107
108 if (!initializedFrames_)
109 {
110 initializeFrames();
111 }
112
113 return !frames_.empty();
114 }
115
116 const std::vector<frame>& word::getFrames() const
97 { 117 {
98 if (!valid_) 118 if (!valid_)
99 { 119 {
@@ -105,12 +125,12 @@ namespace verbly {
105 throw std::domain_error("Word does not have a group"); 125 throw std::domain_error("Word does not have a group");
106 } 126 }
107 127
108 if (!group_) 128 if (!initializedFrames_)
109 { 129 {
110 group_ = db_->groups(group::id == groupId_).first(); 130 initializeFrames();
111 } 131 }
112 132
113 return group_; 133 return frames_;
114 } 134 }
115 135
116 std::string word::getBaseForm() const 136 std::string word::getBaseForm() const
@@ -129,4 +149,10 @@ namespace verbly {
129 return result; 149 return result;
130 } 150 }
131 151
152 void word::initializeFrames() const
153 {
154 initializedFrames_ = true;
155 frames_ = db_->frames(*this, {}, -1).all();
156 }
157
132}; 158};
diff --git a/lib/word.h b/lib/word.h index ddcabe4..8a333a4 100644 --- a/lib/word.h +++ b/lib/word.h
@@ -7,7 +7,7 @@
7#include "filter.h" 7#include "filter.h"
8#include "notion.h" 8#include "notion.h"
9#include "lemma.h" 9#include "lemma.h"
10#include "group.h" 10#include "frame.h"
11 11
12struct sqlite3_stmt; 12struct sqlite3_stmt;
13 13
@@ -97,17 +97,9 @@ namespace verbly {
97 97
98 const lemma& getLemma() const; 98 const lemma& getLemma() const;
99 99
100 bool hasGroup() const 100 bool hasFrames() const;
101 {
102 if (!valid_)
103 {
104 throw std::domain_error("Bad access to uninitialized word");
105 }
106 101
107 return hasGroup_; 102 const std::vector<frame>& getFrames() const;
108 }
109
110 const group& getGroup() const;
111 103
112 // Convenience accessors 104 // Convenience accessors
113 105
@@ -136,7 +128,7 @@ namespace verbly {
136 128
137 static const field notion; 129 static const field notion;
138 static const field lemma; 130 static const field lemma;
139 static const field group; 131 static const field frame;
140 132
141 // Relationships with self 133 // Relationships with self
142 134
@@ -161,6 +153,9 @@ namespace verbly {
161 static const field regionalDomains; 153 static const field regionalDomains;
162 154
163 private: 155 private:
156
157 void initializeFrames() const;
158
164 bool valid_ = false; 159 bool valid_ = false;
165 160
166 int id_; 161 int id_;
@@ -176,7 +171,9 @@ namespace verbly {
176 171
177 mutable class notion notion_; 172 mutable class notion notion_;
178 mutable class lemma lemma_; 173 mutable class lemma lemma_;
179 mutable class group group_; 174
175 mutable bool initializedFrames_ = false;
176 mutable std::vector<class frame> frames_;
180 177
181 }; 178 };
182 179