proskomma-core 0.9.10 → 0.9.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/.eslintrc.json +79 -0
  2. package/.gitattributes +2 -0
  3. package/docs/_config.yml +1 -0
  4. package/docs/index.md +3 -0
  5. package/docs/pk_edit_0_9.odt +0 -0
  6. package/docs/pk_edit_0_9.pdf +0 -0
  7. package/docs/proskomma_101_slideshow.odp +0 -0
  8. package/docs/proskomma_101_slideshow.pdf +0 -0
  9. package/docs/proskomma_edit_html.png +0 -0
  10. package/docs/proskomma_edit_html.svg +451 -0
  11. package/docs/proskomma_edit_reference.png +0 -0
  12. package/docs/proskomma_edit_reference.svg +349 -0
  13. package/docs/repo_logo.png +0 -0
  14. package/esm/package.json +3 -0
  15. package/esm/wrapper.mjs +5 -0
  16. package/index.cjs +6 -0
  17. package/package.json +18 -15
  18. package/src/graph/index.cjs +201 -0
  19. package/src/graph/lib/do_cv.cjs +249 -0
  20. package/src/graph/lib/dump.cjs +40 -0
  21. package/src/graph/lib/remake_blocks.cjs +57 -0
  22. package/src/graph/lib/sequence_chars.cjs +84 -0
  23. package/src/graph/lib/sort.cjs +39 -0
  24. package/src/graph/lib/tribos.cjs +97 -0
  25. package/src/graph/lib/tribos_expression.cjs +556 -0
  26. package/src/graph/lib/tribos_ohm_UNUSED.cjs +37 -0
  27. package/src/graph/lib/tribos_step.cjs +434 -0
  28. package/src/graph/mutations/add.cjs +91 -0
  29. package/src/graph/mutations/delete.cjs +61 -0
  30. package/src/graph/mutations/index.cjs +54 -0
  31. package/src/graph/mutations/rehash.cjs +17 -0
  32. package/src/graph/mutations/tags.cjs +120 -0
  33. package/src/graph/mutations/update.cjs +210 -0
  34. package/src/graph/mutations/versification.cjs +96 -0
  35. package/src/graph/queries/block.cjs +228 -0
  36. package/src/graph/queries/cIndex.cjs +87 -0
  37. package/src/graph/queries/cell.cjs +42 -0
  38. package/src/graph/queries/cv.cjs +19 -0
  39. package/src/graph/queries/cvBook.cjs +16 -0
  40. package/src/graph/queries/cvChapter.cjs +16 -0
  41. package/src/graph/queries/cvIndex.cjs +47 -0
  42. package/src/graph/queries/cvNavigation.cjs +116 -0
  43. package/src/graph/queries/cvVerseElement.cjs +84 -0
  44. package/src/graph/queries/cvVerses.cjs +14 -0
  45. package/src/graph/queries/doc_set.cjs +220 -0
  46. package/src/graph/queries/document.cjs +555 -0
  47. package/src/graph/queries/idParts.cjs +30 -0
  48. package/src/graph/queries/index.cjs +151 -0
  49. package/src/graph/queries/inputBlockSpec.cjs +17 -0
  50. package/src/graph/queries/inputItemObject.cjs +12 -0
  51. package/src/graph/queries/input_att_spec.cjs +14 -0
  52. package/src/graph/queries/input_key_matches.cjs +11 -0
  53. package/src/graph/queries/input_key_value.cjs +11 -0
  54. package/src/graph/queries/input_key_values.cjs +11 -0
  55. package/src/graph/queries/input_selector_spec.cjs +19 -0
  56. package/src/graph/queries/item.cjs +71 -0
  57. package/src/graph/queries/itemGroup.cjs +75 -0
  58. package/src/graph/queries/key_value.cjs +18 -0
  59. package/src/graph/queries/kv_entry.cjs +21 -0
  60. package/src/graph/queries/kv_sequence.cjs +151 -0
  61. package/src/graph/queries/node.cjs +44 -0
  62. package/src/graph/queries/orig.cjs +11 -0
  63. package/src/graph/queries/regex_index.cjs +18 -0
  64. package/src/graph/queries/row_equals_spec.cjs +11 -0
  65. package/src/graph/queries/row_match_spec.cjs +11 -0
  66. package/src/graph/queries/selector_spec.cjs +29 -0
  67. package/src/graph/queries/sequence.cjs +314 -0
  68. package/src/graph/queries/table_sequence.cjs +192 -0
  69. package/src/graph/queries/tree_sequence.cjs +58 -0
  70. package/src/graph/queries/verseNumber.cjs +44 -0
  71. package/src/graph/queries/verseRange.cjs +11 -0
  72. package/src/graph/queries/versification.cjs +25 -0
  73. package/src/index.cjs +583 -0
  74. package/src/model/doc_set.cjs +389 -0
  75. package/src/model/doc_set_helpers/block.cjs +232 -0
  76. package/src/model/doc_set_helpers/enum.cjs +87 -0
  77. package/src/model/doc_set_helpers/item.cjs +198 -0
  78. package/src/model/doc_set_helpers/rehash.cjs +53 -0
  79. package/src/model/doc_set_helpers/selectors.cjs +58 -0
  80. package/src/model/doc_set_helpers/serialize.cjs +22 -0
  81. package/src/model/doc_set_helpers/unsuccinctify.cjs +288 -0
  82. package/src/model/doc_set_helpers/update.cjs +290 -0
  83. package/src/model/document.cjs +369 -0
  84. package/src/model/document_helpers/blocks.cjs +135 -0
  85. package/src/model/document_helpers/chapter_verse.cjs +291 -0
  86. package/src/model/document_helpers/pre_enums.cjs +67 -0
  87. package/src/model/document_helpers/sequences.cjs +205 -0
  88. package/src/model/document_helpers/serialize_succinct.cjs +55 -0
  89. package/src/model/document_helpers/succinct_filter.cjs +102 -0
  90. package/src/model/versifications/eng_vrs.cjs +455 -0
  91. package/src/model/versifications/index.cjs +38 -0
  92. package/src/model/versifications/lxx_vrs.cjs +950 -0
  93. package/src/model/versifications/org_vrs.cjs +155 -0
  94. package/src/model/versifications/rsc_vrs.cjs +342 -0
  95. package/src/model/versifications/rso_vrs.cjs +541 -0
  96. package/src/model/versifications/vul_vrs.cjs +835 -0
  97. package/src/parser/index.cjs +506 -0
  98. package/src/parser/lexers/index.cjs +11 -0
  99. package/src/parser/lexers/lexingRegexes.cjs +45 -0
  100. package/src/parser/lexers/nodes.cjs +123 -0
  101. package/src/parser/lexers/object_for_fragment.cjs +123 -0
  102. package/src/parser/lexers/tsv.cjs +45 -0
  103. package/src/parser/lexers/usfm.cjs +17 -0
  104. package/src/parser/lexers/usx/index.cjs +7 -0
  105. package/src/parser/lexers/usx/usx_lexer.cjs +321 -0
  106. package/src/parser/lib/tokenize.cjs +37 -0
  107. package/src/parser/model/index.cjs +637 -0
  108. package/src/parser/parser_specs.cjs +781 -0
  109. package/src/pipelines/perf2x/index.cjs +7 -0
  110. package/src/pipelines/perf2x/perf2PkJsonPipeline.json +41 -0
  111. package/src/pipelines/perf2x/perf2usfmPipeline.json +65 -0
  112. package/src/transforms/calculateUsfmChapterPositions.cjs +106 -0
  113. package/src/transforms/index.cjs +9 -0
  114. package/src/transforms/perf2PkJson.cjs +534 -0
  115. package/src/transforms/perf2usfm.cjs +294 -0
  116. package/src/util/byteArray.cjs +251 -0
  117. package/src/util/canons.cjs +117 -0
  118. package/src/util/enums.cjs +41 -0
  119. package/src/util/generateId.cjs +6 -0
  120. package/src/util/graftDefs.cjs +15 -0
  121. package/src/util/index.cjs +29 -0
  122. package/src/util/inspectSuccinct.cjs +59 -0
  123. package/src/util/itemDefs.cjs +33 -0
  124. package/src/util/parserConstantDefs.cjs +30 -0
  125. package/src/util/scopeDefs.cjs +173 -0
  126. package/src/util/scriptlike/blocksSpec.cjs +192 -0
  127. package/src/util/scriptlike/dodson_lexicon.xsl +19 -0
  128. package/src/util/scriptlike/dodson_lexicon_to_entries.cjs +85 -0
  129. package/src/util/scriptlike/open_scriptures_hebrew_lexicon.xsl +28 -0
  130. package/src/util/scriptlike/open_scriptures_hebrew_lexicon_to_entries.cjs +85 -0
  131. package/src/util/succinct.cjs +159 -0
  132. package/src/util/tags.cjs +28 -0
  133. package/src/util/tokenDefs.cjs +27 -0
  134. package/src/util/versification.cjs +364 -0
  135. package/dist/index.js +0 -8
  136. package/dist/index.js.LICENSE.txt +0 -23
@@ -0,0 +1,556 @@
1
+ const xre = require('xregexp');
2
+
3
+ const aggregateFunctions = {
4
+ equals: (docSet, node, a, b) => a === b,
5
+ notEqual: (docSet, node, a, b) => a !== b,
6
+ and: (docSet, node, ...args) => args.filter(a => !a).length === 0,
7
+ or: (docSet, node, ...args) => args.filter(a => a).length > 0,
8
+ not: (docSet, node, a) => !a,
9
+ idRef: (docSet, node) => docSet.unsuccinctifyScopes(node.bs)[0][2].split('/')[1],
10
+ parentIdRef: (docSet, node) => docSet.unsuccinctifyScopes(node.is).filter(s => s[2].startsWith('tTreeParent'))[0][2].split('/')[1],
11
+ nChildren: (docSet, node) => docSet.unsuccinctifyScopes(node.is).filter(s => s[2].startsWith('tTreeChild')).length,
12
+ contentRef: (docSet, node, label) => {
13
+ const labelIG = docSet.sequenceItemsByScopes([node], ['tTreeContent/'], false)
14
+ .filter(ig => {
15
+ const key = ig[0].filter(s => s.startsWith('tTreeContent'))[0].split('/')[1];
16
+ return key === label;
17
+ });
18
+ return labelIG[0] ?
19
+ labelIG[0][1].filter(i => i[0] === 'token').map(t => t[2]).join('') :
20
+ '';
21
+ },
22
+ hasContent: (docSet, node, label) => {
23
+ const labelIG = docSet.sequenceItemsByScopes([node], ['tTreeContent/'], false)
24
+ .filter(ig => {
25
+ const key = ig[0].filter(s => s.startsWith('tTreeContent'))[0].split('/')[1];
26
+ return key === label;
27
+ });
28
+ return labelIG.length > 0;
29
+ },
30
+ concat: (docSet, node, ...args) => args.join(''),
31
+ startsWith: (docSet, node, a, b) => a.startsWith(b),
32
+ endsWith: (docSet, node, a, b) => a.endsWith(b),
33
+ contains: (docSet, node, a, b) => a.includes(b),
34
+ matches: (docSet, node, a, b) => xre.test(a, xre(b)),
35
+ int: (docSet, node, str) => parseInt(str),
36
+ string: (docSet, node, int) => `${int}`,
37
+ left: (docSet, node, str, int) => str.substring(0, int),
38
+ right: (docSet, node, str, int) => str.substring(str.length - int),
39
+ length: (docSet, node, str) => str.length,
40
+ indexOf: (docSet, node, a, b) => a.indexOf(b),
41
+ add: (docSet, node, ...args) => args.reduce((x, y) => x + y),
42
+ mul: (docSet, node, ...args) => args.reduce((x, y) => x * y),
43
+ sub: (docSet, node, a, b) => a - b,
44
+ div: (docSet, node, a, b) => Math.floor(a / b),
45
+ mod: (docSet, node, a, b) => a % b,
46
+ gt: (docSet, node, a, b) => a > b,
47
+ lt: (docSet, node, a, b) => a < b,
48
+ ge: (docSet, node, a, b) => a >= b,
49
+ le: (docSet, node, a, b) => a <= b,
50
+ };
51
+
52
+ const parseFunctions = {
53
+ quotedString: str => str.substring(1, str.length - 1),
54
+ int: str => parseInt(str),
55
+ true: () => true,
56
+ false: () => false,
57
+ };
58
+
59
+ const splitArgs = str => {
60
+ const ret = [[]];
61
+ let pos = 0;
62
+ let nParen = 0;
63
+ let inQuote = false;
64
+
65
+ while (str && pos < str.length) {
66
+ switch (str[pos]) {
67
+ case '\\':
68
+ ret[ret.length - 1].push(str[pos]);
69
+
70
+ if (str[pos + 1] ==='\'') {
71
+ ret[ret.length - 1].push(str[pos + 1]);
72
+ pos++;
73
+ }
74
+ break;
75
+ case '\'':
76
+ ret[ret.length - 1].push(str[pos]);
77
+ inQuote = !inQuote;
78
+ break;
79
+ case '(':
80
+ if (inQuote) {
81
+ ret[ret.length - 1].push(str[pos]);
82
+ } else {
83
+ ret[ret.length - 1].push(str[pos]);
84
+ nParen++;
85
+ }
86
+ break;
87
+ case ')':
88
+ if (inQuote) {
89
+ ret[ret.length - 1].push(str[pos]);
90
+ } else {
91
+ ret[ret.length - 1].push(str[pos]);
92
+ nParen--;
93
+ }
94
+ break;
95
+ case ',':
96
+ if (!inQuote && nParen === 0) {
97
+ ret.push([]);
98
+
99
+ while (str[pos + 1] === ' ') {
100
+ pos++;
101
+ }
102
+ } else {
103
+ ret[ret.length - 1].push(str[pos]);
104
+ }
105
+ break;
106
+ default:
107
+ ret[ret.length - 1].push(str[pos]);
108
+ }
109
+ pos++;
110
+ }
111
+ return ret.map(e => e.join(''));
112
+ };
113
+
114
+ const expressions = {
115
+ expression: { oneOf: ['stringExpression', 'intExpression', 'booleanExpression'] },
116
+ booleanExpression: { oneOf: ['booleanPrimitive', 'equals', 'notEqual', 'and', 'or', 'not', 'contains', 'startsWith', 'endsWith', 'matches', 'gt', 'lt', 'ge', 'le', 'hasContent'] },
117
+ stringExpression: { oneOf: ['concat', 'left', 'right', 'string', 'idRef', 'parentIdRef', 'contentRef', 'stringPrimitive'] },
118
+ intExpression: { oneOf: ['length', 'indexOf', 'int', 'nChildren', 'intPrimitive', 'add', 'sub', 'mul', 'div', 'mod'] },
119
+ equals: {
120
+ regex: xre('^==\\((.+)\\)$'),
121
+ doc: {
122
+ operator: '==',
123
+ args: ['expression', 'expression'],
124
+ result: 'boolean',
125
+ description: 'Are the arguments strictly equal?',
126
+ },
127
+ argStructure: [['expression', [2, 2]]],
128
+ },
129
+ notEqual: {
130
+ regex: xre('^!=\\((.+)\\)$'),
131
+ doc: {
132
+ operator: '!=',
133
+ args: ['expression', 'expression'],
134
+ result: 'boolean',
135
+ description: 'Are the arguments not strictly equal?',
136
+ },
137
+ argStructure: [['expression', [2, 2]]],
138
+ },
139
+ and: {
140
+ regex: xre('^and\\((.+)\\)$'),
141
+ doc: {
142
+ operator: 'and',
143
+ args: ['boolean', 'boolean', '...'],
144
+ result: 'boolean',
145
+ description: 'Are all the arguments true?',
146
+ },
147
+ breakOn: false,
148
+ argStructure: [['booleanExpression', [2, null]]],
149
+ },
150
+ or: {
151
+ regex: xre('^or\\((.+)\\)$'),
152
+ doc: {
153
+ operator: 'or',
154
+ args: ['boolean', 'boolean'],
155
+ result: 'boolean',
156
+ description: 'Are any arguments true?',
157
+ },
158
+ breakOn: true,
159
+ argStructure: [['booleanExpression', [2, null]]],
160
+ },
161
+ concat: {
162
+ regex: xre('^concat\\((.+)\\)$'),
163
+ doc: {
164
+ operator: 'concat',
165
+ args: ['string', 'string', '...'],
166
+ result: 'string',
167
+ description: 'Concatenates string arguments',
168
+ },
169
+ argStructure: [['stringExpression', [2, null]]],
170
+ },
171
+ contentRef: {
172
+ regex: xre('^content\\((.+)\\)$'),
173
+ doc: {
174
+ operator: 'content',
175
+ args: ['string'],
176
+ result: 'string',
177
+ description: 'String value of the specified content for the node',
178
+ },
179
+ argStructure: [['stringExpression', [1, 1]]],
180
+ },
181
+ hasContent: {
182
+ regex: xre('^hasContent\\((.+)\\)$'),
183
+ doc: {
184
+ operator: 'hasContent',
185
+ args: ['string'],
186
+ result: 'boolean',
187
+ description: 'Does the node have this content?',
188
+ },
189
+ argStructure: [['stringExpression', [1, 1]]],
190
+ },
191
+ contains: {
192
+ regex: xre('^contains\\((.+)\\)$'),
193
+ doc: {
194
+ operator: 'contains',
195
+ args: ['string', 'string'],
196
+ result: 'boolean',
197
+ description: 'Does the first string contain the second string?',
198
+ },
199
+ argStructure: [['stringExpression', [2, 2]]],
200
+ },
201
+ startsWith: {
202
+ regex: xre('^startsWith\\((.+)\\)$'),
203
+ doc: {
204
+ operator: 'startsWith',
205
+ args: ['string', 'string'],
206
+ result: 'boolean',
207
+ description: 'Does the first string start with the second string?',
208
+ },
209
+ argStructure: [['stringExpression', [2, 2]]],
210
+ },
211
+ endsWith: {
212
+ regex: xre('^endsWith\\((.+)\\)$'),
213
+ doc: {
214
+ operator: 'endsWith',
215
+ args: ['string', 'string'],
216
+ result: 'boolean',
217
+ description: 'Does the first string end with the second string?',
218
+ },
219
+ argStructure: [['stringExpression', [2, 2]]],
220
+ },
221
+ matches: {
222
+ regex: xre('^matches\\((.+)\\)$'),
223
+ doc: {
224
+ operator: 'matches',
225
+ args: ['string', 'regex'],
226
+ result: 'boolean',
227
+ description: 'Does the first string match the regex in the second string?',
228
+ },
229
+ argStructure: [['stringExpression', [2, 2]]],
230
+ },
231
+ left: {
232
+ regex: xre('^left\\((.+)\\)$'),
233
+ doc: {
234
+ operator: 'left',
235
+ args: ['string', 'integer'],
236
+ result: 'string',
237
+ description: 'The first n characters of the string',
238
+ },
239
+ argStructure: [['stringExpression', [1, 1]], ['intExpression', [1, 1]]],
240
+ },
241
+ right: {
242
+ regex: xre('^right\\((.+)\\)$'),
243
+ doc: {
244
+ operator: 'right',
245
+ args: ['string', 'integer'],
246
+ result: 'string',
247
+ description: 'The last n characters of the string',
248
+ },
249
+ argStructure: [['stringExpression', [1, 1]], ['intExpression', [1, 1]]],
250
+ },
251
+ length: {
252
+ regex: xre('^length\\((.+)\\)$'),
253
+ doc: {
254
+ operator: 'length',
255
+ args: ['string'],
256
+ result: 'integer',
257
+ description: 'The number of characters in the string',
258
+ },
259
+ argStructure: [['stringExpression', [1, 1]]],
260
+ },
261
+ indexOf: {
262
+ regex: xre('^indexOf\\((.+)\\)$'),
263
+ doc: {
264
+ operator: 'indexOf',
265
+ args: ['string', 'string'],
266
+ result: 'number',
267
+ description: 'The integer position at which the second string starts in the first string',
268
+ },
269
+ argStructure: [['stringExpression', [2, 2]]],
270
+ },
271
+ not: {
272
+ regex: xre('^not\\((.+)\\)$'),
273
+ doc: {
274
+ operator: 'not',
275
+ args: ['boolean'],
276
+ result: 'boolean',
277
+ description: 'The inverse boolean value of the argument',
278
+ },
279
+ argStructure: [['booleanExpression', [1, 1]]],
280
+ },
281
+ int: {
282
+ regex: xre('^int\\((.+)\\)$'),
283
+ doc: {
284
+ operator: 'int',
285
+ args: ['string'],
286
+ result: 'integer',
287
+ description: 'The integer value of the string',
288
+ },
289
+ argStructure: [['stringExpression', [1, 1]]],
290
+ },
291
+ string: {
292
+ regex: xre('^string\\((.+)\\)$'),
293
+ doc: {
294
+ operator: 'string',
295
+ args: ['integer'],
296
+ result: 'string',
297
+ description: 'The string value of the integer',
298
+ },
299
+ argStructure: [['intExpression', [1, 1]]],
300
+ },
301
+ idRef: {
302
+ regex: xre('^id$'),
303
+ doc: {
304
+ operator: 'id',
305
+ args: [],
306
+ result: 'string',
307
+ description: 'The node ID',
308
+ },
309
+ argStructure: [],
310
+ },
311
+ parentIdRef: {
312
+ regex: xre('^parentId$'),
313
+ doc: {
314
+ operator: 'parentId',
315
+ args: [],
316
+ result: 'string',
317
+ description: 'The node\'s parent ID',
318
+ },
319
+ argStructure: [],
320
+ },
321
+ nChildren: {
322
+ regex: xre('^nChildren$'),
323
+ doc: {
324
+ operator: 'nChildren',
325
+ args: [],
326
+ result: 'int',
327
+ description: 'The number of children of the node',
328
+ },
329
+ argStructure: [],
330
+ },
331
+ add: {
332
+ regex: xre('^add\\((.+)\\)$'),
333
+ doc: {
334
+ operator: 'add',
335
+ args: ['integer', '...'],
336
+ result: 'integer',
337
+ description: 'The numeric sum of the arguments',
338
+ },
339
+ argStructure: [['intExpression', [2, null]]],
340
+ },
341
+ mul: {
342
+ regex: xre('^mul\\((.+)\\)$'),
343
+ doc: {
344
+ operator: 'mul',
345
+ args: ['integer', '...'],
346
+ result: 'integer',
347
+ description: 'The numeric product of the arguments',
348
+ },
349
+ argStructure: [['intExpression', [2, null]]],
350
+ },
351
+ sub: {
352
+ regex: xre('^sub\\((.+)\\)$'),
353
+ doc: {
354
+ operator: 'sub',
355
+ args: ['integer', 'integer'],
356
+ result: 'integer',
357
+ description: 'The first integer minus the second',
358
+ },
359
+ argStructure: [['intExpression', [2, 2]]],
360
+ },
361
+ div: {
362
+ regex: xre('^div\\((.+)\\)$'),
363
+ doc: {
364
+ operator: 'div',
365
+ args: ['integer', 'integer'],
366
+ result: 'integer',
367
+ description: 'The first integer divided by the second',
368
+ },
369
+ argStructure: [['intExpression', [2, 2]]],
370
+ },
371
+ mod: {
372
+ regex: xre('^mod\\((.+)\\)$'),
373
+ doc: {
374
+ operator: 'mod',
375
+ args: ['integer', 'integer'],
376
+ result: 'integer',
377
+ description: 'The modulus of the first integer when divided by the second',
378
+ },
379
+ argStructure: [['intExpression', [2, 2]]],
380
+ },
381
+ gt: {
382
+ regex: xre('^>\\((.+)\\)$'),
383
+ doc: {
384
+ operator: '>',
385
+ args: ['integer', 'integer'],
386
+ result: 'boolean',
387
+ description: 'Is the first integer numerically greater than the second?',
388
+ },
389
+ argStructure: [['intExpression', [2, 2]]],
390
+ },
391
+ lt: {
392
+ regex: xre('^<\\((.+)\\)$'),
393
+ doc: {
394
+ operator: '<',
395
+ args: ['integer', 'integer'],
396
+ result: 'boolean',
397
+ description: 'Is the first integer numerically less than the second?',
398
+ },
399
+ argStructure: [['intExpression', [2, 2]]],
400
+ },
401
+ ge: {
402
+ regex: xre('^>=\\((.+)\\)$'),
403
+ doc: {
404
+ operator: '>=',
405
+ args: ['integer', 'integer'],
406
+ result: 'boolean',
407
+ description: 'Is the first integer numerically greater than or equal to the second?',
408
+ },
409
+ argStructure: [['intExpression', [2, 2]]],
410
+ },
411
+ le: {
412
+ regex: xre('^<=\\((.+)\\)$'),
413
+ doc: {
414
+ operator: '<=',
415
+ args: ['integer', 'integer'],
416
+ result: 'boolean',
417
+ description: 'Is the first integer numerically less than or equal to the second?',
418
+ },
419
+ argStructure: [['intExpression', [2, 2]]],
420
+ },
421
+ stringPrimitive: {
422
+ regex: xre('^(\'([^\']|\\\\\')*\')$'),
423
+ parseFunctions: [null, 'quotedString'],
424
+ },
425
+ intPrimitive: {
426
+ regex: xre('^(-?[0-9]+)$'),
427
+ parseFunctions: [null, 'int'],
428
+ },
429
+ booleanPrimitive: {
430
+ regex: xre('^(true)|(false)$'),
431
+ parseFunctions: [null, 'true', 'false'],
432
+ },
433
+ };
434
+
435
+ const parseRegexExpression = (docSet, node, predicateString, expressionId, matches) => {
436
+ // console.log(`parseRegexExpression ${predicateString} ${expressionId} ${matches}`);
437
+ const expressionRecord = expressions[expressionId];
438
+
439
+ if (!expressionRecord) {
440
+ throw new Error(`Unknown expression ${expressionId} for predicate ${predicateString}`);
441
+ }
442
+
443
+ const nExpectedArgs = structure => [
444
+ structure.map(a => a[1][0]).reduce((a, b) => a + b),
445
+ structure.filter(a => a[1][1] === null).length > 0,
446
+ ];
447
+
448
+ if (expressionRecord.parseFunctions) {
449
+ let found = false;
450
+
451
+ for (const [n, parseFunction] of expressionRecord.parseFunctions.entries()) {
452
+ if (!parseFunction || !matches[n]) {
453
+ continue;
454
+ }
455
+ found = true;
456
+ return { data: parseFunctions[parseFunction](matches[n]) };
457
+ }
458
+
459
+ if (!found) {
460
+ return { errors: `Could not parse predicate ${predicateString}` };
461
+ }
462
+ } else {
463
+ const argRecords = splitArgs(matches[1]);
464
+ const argStructure = expressionRecord.argStructure;
465
+ const argResults = [];
466
+
467
+ if (argStructure.length > 0) {
468
+ const nExpected = nExpectedArgs(argStructure);
469
+
470
+ if (argRecords.length < nExpected[0]) {
471
+ return { errors: `Expected at least ${nExpected[0]} args for '${expressionId}', found ${argRecords.length}` };
472
+ }
473
+
474
+ if (!nExpected[1] && argRecords.length > nExpected[0]) {
475
+ return { errors: `Expected at most ${nExpected[0]} args for '${expressionId}', found ${argRecords.length}` };
476
+ }
477
+
478
+ let argRecordN = 0;
479
+ let argStructureN = 0;
480
+ let nOccs = 0;
481
+
482
+ while (argRecordN < argRecords.length) {
483
+ const argRecord = argRecords[argRecordN];
484
+ const argResult = parseExpression(docSet, node, argRecord, argStructure[argStructureN][0]);
485
+
486
+ if ('breakOn' in expressionRecord && !argRecord.errors && argResult.data === expressionRecord.breakOn) {
487
+ return argResult;
488
+ }
489
+ argResults.push(argResult);
490
+ argRecordN++;
491
+ nOccs++;
492
+
493
+ if (argStructure[argStructureN][1][1] && nOccs >= argStructure[argStructureN][1][1]) {
494
+ argStructureN++;
495
+ nOccs = 0;
496
+ }
497
+ }
498
+ }
499
+
500
+ if (argResults.filter(ar => ar.errors).length === 0) {
501
+ // console.log(expressionId);
502
+ const args = argResults.map(ar => ar.data);
503
+ const aggregated = aggregateFunctions[expressionId](docSet, node, ...args);
504
+ // console.log('aggregated', expressionId, argRecords, aggregated);
505
+ return { data: aggregated };
506
+ }
507
+ return { errors: `Could not parse arguments to ${expressionId}: ${argRecords.filter(ar => ar.errors).map(ar => ar.errors).join('; ')}` };
508
+ }
509
+ };
510
+
511
+ const parseExpression = (docSet, node, predicate, expressionId) => {
512
+ // console.log(`parseExpression ${predicate}, ${expressionId}`);
513
+ const expressionRecord = expressions[expressionId];
514
+
515
+ if (!expressionRecord) {
516
+ throw new Error(`Unknown expression ${expressionId} for predicate ${predicate}`);
517
+ }
518
+
519
+ if (expressionRecord.oneOf) {
520
+ let errors = null;
521
+
522
+ for (const option of expressionRecord.oneOf) {
523
+ const optionResult = parseExpression(docSet, node, predicate, option);
524
+
525
+ if (!optionResult.errors) {
526
+ return optionResult;
527
+ } else if (!errors || (optionResult.errors.length < errors.length)) {
528
+ errors = optionResult.errors;
529
+ }
530
+ }
531
+ return { errors: errors };
532
+ } else {
533
+ const matches = xre.exec(predicate, expressionRecord.regex);
534
+
535
+ if (matches) {
536
+ const reResult = parseRegexExpression(docSet, node, predicate, expressionId, matches);
537
+ return reResult;
538
+ } else {
539
+ return { errors: `Could not match ${predicate}` };
540
+ }
541
+ }
542
+ };
543
+
544
+ const doPredicate = (docSet, result, predicateString) => ({
545
+ data: result.data.filter(node => {
546
+ const nodeResult = parseExpression(docSet, node, predicateString, 'booleanExpression');
547
+
548
+ // console.log();
549
+ if (nodeResult.errors) {
550
+ throw new Error(`Predicate - ${nodeResult.errors}`);
551
+ }
552
+ return nodeResult.data;
553
+ }),
554
+ });
555
+
556
+ module.exports = { expressions, doPredicate };
@@ -0,0 +1,37 @@
1
+ /*
2
+ predicateExpression {
3
+ Exp
4
+ = booleanExpression
5
+
6
+ booleanExpression =
7
+ "true" |
8
+ "false" |
9
+ booleanNonPrimitive
10
+
11
+ booleanNonPrimitive =
12
+ equalsOperator |
13
+ notEqualOperator |
14
+ andOperator |
15
+ orOperator
16
+
17
+ equalsOperator = "==" "(" expressions ")"
18
+ notEqualOperator = "!=" "(" expressions ")"
19
+ andOperator = "and" "(" booleanExpressions ")"
20
+ orOperator = "or" "(" booleanExpressions ")"
21
+
22
+ expressions = expression ("," " "* expressions)*
23
+ booleanExpressions = booleanExpression ("," " "* booleanExpressions)*
24
+ stringExpressions = stringExpression ("," " "* stringExpressions)*
25
+
26
+ expression = booleanExpression | stringExpression
27
+
28
+ stringExpression = stringLiteral | stringNonPrimitive
29
+
30
+ stringNonPrimitive = concatOperator
31
+
32
+ concatOperator = "concat" "(" stringExpressions ")"
33
+
34
+ stringLiteral = "'" letter* "'"
35
+
36
+ }
37
+ */