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.
- package/.eslintrc.json +79 -0
- package/.gitattributes +2 -0
- package/docs/_config.yml +1 -0
- package/docs/index.md +3 -0
- package/docs/pk_edit_0_9.odt +0 -0
- package/docs/pk_edit_0_9.pdf +0 -0
- package/docs/proskomma_101_slideshow.odp +0 -0
- package/docs/proskomma_101_slideshow.pdf +0 -0
- package/docs/proskomma_edit_html.png +0 -0
- package/docs/proskomma_edit_html.svg +451 -0
- package/docs/proskomma_edit_reference.png +0 -0
- package/docs/proskomma_edit_reference.svg +349 -0
- package/docs/repo_logo.png +0 -0
- package/esm/package.json +3 -0
- package/esm/wrapper.mjs +5 -0
- package/index.cjs +6 -0
- package/package.json +18 -15
- package/src/graph/index.cjs +201 -0
- package/src/graph/lib/do_cv.cjs +249 -0
- package/src/graph/lib/dump.cjs +40 -0
- package/src/graph/lib/remake_blocks.cjs +57 -0
- package/src/graph/lib/sequence_chars.cjs +84 -0
- package/src/graph/lib/sort.cjs +39 -0
- package/src/graph/lib/tribos.cjs +97 -0
- package/src/graph/lib/tribos_expression.cjs +556 -0
- package/src/graph/lib/tribos_ohm_UNUSED.cjs +37 -0
- package/src/graph/lib/tribos_step.cjs +434 -0
- package/src/graph/mutations/add.cjs +91 -0
- package/src/graph/mutations/delete.cjs +61 -0
- package/src/graph/mutations/index.cjs +54 -0
- package/src/graph/mutations/rehash.cjs +17 -0
- package/src/graph/mutations/tags.cjs +120 -0
- package/src/graph/mutations/update.cjs +210 -0
- package/src/graph/mutations/versification.cjs +96 -0
- package/src/graph/queries/block.cjs +228 -0
- package/src/graph/queries/cIndex.cjs +87 -0
- package/src/graph/queries/cell.cjs +42 -0
- package/src/graph/queries/cv.cjs +19 -0
- package/src/graph/queries/cvBook.cjs +16 -0
- package/src/graph/queries/cvChapter.cjs +16 -0
- package/src/graph/queries/cvIndex.cjs +47 -0
- package/src/graph/queries/cvNavigation.cjs +116 -0
- package/src/graph/queries/cvVerseElement.cjs +84 -0
- package/src/graph/queries/cvVerses.cjs +14 -0
- package/src/graph/queries/doc_set.cjs +220 -0
- package/src/graph/queries/document.cjs +555 -0
- package/src/graph/queries/idParts.cjs +30 -0
- package/src/graph/queries/index.cjs +151 -0
- package/src/graph/queries/inputBlockSpec.cjs +17 -0
- package/src/graph/queries/inputItemObject.cjs +12 -0
- package/src/graph/queries/input_att_spec.cjs +14 -0
- package/src/graph/queries/input_key_matches.cjs +11 -0
- package/src/graph/queries/input_key_value.cjs +11 -0
- package/src/graph/queries/input_key_values.cjs +11 -0
- package/src/graph/queries/input_selector_spec.cjs +19 -0
- package/src/graph/queries/item.cjs +71 -0
- package/src/graph/queries/itemGroup.cjs +75 -0
- package/src/graph/queries/key_value.cjs +18 -0
- package/src/graph/queries/kv_entry.cjs +21 -0
- package/src/graph/queries/kv_sequence.cjs +151 -0
- package/src/graph/queries/node.cjs +44 -0
- package/src/graph/queries/orig.cjs +11 -0
- package/src/graph/queries/regex_index.cjs +18 -0
- package/src/graph/queries/row_equals_spec.cjs +11 -0
- package/src/graph/queries/row_match_spec.cjs +11 -0
- package/src/graph/queries/selector_spec.cjs +29 -0
- package/src/graph/queries/sequence.cjs +314 -0
- package/src/graph/queries/table_sequence.cjs +192 -0
- package/src/graph/queries/tree_sequence.cjs +58 -0
- package/src/graph/queries/verseNumber.cjs +44 -0
- package/src/graph/queries/verseRange.cjs +11 -0
- package/src/graph/queries/versification.cjs +25 -0
- package/src/index.cjs +583 -0
- package/src/model/doc_set.cjs +389 -0
- package/src/model/doc_set_helpers/block.cjs +232 -0
- package/src/model/doc_set_helpers/enum.cjs +87 -0
- package/src/model/doc_set_helpers/item.cjs +198 -0
- package/src/model/doc_set_helpers/rehash.cjs +53 -0
- package/src/model/doc_set_helpers/selectors.cjs +58 -0
- package/src/model/doc_set_helpers/serialize.cjs +22 -0
- package/src/model/doc_set_helpers/unsuccinctify.cjs +288 -0
- package/src/model/doc_set_helpers/update.cjs +290 -0
- package/src/model/document.cjs +369 -0
- package/src/model/document_helpers/blocks.cjs +135 -0
- package/src/model/document_helpers/chapter_verse.cjs +291 -0
- package/src/model/document_helpers/pre_enums.cjs +67 -0
- package/src/model/document_helpers/sequences.cjs +205 -0
- package/src/model/document_helpers/serialize_succinct.cjs +55 -0
- package/src/model/document_helpers/succinct_filter.cjs +102 -0
- package/src/model/versifications/eng_vrs.cjs +455 -0
- package/src/model/versifications/index.cjs +38 -0
- package/src/model/versifications/lxx_vrs.cjs +950 -0
- package/src/model/versifications/org_vrs.cjs +155 -0
- package/src/model/versifications/rsc_vrs.cjs +342 -0
- package/src/model/versifications/rso_vrs.cjs +541 -0
- package/src/model/versifications/vul_vrs.cjs +835 -0
- package/src/parser/index.cjs +506 -0
- package/src/parser/lexers/index.cjs +11 -0
- package/src/parser/lexers/lexingRegexes.cjs +45 -0
- package/src/parser/lexers/nodes.cjs +123 -0
- package/src/parser/lexers/object_for_fragment.cjs +123 -0
- package/src/parser/lexers/tsv.cjs +45 -0
- package/src/parser/lexers/usfm.cjs +17 -0
- package/src/parser/lexers/usx/index.cjs +7 -0
- package/src/parser/lexers/usx/usx_lexer.cjs +321 -0
- package/src/parser/lib/tokenize.cjs +37 -0
- package/src/parser/model/index.cjs +637 -0
- package/src/parser/parser_specs.cjs +781 -0
- package/src/pipelines/perf2x/index.cjs +7 -0
- package/src/pipelines/perf2x/perf2PkJsonPipeline.json +41 -0
- package/src/pipelines/perf2x/perf2usfmPipeline.json +65 -0
- package/src/transforms/calculateUsfmChapterPositions.cjs +106 -0
- package/src/transforms/index.cjs +9 -0
- package/src/transforms/perf2PkJson.cjs +534 -0
- package/src/transforms/perf2usfm.cjs +294 -0
- package/src/util/byteArray.cjs +251 -0
- package/src/util/canons.cjs +117 -0
- package/src/util/enums.cjs +41 -0
- package/src/util/generateId.cjs +6 -0
- package/src/util/graftDefs.cjs +15 -0
- package/src/util/index.cjs +29 -0
- package/src/util/inspectSuccinct.cjs +59 -0
- package/src/util/itemDefs.cjs +33 -0
- package/src/util/parserConstantDefs.cjs +30 -0
- package/src/util/scopeDefs.cjs +173 -0
- package/src/util/scriptlike/blocksSpec.cjs +192 -0
- package/src/util/scriptlike/dodson_lexicon.xsl +19 -0
- package/src/util/scriptlike/dodson_lexicon_to_entries.cjs +85 -0
- package/src/util/scriptlike/open_scriptures_hebrew_lexicon.xsl +28 -0
- package/src/util/scriptlike/open_scriptures_hebrew_lexicon_to_entries.cjs +85 -0
- package/src/util/succinct.cjs +159 -0
- package/src/util/tags.cjs +28 -0
- package/src/util/tokenDefs.cjs +27 -0
- package/src/util/versification.cjs +364 -0
- package/dist/index.js +0 -8
- package/dist/index.js.LICENSE.txt +0 -23
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
const utils = require('../../util/index.cjs');
|
|
2
|
+
|
|
3
|
+
const countItems = (docSet, succinct) => {
|
|
4
|
+
let count = 0;
|
|
5
|
+
let pos = 0;
|
|
6
|
+
|
|
7
|
+
while (pos < succinct.length) {
|
|
8
|
+
count++;
|
|
9
|
+
const headerByte = succinct.byte(pos);
|
|
10
|
+
const itemLength = headerByte & 0x0000003F;
|
|
11
|
+
pos += itemLength;
|
|
12
|
+
}
|
|
13
|
+
return count;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const itemsByIndex = (docSet, mainSequence, index, includeContext) => {
|
|
17
|
+
let ret = [];
|
|
18
|
+
|
|
19
|
+
if (!index) {
|
|
20
|
+
return ret;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let currentBlock = index.startBlock;
|
|
24
|
+
let nextToken = index.nextToken;
|
|
25
|
+
|
|
26
|
+
while (currentBlock <= index.endBlock) {
|
|
27
|
+
let blockItems = docSet.unsuccinctifyItems(mainSequence.blocks[currentBlock].c, {}, nextToken);
|
|
28
|
+
const blockScope = docSet.unsuccinctifyScopes(mainSequence.blocks[currentBlock].bs)[0];
|
|
29
|
+
const blockGrafts = docSet.unsuccinctifyGrafts(mainSequence.blocks[currentBlock].bg);
|
|
30
|
+
|
|
31
|
+
if (currentBlock === index.startBlock && currentBlock === index.endBlock) {
|
|
32
|
+
blockItems = blockItems.slice(index.startItem, index.endItem + 1);
|
|
33
|
+
} else if (currentBlock === index.startBlock) {
|
|
34
|
+
blockItems = blockItems.slice(index.startItem);
|
|
35
|
+
} else if (currentBlock === index.endBlock) {
|
|
36
|
+
blockItems = blockItems.slice(0, index.endItem + 1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (includeContext) {
|
|
40
|
+
let extendedBlockItems = [];
|
|
41
|
+
|
|
42
|
+
for (const bi of blockItems) {
|
|
43
|
+
extendedBlockItems.push(bi.concat([bi[0] === 'token' && bi[1] === 'wordLike' ? nextToken++ : null]));
|
|
44
|
+
}
|
|
45
|
+
blockItems = extendedBlockItems;
|
|
46
|
+
}
|
|
47
|
+
ret.push([...blockGrafts, ['scope', 'start', blockScope[2]], ...blockItems, ['scope', 'end', blockScope[2]]]);
|
|
48
|
+
currentBlock++;
|
|
49
|
+
}
|
|
50
|
+
return ret;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const sequenceItemsByScopes = (docSet, blocks, byScopes) => {
|
|
54
|
+
// Return array of [scopes, items]
|
|
55
|
+
// Scan block items, track scopes
|
|
56
|
+
// If all scopes found:
|
|
57
|
+
// - turn found scopes into string
|
|
58
|
+
// - if that scope string doesn't exist, add to lookup table and push array
|
|
59
|
+
// - add item to array matching scope string
|
|
60
|
+
let allBlockScopes = [];
|
|
61
|
+
|
|
62
|
+
const allScopesPresent = () => {
|
|
63
|
+
for (const requiredScope of byScopes) {
|
|
64
|
+
if (!matchingScope(requiredScope)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const matchingScope = (scopeToMatch) => {
|
|
72
|
+
for (const blockScope of allBlockScopes) {
|
|
73
|
+
if (blockScope.startsWith(scopeToMatch)) {
|
|
74
|
+
return blockScope;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
docSet.maybeBuildEnumIndexes();
|
|
81
|
+
const ret = [];
|
|
82
|
+
let waitingScopes = new Set([]);
|
|
83
|
+
let scopeMatchEnded = true; // Always start new itemGroup the first time
|
|
84
|
+
|
|
85
|
+
for (const [blockN, block] of blocks.entries()) {
|
|
86
|
+
const [itemLength, itemType, itemSubtype] = utils.succinct.headerBytes(block.bs, 0);
|
|
87
|
+
const blockScope = docSet.unsuccinctifyScope(block.bs, itemType, itemSubtype, 0)[2];
|
|
88
|
+
const startBlockScope = ['scope', 'start', blockScope];
|
|
89
|
+
const endBlockScope = ['scope', 'end', blockScope];
|
|
90
|
+
const blockGrafts = docSet.unsuccinctifyGrafts(block.bg);
|
|
91
|
+
|
|
92
|
+
allBlockScopes = new Set(docSet.unsuccinctifyScopes(block.os)
|
|
93
|
+
.map(s => s[2])
|
|
94
|
+
.concat([blockScope]),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
for (
|
|
98
|
+
const item of blockGrafts.concat(
|
|
99
|
+
[
|
|
100
|
+
startBlockScope,
|
|
101
|
+
...docSet.unsuccinctifyItems(block.c, {}, block.nt.nByte(0), allBlockScopes),
|
|
102
|
+
endBlockScope,
|
|
103
|
+
],
|
|
104
|
+
).concat(
|
|
105
|
+
(blockN !== blocks.length - 1) ? [['token', 'lineSpace', ' ']] : [],
|
|
106
|
+
)
|
|
107
|
+
) {
|
|
108
|
+
if (item[0] === 'scope' && item[1] === 'start') {
|
|
109
|
+
waitingScopes.add(item[2]);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (item[0] === 'token' && waitingScopes.size > 0) {
|
|
113
|
+
for (const waiting of Array.from(waitingScopes)) {
|
|
114
|
+
allBlockScopes.add(waiting);
|
|
115
|
+
}
|
|
116
|
+
waitingScopes.clear();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (allScopesPresent()) {
|
|
120
|
+
if (scopeMatchEnded) {
|
|
121
|
+
ret.push([[...allBlockScopes], []]);
|
|
122
|
+
}
|
|
123
|
+
ret[ret.length - 1][1].push(item);
|
|
124
|
+
scopeMatchEnded = false;
|
|
125
|
+
} else {
|
|
126
|
+
scopeMatchEnded = true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (item[0] === 'scope' && item[1] === 'end') {
|
|
130
|
+
allBlockScopes.delete(item[2]);
|
|
131
|
+
waitingScopes.delete(item[2]);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return ret;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const sequenceItemsByMilestones = (docSet, blocks, byMilestones) => {
|
|
139
|
+
// Return array of [scopes, items]
|
|
140
|
+
// Scan block items
|
|
141
|
+
// If milestone found:
|
|
142
|
+
// - add array
|
|
143
|
+
// push item to last array
|
|
144
|
+
let allBlockScopes = new Set([]);
|
|
145
|
+
const milestoneFound = (item) => item[0] === 'scope' && item[1] === 'start' && byMilestones.includes(item[2]);
|
|
146
|
+
|
|
147
|
+
docSet.maybeBuildEnumIndexes();
|
|
148
|
+
const ret = [[[], []]];
|
|
149
|
+
|
|
150
|
+
for (const block of blocks) {
|
|
151
|
+
const [itemLength, itemType, itemSubtype] = utils.succinct.headerBytes(block.bs, 0);
|
|
152
|
+
const blockScope = docSet.unsuccinctifyScope(block.bs, itemType, itemSubtype, 0)[2];
|
|
153
|
+
const blockGrafts = docSet.unsuccinctifyGrafts(block.bg);
|
|
154
|
+
allBlockScopes.add(blockScope);
|
|
155
|
+
docSet.unsuccinctifyScopes(block.os).forEach(s => allBlockScopes.add(s[2]));
|
|
156
|
+
const items = blockGrafts.concat(
|
|
157
|
+
[blockScope].concat(
|
|
158
|
+
docSet.unsuccinctifyItems(block.c, {}, block.nt.nByte(0)),
|
|
159
|
+
),
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
for (const item of items) {
|
|
163
|
+
if (item[0] === 'scope' && item[1] === 'start') {
|
|
164
|
+
allBlockScopes.add(item[2]);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (milestoneFound(item)) {
|
|
168
|
+
ret[ret.length - 1][0] = [...allBlockScopes].sort();
|
|
169
|
+
ret.push([[], []]);
|
|
170
|
+
|
|
171
|
+
for (
|
|
172
|
+
const bs of [...allBlockScopes]
|
|
173
|
+
.filter(
|
|
174
|
+
s => {
|
|
175
|
+
const excludes = ['blockTag', 'verse', 'verses', 'chapter'];
|
|
176
|
+
return excludes.includes(s.split('/')[0]) || byMilestones.includes(s);
|
|
177
|
+
},
|
|
178
|
+
)
|
|
179
|
+
) {
|
|
180
|
+
allBlockScopes.delete(bs);
|
|
181
|
+
}
|
|
182
|
+
allBlockScopes.add(blockScope);
|
|
183
|
+
}
|
|
184
|
+
ret[ret.length - 1][1].push(item);
|
|
185
|
+
}
|
|
186
|
+
ret[ret.length - 1][1].push(['scope', 'end', blockScope]);
|
|
187
|
+
ret[ret.length - 1][1].push(['token', 'punctuation', '\n']);
|
|
188
|
+
}
|
|
189
|
+
ret[ret.length - 1][0] = [...allBlockScopes].sort();
|
|
190
|
+
return ret;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
module.exports = {
|
|
194
|
+
countItems,
|
|
195
|
+
itemsByIndex,
|
|
196
|
+
sequenceItemsByMilestones,
|
|
197
|
+
sequenceItemsByScopes,
|
|
198
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const rehash = docSet => {
|
|
2
|
+
docSet.preEnums = {};
|
|
3
|
+
|
|
4
|
+
for (const category of Object.keys(docSet.enums)) {
|
|
5
|
+
docSet.preEnums[category] = new Map();
|
|
6
|
+
}
|
|
7
|
+
docSet.maybeBuildEnumIndexes();
|
|
8
|
+
|
|
9
|
+
for (const document of docSet.documents()) {
|
|
10
|
+
for (const sequence of Object.values(document.sequences)) {
|
|
11
|
+
document.rerecordPreEnums(docSet, sequence);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
docSet.sortPreEnums();
|
|
15
|
+
const oldToNew = docSet.makeRehashEnumMap();
|
|
16
|
+
|
|
17
|
+
for (const document of docSet.documents()) {
|
|
18
|
+
for (const sequence of Object.values(document.sequences)) {
|
|
19
|
+
document.rewriteSequenceBlocks(sequence.id, oldToNew);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
docSet.buildEnums();
|
|
23
|
+
docSet.buildEnumIndexes();
|
|
24
|
+
return true;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const makeRehashEnumMap = docSet => {
|
|
28
|
+
const ret = {};
|
|
29
|
+
|
|
30
|
+
for (const [category, enumSuccinct] of Object.entries(docSet.enums)) {
|
|
31
|
+
ret[category] = [];
|
|
32
|
+
let pos = 0;
|
|
33
|
+
|
|
34
|
+
while (pos < enumSuccinct.length) {
|
|
35
|
+
const stringLength = enumSuccinct.byte(pos);
|
|
36
|
+
const enumString = enumSuccinct.countedString(pos);
|
|
37
|
+
|
|
38
|
+
if (docSet.preEnums[category].has(enumString)) {
|
|
39
|
+
ret[category].push(docSet.preEnums[category].get(enumString).enum);
|
|
40
|
+
} else {
|
|
41
|
+
ret[category].push(null);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
pos += stringLength + 1;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return ret;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
module.exports = {
|
|
51
|
+
rehash,
|
|
52
|
+
makeRehashEnumMap,
|
|
53
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const xre = require('xregexp');
|
|
2
|
+
|
|
3
|
+
const validateSelectors = (docSet, selectors) => {
|
|
4
|
+
if (typeof selectors !== 'object') {
|
|
5
|
+
throw new Error(`DocSet constructor expects selectors to be object, found ${typeof docSet.selectors}`);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const expectedSelectors = {};
|
|
9
|
+
|
|
10
|
+
for (const selector of docSet.processor.selectors) {
|
|
11
|
+
expectedSelectors[selector.name] = selector;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
for (const [name, value] of Object.entries(selectors)) {
|
|
15
|
+
if (!(name in expectedSelectors)) {
|
|
16
|
+
throw new Error(`Unexpected selector '${name}' (expected one of [${Object.keys(expectedSelectors).join(', ')}])`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (
|
|
20
|
+
(typeof value === 'string' && expectedSelectors[name].type !== 'string') ||
|
|
21
|
+
(typeof value === 'number' && expectedSelectors[name].type !== 'integer')
|
|
22
|
+
) {
|
|
23
|
+
throw new Error(`Selector '${name}' is of type ${typeof value} (expected ${expectedSelectors[name].type})`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (typeof value === 'number') {
|
|
27
|
+
if (!Number.isInteger(value)) {
|
|
28
|
+
throw new Error(`Value '${value}' of integer selector '${name}' is not an integer`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if ('min' in expectedSelectors[name] && value < expectedSelectors[name].min) {
|
|
32
|
+
throw new Error(`Value '${value}' is less than ${expectedSelectors[name].min}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if ('max' in expectedSelectors[name] && value > expectedSelectors[name].max) {
|
|
36
|
+
throw new Error(`Value '${value}' is greater than ${expectedSelectors[name].max}`);
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
if ('regex' in expectedSelectors[name] && !xre.exec(value, xre(expectedSelectors[name].regex), 0)) {
|
|
40
|
+
throw new Error(`Value '${value}' does not match regex '${expectedSelectors[name].regex}'`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if ('enum' in expectedSelectors[name] && !expectedSelectors[name].enum.includes(value)) {
|
|
45
|
+
throw new Error(`Value '${value}' of selector '${name}' is not in enum`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (const name of Object.keys(expectedSelectors)) {
|
|
50
|
+
if (!(name in selectors)) {
|
|
51
|
+
throw new Error(`Expected selector '${name}' not found`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return selectors;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
module.exports = { validateSelectors };
|
|
58
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const serializeSuccinct = docSet => {
|
|
2
|
+
const ret = {
|
|
3
|
+
id: docSet.id,
|
|
4
|
+
metadata: { selectors: docSet.selectors },
|
|
5
|
+
enums: {},
|
|
6
|
+
docs: {},
|
|
7
|
+
tags: Array.from(docSet.tags),
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
for (const [eK, eV] of Object.entries(docSet.enums)) {
|
|
11
|
+
eV.trim();
|
|
12
|
+
ret.enums[eK] = eV.base64();
|
|
13
|
+
}
|
|
14
|
+
ret.docs = {};
|
|
15
|
+
|
|
16
|
+
for (const docId of docSet.docIds) {
|
|
17
|
+
ret.docs[docId] = docSet.processor.documents[docId].serializeSuccinct();
|
|
18
|
+
}
|
|
19
|
+
return ret;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
module.exports = { serializeSuccinct };
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
const xre = require('xregexp');
|
|
2
|
+
const utils = require('../../util/index.cjs');
|
|
3
|
+
|
|
4
|
+
const unsuccinctifyBlock = (docSet, block, options) => {
|
|
5
|
+
docSet.maybeBuildEnumIndexes();
|
|
6
|
+
const succinctBlockScope = block.bs;
|
|
7
|
+
const [itemLength, itemType, itemSubtype] = utils.succinct.headerBytes(succinctBlockScope, 0);
|
|
8
|
+
const blockScope = docSet.unsuccinctifyScope(succinctBlockScope, itemType, itemSubtype, 0);
|
|
9
|
+
const blockGrafts = docSet.unsuccinctifyGrafts(block.bg);
|
|
10
|
+
const openScopes = docSet.unsuccinctifyScopes(block.os);
|
|
11
|
+
const includedScopes = docSet.unsuccinctifyScopes(block.is);
|
|
12
|
+
const nextToken = block.nt.nByte(0);
|
|
13
|
+
const blockItems = docSet.unsuccinctifyItems(block.c, options || {}, nextToken);
|
|
14
|
+
return {
|
|
15
|
+
bs: blockScope,
|
|
16
|
+
bg: blockGrafts,
|
|
17
|
+
c: blockItems,
|
|
18
|
+
os: openScopes,
|
|
19
|
+
is: includedScopes,
|
|
20
|
+
nt: nextToken,
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const unsuccinctifyItems = (docSet, succinct, options, nextToken, openScopes) => {
|
|
25
|
+
if (nextToken === undefined) {
|
|
26
|
+
throw new Error('nextToken (previously includeContext) must now be provided to unsuccinctifyItems');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (nextToken !== null && typeof nextToken !== 'number') {
|
|
30
|
+
throw new Error(`nextToken (previously includeContext) must be null or an integer, not ${typeof nextToken} '${JSON.stringify(nextToken)}' in unsuccinctifyItems`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const ret = [];
|
|
34
|
+
let pos = 0;
|
|
35
|
+
let tokenCount = nextToken || 0;
|
|
36
|
+
const scopes = new Set(openScopes || []);
|
|
37
|
+
|
|
38
|
+
while (pos < succinct.length) {
|
|
39
|
+
const [item, itemLength] = docSet.unsuccinctifyItem(succinct, pos, {});
|
|
40
|
+
|
|
41
|
+
if (item[0] === 'token') {
|
|
42
|
+
if ((Object.keys(options).length === 0) || options.tokens) {
|
|
43
|
+
if (nextToken !== null) {
|
|
44
|
+
item.push(item[0] === 'token' && item[1] === 'wordLike' ? tokenCount++ : null);
|
|
45
|
+
item.push([...scopes]);
|
|
46
|
+
}
|
|
47
|
+
ret.push(item);
|
|
48
|
+
}
|
|
49
|
+
} else if (item[0] === 'scope' && item[1] === 'start') {
|
|
50
|
+
scopes.add(item[2]);
|
|
51
|
+
|
|
52
|
+
if ((Object.keys(options).length === 0) || options.scopes) {
|
|
53
|
+
ret.push(item);
|
|
54
|
+
}
|
|
55
|
+
} else if (item[0] === 'scope' && item[1] === 'end') {
|
|
56
|
+
scopes.delete(item[2]);
|
|
57
|
+
|
|
58
|
+
if ((Object.keys(options).length === 0) || options.scopes) {
|
|
59
|
+
ret.push(item);
|
|
60
|
+
}
|
|
61
|
+
} else if (item[0] === 'graft') {
|
|
62
|
+
if ((Object.keys(options).length === 0) || options.grafts) {
|
|
63
|
+
ret.push(item);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
pos += itemLength;
|
|
67
|
+
}
|
|
68
|
+
return ret;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const unsuccinctifyItem = (docSet, succinct, pos, options) => {
|
|
72
|
+
let item = null;
|
|
73
|
+
const [itemLength, itemType, itemSubtype] = utils.succinct.headerBytes(succinct, pos);
|
|
74
|
+
|
|
75
|
+
switch (itemType) {
|
|
76
|
+
case utils.itemDefs.itemEnum.token:
|
|
77
|
+
if (Object.keys(options).length === 0 || options.tokens) {
|
|
78
|
+
item = docSet.unsuccinctifyToken(succinct, itemSubtype, pos);
|
|
79
|
+
}
|
|
80
|
+
break;
|
|
81
|
+
case utils.itemDefs.itemEnum.startScope:
|
|
82
|
+
case utils.itemDefs.itemEnum.endScope:
|
|
83
|
+
if (Object.keys(options).length === 0 || options.scopes) {
|
|
84
|
+
item = docSet.unsuccinctifyScope(succinct, itemType, itemSubtype, pos);
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
case utils.itemDefs.itemEnum.graft:
|
|
88
|
+
if (Object.keys(options).length === 0 || options.grafts) {
|
|
89
|
+
item = docSet.unsuccinctifyGraft(succinct, itemSubtype, pos);
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
return [item, itemLength];
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const unsuccinctifyPrunedItems = (docSet, block, options) => {
|
|
97
|
+
const openScopes = new Set(docSet.unsuccinctifyScopes(block.os).map(ri => ri[2]));
|
|
98
|
+
const requiredScopes = options.requiredScopes || [];
|
|
99
|
+
const anyScope = options.anyScope || false;
|
|
100
|
+
|
|
101
|
+
const allScopesInItem = () => {
|
|
102
|
+
for (const scope of requiredScopes) {
|
|
103
|
+
if (!openScopes.has(scope)) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const anyScopeInItem = () => {
|
|
111
|
+
for (const scope of requiredScopes) {
|
|
112
|
+
if (openScopes.has(scope)) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return (requiredScopes.length === 0);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const scopeTest = anyScope ? anyScopeInItem : allScopesInItem;
|
|
120
|
+
const charsTest = (item) =>
|
|
121
|
+
!options.withChars ||
|
|
122
|
+
options.withChars.length === 0 ||
|
|
123
|
+
(item[0] === 'token' && options.withChars.includes(item[2]));
|
|
124
|
+
const ret = [];
|
|
125
|
+
|
|
126
|
+
for (const item of docSet.unsuccinctifyItems(block.c, options, block.nt.nByte(0), openScopes)) {
|
|
127
|
+
if (item[0] === 'scope' && item[1] === 'start') {
|
|
128
|
+
openScopes.add(item[2]);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (scopeTest() && charsTest(item)) {
|
|
132
|
+
ret.push(item);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (item[0] === 'scope' && item[1] === 'end') {
|
|
136
|
+
openScopes.delete(item[2]);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return ret;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const unsuccinctifyItemsWithScriptureCV = (docSet, block, cv, options) => {
|
|
143
|
+
options = options || {};
|
|
144
|
+
const openScopes = new Set(docSet.unsuccinctifyScopes(block.os).map(ri => ri[2]));
|
|
145
|
+
|
|
146
|
+
const cvMatchFunction = () => {
|
|
147
|
+
if (xre.exec(cv, xre('^[1-9][0-9]*$'))) {
|
|
148
|
+
return () => openScopes.has(`chapter/${cv}`);
|
|
149
|
+
} else if (xre.exec(cv, xre('^[1-9][0-9]*-[1-9][0-9]*$'))) {
|
|
150
|
+
return () => {
|
|
151
|
+
const [fromC, toC] = cv.split('-').map(v => parseInt(v));
|
|
152
|
+
|
|
153
|
+
if (fromC > toC) {
|
|
154
|
+
throw new Error(`Chapter range must be from min to max, not '${cv}'`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
for (const scope of [...Array((toC - fromC) + 1).keys()].map(n => `chapter/${n + fromC}`)) {
|
|
158
|
+
if (openScopes.has(scope)) {
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return false;
|
|
163
|
+
};
|
|
164
|
+
} else if (xre.exec(cv, xre('^[1-9][0-9]*:[0-9]+$'))) {
|
|
165
|
+
return () => {
|
|
166
|
+
const [fromC, fromV] = cv.split(':').map(v => parseInt(v));
|
|
167
|
+
|
|
168
|
+
if (fromV === 0) {
|
|
169
|
+
return (
|
|
170
|
+
openScopes.has(`chapter/${fromC}`) &&
|
|
171
|
+
[...openScopes].filter(s => s.startsWith('verse')).length === 0
|
|
172
|
+
);
|
|
173
|
+
} else {
|
|
174
|
+
for (const scope of [`chapter/${fromC}`, `verse/${fromV}`]) {
|
|
175
|
+
if (!openScopes.has(scope)) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
} else if (xre.exec(cv, xre('^[1-9][0-9]*:[0-9]+-[1-9][0-9]*$'))) {
|
|
183
|
+
return () => {
|
|
184
|
+
const [fromC, vs] = cv.split(':');
|
|
185
|
+
const [fromV, toV] = vs.split('-').map(v => parseInt(v));
|
|
186
|
+
|
|
187
|
+
if (fromV > toV) {
|
|
188
|
+
throw new Error(`Verse range must be from min to max, not '${vs}'`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const chapterScope = `chapter/${fromC}`;
|
|
192
|
+
const verseScopes = [...Array((toV - fromV) + 1).keys()].map(n => `verse/${n + fromV}`);
|
|
193
|
+
|
|
194
|
+
if (!openScopes.has(chapterScope)) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
for (const scope of verseScopes) {
|
|
199
|
+
if (openScopes.has(scope)) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return fromV === 0 && [...openScopes].filter(s => s.startsWith('verse')).length === 0;
|
|
204
|
+
};
|
|
205
|
+
} else if (xre.exec(cv, xre('^[1-9][0-9]*:[0-9]+-[1-9][0-9]*:[0-9]+$'))) {
|
|
206
|
+
return () => {
|
|
207
|
+
const [fromCV, toCV] = cv.split('-');
|
|
208
|
+
const [fromC, fromV] = fromCV.split(':').map(c => parseInt(c));
|
|
209
|
+
const [toC, toV] = toCV.split(':').map(v => parseInt(v));
|
|
210
|
+
|
|
211
|
+
if (fromC > toC) {
|
|
212
|
+
throw new Error(`Chapter range must be from min to max, not '${fromC}-${toV}'`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const scopeArray = [...openScopes];
|
|
216
|
+
const chapterScopes = scopeArray.filter(s => s.startsWith('chapter/'));
|
|
217
|
+
|
|
218
|
+
if (chapterScopes.length > 1) {
|
|
219
|
+
throw new Error(`Expected zero or one chapter for item, found ${chapterScopes.length}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const chapterNo = parseInt(chapterScopes[0].split('/')[1]);
|
|
223
|
+
|
|
224
|
+
if ((chapterNo < fromC) || (chapterNo > toC)) {
|
|
225
|
+
return false;
|
|
226
|
+
} else if (chapterNo === fromC) {
|
|
227
|
+
return scopeArray.filter(
|
|
228
|
+
s =>
|
|
229
|
+
s.startsWith('verse/') &&
|
|
230
|
+
parseInt(s.split('/')[1]) >= fromV,
|
|
231
|
+
).length > 0 ||
|
|
232
|
+
(fromV === 0 && scopeArray.filter(s => s.startsWith('verse')).length === 0);
|
|
233
|
+
} else if (chapterNo === toC) {
|
|
234
|
+
return scopeArray.filter(
|
|
235
|
+
s =>
|
|
236
|
+
s.startsWith('verse/') &&
|
|
237
|
+
parseInt(s.split('/')[1]) <= toV,
|
|
238
|
+
).length > 0 ||
|
|
239
|
+
(toV === 0 && scopeArray.filter(s => s.startsWith('verse')).length === 0);
|
|
240
|
+
} else {
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
} else {
|
|
245
|
+
throw new Error(`Bad cv reference '${cv}'`);
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const itemMatchesCV = cvMatchFunction();
|
|
250
|
+
|
|
251
|
+
const itemInOptions = (item) => {
|
|
252
|
+
if (!options || Object.keys(options).length === 0) {
|
|
253
|
+
return true;
|
|
254
|
+
} else {
|
|
255
|
+
const itemType = item[0];
|
|
256
|
+
return (
|
|
257
|
+
(itemType === 'token' && 'tokens' in options) ||
|
|
258
|
+
(itemType === 'graft' && 'grafts' in options) ||
|
|
259
|
+
(itemType === 'scope' && 'scopes' in options)
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const ret = [];
|
|
265
|
+
|
|
266
|
+
for (const item of docSet.unsuccinctifyItems(block.c, {}, block.nt.nByte(0))) {
|
|
267
|
+
if (item[0] === 'scope' && item[1] === 'start') {
|
|
268
|
+
openScopes.add(item[2]);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (itemMatchesCV() && itemInOptions(item)) {
|
|
272
|
+
ret.push(item);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (item[0] === 'scope' && item[1] === 'end') {
|
|
276
|
+
openScopes.delete(item[2]);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return ret;
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
module.exports = {
|
|
283
|
+
unsuccinctifyBlock,
|
|
284
|
+
unsuccinctifyItem,
|
|
285
|
+
unsuccinctifyItems,
|
|
286
|
+
unsuccinctifyItemsWithScriptureCV,
|
|
287
|
+
unsuccinctifyPrunedItems,
|
|
288
|
+
};
|