1 | const {
|
2 | XM_TERM_TAG_DEFAULT_GRAPH,
|
3 | XM_TERM_TAG_NODE_NAMED,
|
4 | XM_TERM_TAG_NODE_BLANK,
|
5 | XM_TERM_TAG_LITERAL_SIMPLE,
|
6 | XM_TERM_TAG_LITERAL_LANGUAGED,
|
7 | XM_TERM_TAG_LITERAL_DATATYPED,
|
8 | XM_TERM_TAG_NODE,
|
9 | XM_TERM_TAG_LITERAL,
|
10 | XM_TERM_TAG_ANY,
|
11 | } = require('./constants.js');
|
12 |
|
13 | const X_RANK_PROPERTY_ACCESS = 1;
|
14 | const X_RANK_STRING_EQUALS = 3;
|
15 | const X_RANK_STRING_INDEX_OF = 3.5;
|
16 | const X_RANK_STRING_ENDS_WITH = 5;
|
17 | const X_RANK_CONCISE_STRING_EQUALS = 6;
|
18 | const X_RANK_CONCISE_PREFIXES_STRING_EQUALS = 6.5;
|
19 | const X_RANK_CONCISE_STRING_INDEX_OF = 7;
|
20 | const X_RANK_CONCISE_PREFIXES_STRING_INDEX_OF = 7.5;
|
21 | const X_RANK_REGEX = 11;
|
22 | const X_RANK_CONCISE_REGEX = 12;
|
23 | const X_RANK_TERM_COMPARE = 5.5;
|
24 |
|
25 | const F_SORT_GEN_RANK = (g_a, g_b) => g_a.rank - g_b.rank;
|
26 |
|
27 | const F_REDUCE_DECAY = (x_total, x_rank) => x_total + (x_rank / 2);
|
28 |
|
29 | const R_PLAIN_REGEX = /^(\^?)((?:\\.|[^.[\]{}?*+$()|])*)(\$?)$/;
|
30 |
|
31 | const RT_TEXT_PREFIX = /^[^~>_^@"*?]/;
|
32 |
|
33 | const H_TEST_RANGE = {
|
34 | [XM_TERM_TAG_DEFAULT_GRAPH]: {
|
35 | rank: X_RANK_PROPERTY_ACCESS,
|
36 |
|
37 | gen: sj => `${sj}.isDefaultGraph`,
|
38 | },
|
39 |
|
40 | [XM_TERM_TAG_NODE]: {
|
41 | rank: X_RANK_PROPERTY_ACCESS + (X_RANK_PROPERTY_ACCESS / 2),
|
42 |
|
43 | gen(sj) {
|
44 | return `(${sj}.isNamedNode || ${sj}.isBlankNode)`;
|
45 | },
|
46 | },
|
47 |
|
48 | [XM_TERM_TAG_LITERAL]: {
|
49 | rank: X_RANK_PROPERTY_ACCESS,
|
50 |
|
51 | gen(sj) {
|
52 | return `${sj}.isLiteral`;
|
53 | },
|
54 | },
|
55 |
|
56 | [XM_TERM_TAG_NODE_NAMED]: {
|
57 | rank: X_RANK_PROPERTY_ACCESS,
|
58 |
|
59 | gen(sj) {
|
60 | return `${sj}.isNamedNode`;
|
61 | },
|
62 | },
|
63 |
|
64 | [XM_TERM_TAG_NODE_BLANK]: {
|
65 | rank: X_RANK_PROPERTY_ACCESS,
|
66 |
|
67 | gen(sj) {
|
68 | return `${sj}.isBlankNode`;
|
69 | },
|
70 | },
|
71 |
|
72 | [XM_TERM_TAG_LITERAL_SIMPLE]: {
|
73 | rank: X_RANK_PROPERTY_ACCESS,
|
74 |
|
75 | gen(sj) {
|
76 | return `${sj}.isSimple`;
|
77 | },
|
78 | },
|
79 |
|
80 | [XM_TERM_TAG_LITERAL_LANGUAGED]: {
|
81 | rank: X_RANK_PROPERTY_ACCESS,
|
82 |
|
83 | gen(sj) {
|
84 | return `${sj}.isLanguaged`;
|
85 | },
|
86 | },
|
87 |
|
88 | [XM_TERM_TAG_LITERAL_DATATYPED]: {
|
89 | rank: X_RANK_PROPERTY_ACCESS,
|
90 |
|
91 | gen(sj) {
|
92 | return `${sj}.isDatatyped`;
|
93 | },
|
94 | },
|
95 | };
|
96 |
|
97 |
|
98 | function compile_tags(xm_tags) {
|
99 |
|
100 | if(xm_tags && XM_TERM_TAG_ANY !== xm_tags) {
|
101 |
|
102 | if(xm_tags in H_TEST_RANGE) {
|
103 | return H_TEST_RANGE[xm_tags];
|
104 | }
|
105 |
|
106 | else {
|
107 | let a_selectors = [];
|
108 |
|
109 |
|
110 | for(let [sm_test, g_select] of Object.entries(H_TEST_RANGE)) {
|
111 | let xm_test = +sm_test;
|
112 |
|
113 |
|
114 | if(xm_test === (xm_test & xm_tags)) {
|
115 |
|
116 | xm_tags &= ~xm_test;
|
117 |
|
118 |
|
119 | a_selectors.push(g_select);
|
120 |
|
121 |
|
122 | if(!xm_tags) break;
|
123 | }
|
124 | }
|
125 |
|
126 |
|
127 | if(xm_tags) {
|
128 | console.assert(`case not covered: ${xm_tags}`);
|
129 | }
|
130 |
|
131 |
|
132 | return {
|
133 | rank: a_selectors.map(g => g.rank).reduce(F_REDUCE_DECAY, 0),
|
134 |
|
135 | gen(sj_target) {
|
136 | return '('+a_selectors.map(g => g.gen(sj_target)).join(' && ')+')';
|
137 | },
|
138 | };
|
139 | }
|
140 | }
|
141 | }
|
142 |
|
143 |
|
144 | const pretag = (g, sj_target) => g.tags? compile_tags(g.tags).gen(sj_target)+' && ': '';
|
145 |
|
146 | const wrap = (s, b) => b? `(${s})`: s;
|
147 |
|
148 | const escape_str = s => s.replace(/\\(.)/g, '$1').replace(/'/g, '\\\'').replace(/\r\n\v\f/g, '');
|
149 |
|
150 | const track_prefix = (si_var_iri, si_prefix, s_suffix) => ({
|
151 | declare: `let ${si_var_iri} = null;`,
|
152 |
|
153 |
|
154 | prefix: {
|
155 | [escape_str(si_prefix)]: `${si_var_iri} = p_iri+'${escape_str(s_suffix)}';`,
|
156 | },
|
157 | });
|
158 |
|
159 | const H_COMPILERS = {
|
160 | and(a_items) {
|
161 | let a_compiled = a_items.map(g => this.compile(g));
|
162 |
|
163 |
|
164 | let b_tight_tags = false;
|
165 |
|
166 |
|
167 | if(a_compiled.slice(1).every(g => g === a_compiled[0].tags)) {
|
168 |
|
169 | a_compiled.push(compile_tags(a_compiled[0].tags));
|
170 | }
|
171 |
|
172 | else {
|
173 |
|
174 | b_tight_tags = true;
|
175 | }
|
176 |
|
177 |
|
178 | a_compiled.sort(F_SORT_GEN_RANK);
|
179 |
|
180 | return {
|
181 | rank: a_compiled.reduce(F_REDUCE_DECAY, 0),
|
182 |
|
183 | test(g_quad) {
|
184 | for(let g_compiled of a_compiled) {
|
185 | if(!g_compiled.test(g_quad)) return false;
|
186 | }
|
187 |
|
188 | return true;
|
189 | },
|
190 |
|
191 | gen(sj_target) {
|
192 | let f_map_gen = b_tight_tags
|
193 | ? (k, i) => a_compiled[i].auto(sj_target)
|
194 | : k => k.gen(sj_target);
|
195 |
|
196 | return wrap(a_compiled.map(f_map_gen).join(' && '), a_compiled.length > 1);
|
197 | },
|
198 | };
|
199 | },
|
200 |
|
201 | or(a_items) {
|
202 | let a_compiled = a_items.map(g => this.compile(g)).sort(F_SORT_GEN_RANK);
|
203 |
|
204 | return {
|
205 | rank: a_compiled.reduce(F_REDUCE_DECAY, 0),
|
206 |
|
207 | test(g_quad) {
|
208 | for(let g_compiled of a_compiled) {
|
209 | if(g_compiled.test(g_quad)) return true;
|
210 | }
|
211 |
|
212 | return false;
|
213 | },
|
214 |
|
215 | gen(sj_target) {
|
216 | return wrap(a_compiled.map(k => k.auto(sj_target)).join(' || '), a_compiled.length > 1);
|
217 | },
|
218 | };
|
219 | },
|
220 |
|
221 | not(g_not) {
|
222 | let k_not = this.compile(g_not);
|
223 |
|
224 | return {
|
225 | rank: k_not.rank,
|
226 |
|
227 | test(g_quad) {
|
228 | return !k_not.test(g_quad);
|
229 | },
|
230 |
|
231 | gen(sj_target) {
|
232 | return `!(${pretag(k_not, sj_target)}${k_not.gen(sj_target)})`;
|
233 | },
|
234 | };
|
235 | },
|
236 |
|
237 | range(g_range) {
|
238 | let {
|
239 | term: g_term,
|
240 | tags: xm_tags,
|
241 | } = g_range;
|
242 |
|
243 |
|
244 | let k_term = g_term? this.compile(g_range.term): null;
|
245 |
|
246 |
|
247 | let x_rank = 0;
|
248 | let fsj_tag = null;
|
249 |
|
250 |
|
251 | if(k_term && k_term.tags) {
|
252 | xm_tags |= k_term.tags;
|
253 | }
|
254 |
|
255 |
|
256 | if(xm_tags && XM_TERM_TAG_ANY !== xm_tags) {
|
257 |
|
258 | if(xm_tags in H_TEST_RANGE) {
|
259 | ({
|
260 | rank: x_rank,
|
261 | gen: fsj_tag,
|
262 | } = H_TEST_RANGE[xm_tags]);
|
263 | }
|
264 |
|
265 | else {
|
266 | let a_selectors = [];
|
267 |
|
268 |
|
269 | for(let [sm_test, g_select] of Object.entries(H_TEST_RANGE)) {
|
270 | let xm_test = +sm_test;
|
271 |
|
272 |
|
273 | if(xm_test === (xm_test & xm_tags)) {
|
274 |
|
275 | xm_tags &= ~xm_test;
|
276 |
|
277 |
|
278 | a_selectors.push(g_select);
|
279 |
|
280 |
|
281 | if(!xm_tags) break;
|
282 | }
|
283 | }
|
284 |
|
285 |
|
286 | if(xm_tags) {
|
287 | console.assert(`case not covered: ${xm_tags}`);
|
288 | }
|
289 |
|
290 |
|
291 | x_rank = a_selectors.map(g => g.rank).reduce(F_REDUCE_DECAY, 0);
|
292 |
|
293 |
|
294 | fsj_tag = sj_target => a_selectors.map(g => g.gen(sj_target)).join(' && ');
|
295 | }
|
296 | }
|
297 |
|
298 |
|
299 | if(fsj_tag) {
|
300 |
|
301 | if(k_term) {
|
302 | return {
|
303 | rank: X_RANK_PROPERTY_ACCESS,
|
304 |
|
305 | gen(sj_target) {
|
306 | return `${fsj_tag(sj_target)} && ${k_term.gen(sj_target)}`;
|
307 | },
|
308 | };
|
309 | }
|
310 |
|
311 | else {
|
312 | return {
|
313 | rank: x_rank,
|
314 | gen: fsj_tag,
|
315 | };
|
316 | }
|
317 | }
|
318 |
|
319 | else {
|
320 | return {
|
321 | rank: 0,
|
322 |
|
323 | gen: () => 'true',
|
324 | };
|
325 | }
|
326 | },
|
327 |
|
328 | iri(p_iri) {
|
329 | return {
|
330 | tags: XM_TERM_TAG_NODE_NAMED,
|
331 |
|
332 | rank: X_RANK_STRING_EQUALS,
|
333 |
|
334 | gen(sj_target) {
|
335 | return `'${escape_str(p_iri)}' === ${sj_target}.value`;
|
336 | },
|
337 | };
|
338 | },
|
339 |
|
340 | pname(g_pname) {
|
341 | let {
|
342 | prefix: si_prefix,
|
343 | local: s_suffix,
|
344 | } = g_pname;
|
345 |
|
346 | let si_var_iri = this.acquire('p_iri');
|
347 |
|
348 | return {
|
349 | tags: XM_TERM_TAG_NODE_NAMED,
|
350 |
|
351 | rank: X_RANK_STRING_EQUALS,
|
352 |
|
353 | hooks() {
|
354 | return track_prefix(si_var_iri, si_prefix, s_suffix);
|
355 | },
|
356 |
|
357 | gen(sj_target) {
|
358 | return `${si_var_iri} === ${sj_target}.value`;
|
359 | },
|
360 | };
|
361 | },
|
362 |
|
363 | language(s_lang) {
|
364 | return {
|
365 |
|
366 |
|
367 |
|
368 |
|
369 | rank: X_RANK_STRING_EQUALS,
|
370 |
|
371 | gen(sj_target) {
|
372 | return `'${escape_str(s_lang).toLowerCase()}' === ${sj_target}.language`;
|
373 | },
|
374 | };
|
375 | },
|
376 |
|
377 | datatype(g_datatype) {
|
378 | let k_datatype = this.compile(g_datatype);
|
379 |
|
380 | return {
|
381 | tags: XM_TERM_TAG_LITERAL_DATATYPED,
|
382 |
|
383 | rank: X_RANK_PROPERTY_ACCESS,
|
384 |
|
385 | gen(sj_target) {
|
386 | return k_datatype.toString( `${sj_target}.datatype`);
|
387 | },
|
388 | };
|
389 | },
|
390 |
|
391 | literal(g_literal) {
|
392 | let {
|
393 | contents: s_contents,
|
394 | post: g_post,
|
395 | } = g_literal;
|
396 |
|
397 | let f_contents = sj_target => `'${escape_str(s_contents)}' === ${sj_target}.value`;
|
398 |
|
399 | if(g_post) {
|
400 | let k_post = this.compile(g_post);
|
401 |
|
402 | return {
|
403 | tags: k_post.tags,
|
404 |
|
405 | rank: [X_RANK_STRING_EQUALS, k_post.rank].reduce(F_REDUCE_DECAY, 0),
|
406 |
|
407 | gen(sj_target) {
|
408 | return `${f_contents(sj_target)} && ${k_post.gen(sj_target)}`;
|
409 | },
|
410 | };
|
411 | }
|
412 | else {
|
413 | return {
|
414 | tags: XM_TERM_TAG_LITERAL_SIMPLE,
|
415 |
|
416 | rank: X_RANK_STRING_EQUALS,
|
417 |
|
418 | gen(sj_contents) {
|
419 | return f_contents(sj_contents);
|
420 | },
|
421 | };
|
422 | }
|
423 | },
|
424 |
|
425 | default_graph() {
|
426 | return {
|
427 | tags: XM_TERM_TAG_DEFAULT_GRAPH,
|
428 |
|
429 | rank: 0,
|
430 |
|
431 | gen: () => 'true',
|
432 | };
|
433 | },
|
434 |
|
435 | anon() {
|
436 | return {
|
437 | tags: XM_TERM_TAG_NODE_BLANK,
|
438 |
|
439 | rank: 0,
|
440 |
|
441 | gen: () => 'true',
|
442 | };
|
443 | },
|
444 |
|
445 | bnode(s_label) {
|
446 | if(s_label) {
|
447 | return {
|
448 | tags: XM_TERM_TAG_NODE_BLANK,
|
449 |
|
450 | rank: X_RANK_STRING_EQUALS,
|
451 |
|
452 | gen(sj_target) {
|
453 | return `'${escape_str(s_label)}' === ${sj_target}.value`;
|
454 | },
|
455 | };
|
456 | }
|
457 | else {
|
458 | return {
|
459 | tags: XM_TERM_TAG_NODE_BLANK,
|
460 |
|
461 | rank: 0,
|
462 |
|
463 | gen: () => 'true',
|
464 | };
|
465 | }
|
466 | },
|
467 |
|
468 | regex(g_regex) {
|
469 | let r_pattern = g_regex.pattern;
|
470 | let s_source = r_pattern.source;
|
471 |
|
472 |
|
473 | if(g_regex.concise || g_regex.verbose) {
|
474 | let m_plain = R_PLAIN_REGEX.exec(s_source);
|
475 |
|
476 |
|
477 | if(m_plain && r_pattern.flags.indexOf('i') < 0) {
|
478 | let [, s_anchor_start, s_text, s_anchor_end] = m_plain;
|
479 |
|
480 |
|
481 | if(s_anchor_start) {
|
482 |
|
483 | switch(s_text[0]) {
|
484 |
|
485 | case '@':
|
486 | case '^': {
|
487 | let b_datatype = '^' === s_text[0];
|
488 | let b_prefixed_datatype = b_datatype && !g_regex.verbose && '>' !== s_text[1];
|
489 | let sj_prefixes = b_prefixed_datatype? 'h_prefixes': '';
|
490 |
|
491 | let xm_tags = b_datatype? XM_TERM_TAG_LITERAL_DATATYPED: XM_TERM_TAG_LITERAL_LANGUAGED;
|
492 |
|
493 |
|
494 | let s_pattern = b_datatype? escape_str(s_text.slice(1)): escape_str(s_text);
|
495 |
|
496 |
|
497 | if(s_anchor_end) {
|
498 | return {
|
499 | tags: xm_tags,
|
500 |
|
501 | prefixes: !!sj_prefixes,
|
502 |
|
503 | ...(b_prefixed_datatype
|
504 | ? {
|
505 | rank: X_RANK_CONCISE_PREFIXES_STRING_EQUALS,
|
506 |
|
507 | gen(sj_target) {
|
508 | return `'${s_pattern}' === ${sj_target}.concise(${sj_prefixes})`;
|
509 | },
|
510 | }
|
511 | : {
|
512 | rank: X_RANK_CONCISE_STRING_EQUALS,
|
513 |
|
514 | gen(sj_target) {
|
515 | return `'${s_pattern}' === ${sj_target}.concise()`;
|
516 | },
|
517 | }),
|
518 | };
|
519 | }
|
520 |
|
521 | else {
|
522 |
|
523 | let i_contents = s_text.indexOf('"');
|
524 | if(i_contents >= 0) {
|
525 | let s_contents = s_text.slice(i_contents);
|
526 |
|
527 |
|
528 | if(s_contents) {
|
529 | return {
|
530 | tags: xm_tags,
|
531 |
|
532 | prefixes: !!sj_prefixes,
|
533 |
|
534 | ...b_prefixed_datatype
|
535 | ? {
|
536 | rank: X_RANK_CONCISE_PREFIXES_STRING_INDEX_OF,
|
537 |
|
538 | gen(sj_target) {
|
539 | return `0 === ${sj_target}.concise(${sj_prefixes}).indexOf('${s_pattern}')`;
|
540 | },
|
541 | }
|
542 | : {
|
543 | rank: X_RANK_CONCISE_STRING_INDEX_OF,
|
544 |
|
545 | gen(sj_target) {
|
546 | return `0 === ${sj_target}.concise().indexOf('${s_pattern}')`;
|
547 | },
|
548 | },
|
549 | };
|
550 | }
|
551 |
|
552 | else if(b_datatype) {
|
553 | return {
|
554 | tags: xm_tags,
|
555 |
|
556 | prefixes: !!sj_prefixes,
|
557 |
|
558 | ...b_prefixed_datatype
|
559 | ? {
|
560 | rank: X_RANK_CONCISE_PREFIXES_STRING_EQUALS,
|
561 |
|
562 | gen(sj_target) {
|
563 | return `'${s_pattern}' === ${sj_target}.datatype.concise(${sj_prefixes})`;
|
564 | },
|
565 | }
|
566 | : {
|
567 | rank: X_RANK_CONCISE_STRING_EQUALS,
|
568 |
|
569 | gen(sj_target) {
|
570 | return `'${s_pattern}' === ${sj_target}.datatype.concise()`;
|
571 | },
|
572 | },
|
573 | };
|
574 | }
|
575 |
|
576 | else {
|
577 | return {
|
578 | tags: xm_tags,
|
579 |
|
580 | rank: X_RANK_CONCISE_STRING_INDEX_OF,
|
581 |
|
582 | gen(sj_target) {
|
583 | return `'${s_pattern}' === ${sj_target}.language`;
|
584 | },
|
585 | };
|
586 | }
|
587 | }
|
588 |
|
589 | else if(b_datatype) {
|
590 | return {
|
591 | tags: xm_tags,
|
592 |
|
593 | prefixes: !!sj_prefixes,
|
594 |
|
595 | ...b_prefixed_datatype
|
596 | ? {
|
597 | rank: X_RANK_CONCISE_PREFIXES_STRING_INDEX_OF,
|
598 |
|
599 | gen(sj_target) {
|
600 | return `0 === ${sj_target}.datatype.concise(${sj_prefixes}).indexOf('${s_pattern}')`;
|
601 | },
|
602 | }
|
603 | : {
|
604 | rank: X_RANK_CONCISE_STRING_INDEX_OF,
|
605 |
|
606 | gen(sj_target) {
|
607 | return `0 === ${sj_target}.datatype.concise().indexOf('${s_pattern}')`;
|
608 | },
|
609 | },
|
610 | };
|
611 | }
|
612 |
|
613 | else {
|
614 | return {
|
615 | tags: xm_tags,
|
616 |
|
617 | rank: X_RANK_STRING_INDEX_OF,
|
618 |
|
619 | gen(sj_target) {
|
620 | return `0 === ${sj_target}.language.indexOf('${s_pattern}')`;
|
621 | },
|
622 | };
|
623 | }
|
624 | }
|
625 | }
|
626 |
|
627 |
|
628 | case '"': {
|
629 | let s_contents = s_text.slice(1);
|
630 |
|
631 |
|
632 | if(s_anchor_end) {
|
633 | return {
|
634 | tags: XM_TERM_TAG_LITERAL_SIMPLE,
|
635 |
|
636 | rank: X_RANK_STRING_EQUALS,
|
637 |
|
638 | gen(sj_target) {
|
639 | return `'${s_contents}' === ${sj_target}.value`;
|
640 | },
|
641 | };
|
642 | }
|
643 |
|
644 | else {
|
645 | return {
|
646 | tags: XM_TERM_TAG_LITERAL_SIMPLE,
|
647 |
|
648 | rank: X_RANK_STRING_INDEX_OF,
|
649 |
|
650 | gen(sj_target) {
|
651 | return `0 === ${sj_target}.value.indexOf('${s_contents}')`;
|
652 | },
|
653 | };
|
654 | }
|
655 | }
|
656 |
|
657 |
|
658 | case '>': {
|
659 | let p_iri = s_text.slice(1);
|
660 |
|
661 |
|
662 | if(s_anchor_end) {
|
663 | return {
|
664 | tags: XM_TERM_TAG_LITERAL_SIMPLE,
|
665 |
|
666 | rank: X_RANK_STRING_EQUALS,
|
667 |
|
668 | gen(sj_target) {
|
669 | return `'${p_iri}' === ${sj_target}.value`;
|
670 | },
|
671 | };
|
672 | }
|
673 |
|
674 | else {
|
675 | return {
|
676 | tags: XM_TERM_TAG_LITERAL_SIMPLE,
|
677 |
|
678 | rank: X_RANK_STRING_INDEX_OF,
|
679 |
|
680 | gen(sj_target) {
|
681 | return `0 === ${sj_target}.value.indexOf('${p_iri}')`;
|
682 | },
|
683 | };
|
684 | }
|
685 | }
|
686 |
|
687 |
|
688 | default: {
|
689 | let i_colon = s_text.indexOf(':');
|
690 | let si_prefix = s_text.slice(0, i_colon);
|
691 | let s_suffix = s_text.slice(i_colon+1);
|
692 |
|
693 | let si_var_iri = this.acquire('p_iri');
|
694 |
|
695 |
|
696 | if(s_anchor_end) {
|
697 |
|
698 | return {
|
699 | tags: XM_TERM_TAG_NODE_NAMED,
|
700 |
|
701 | rank: X_RANK_STRING_EQUALS,
|
702 |
|
703 | hooks() {
|
704 | return track_prefix(si_var_iri, si_prefix, s_suffix);
|
705 | },
|
706 |
|
707 | gen(sj_target) {
|
708 | return `${si_var_iri} === ${sj_target}.value`;
|
709 | },
|
710 | };
|
711 | }
|
712 |
|
713 | else {
|
714 |
|
715 | return {
|
716 | tags: XM_TERM_TAG_NODE_NAMED,
|
717 |
|
718 | rank: X_RANK_STRING_INDEX_OF,
|
719 |
|
720 | hooks() {
|
721 | return track_prefix(si_var_iri, si_prefix, s_suffix);
|
722 | },
|
723 |
|
724 | gen(sj_target) {
|
725 | return `0 === ${sj_target}.value.indexOf(${si_var_iri})`;
|
726 | },
|
727 | };
|
728 | }
|
729 | }
|
730 | }
|
731 | }
|
732 |
|
733 | else if(s_anchor_end) {
|
734 |
|
735 | return {
|
736 | rank: X_RANK_STRING_ENDS_WITH,
|
737 |
|
738 | gen(sj_target) {
|
739 | return `${sj_target}.concise().endsWith('${escape_str(s_text)}')`;
|
740 | },
|
741 | };
|
742 | }
|
743 |
|
744 | else {
|
745 |
|
746 | return {
|
747 | rank: X_RANK_STRING_INDEX_OF,
|
748 |
|
749 | gen(sj_target) {
|
750 | return `${sj_target}.concise().indexOf('${escape_str(s_text)}') > -1`;
|
751 | },
|
752 | };
|
753 | }
|
754 | }
|
755 |
|
756 | else {
|
757 | let si_var_regex = this.acquire('rt_term');
|
758 |
|
759 | return {
|
760 | rank: X_RANK_CONCISE_REGEX,
|
761 |
|
762 | prefixes: true,
|
763 |
|
764 | hooks() {
|
765 | return {
|
766 | declare: `let ${si_var_regex} = /${s_source}/${r_pattern.flags};`,
|
767 | };
|
768 | },
|
769 |
|
770 | gen(sj_target) {
|
771 | return `${si_var_regex}.test(${sj_target}.concise(${g_regex.verbose? '': 'h_prefixes'}))`;
|
772 | },
|
773 | };
|
774 | }
|
775 | }
|
776 |
|
777 | else {
|
778 | let si_var_regex = this.acquire('rt_term');
|
779 |
|
780 | return {
|
781 | rank: X_RANK_REGEX,
|
782 |
|
783 | hooks() {
|
784 | return {
|
785 | declare: `let ${si_var_regex} = /${s_source}/${r_pattern.flags};`,
|
786 | };
|
787 | },
|
788 |
|
789 | gen(sj_target) {
|
790 | return `${si_var_regex}.test(${sj_target}.value)`;
|
791 | },
|
792 | };
|
793 | }
|
794 | },
|
795 |
|
796 | ref(s_ref) {
|
797 | this._as_destructures.add(s_ref);
|
798 |
|
799 | return {
|
800 | rank: X_RANK_TERM_COMPARE,
|
801 |
|
802 | gen(sj_target) {
|
803 | return `${sj_target}.equals(kt_${s_ref})`;
|
804 | },
|
805 | };
|
806 | },
|
807 | };
|
808 |
|
809 |
|
810 | class Context {
|
811 | constructor() {
|
812 | this._c_vars =0;
|
813 | this._s_declare = '';
|
814 | this._h_prefix_handlers = {};
|
815 | this._b_capture_prefixes = false;
|
816 | this._as_destructures = new Set();
|
817 | this._a_refs = [];
|
818 | this._c_patterns = 0;
|
819 | }
|
820 |
|
821 | start_pattern() {
|
822 | this._a_refs.push({});
|
823 | this._c_patterns += 1;
|
824 | }
|
825 |
|
826 | acquire(s_var) {
|
827 | return `${s_var}_${this._c_vars++}`;
|
828 | }
|
829 |
|
830 | push_hook(g_hook) {
|
831 | if(g_hook.declare) {
|
832 | this._s_declare += g_hook.declare;
|
833 | }
|
834 |
|
835 | if(g_hook.prefix) {
|
836 | let h_handlers = this._h_prefix_handlers;
|
837 |
|
838 | let h_prefix = g_hook.prefix;
|
839 | for(let si_prefix in h_prefix) {
|
840 | (h_handlers[si_prefix] = h_handlers[si_prefix] || [])
|
841 | .push(h_prefix[si_prefix]);
|
842 | }
|
843 | }
|
844 | }
|
845 |
|
846 | get destructures() {
|
847 | return this._as_destructures;
|
848 | }
|
849 |
|
850 | get declarations() {
|
851 | return this._s_declare;
|
852 | }
|
853 |
|
854 | get prefix_handlers() {
|
855 | return this._h_prefix_handlers;
|
856 | }
|
857 |
|
858 | get capture_prefixes() {
|
859 | return this._b_capture_prefixes;
|
860 | }
|
861 |
|
862 | compile(g_src) {
|
863 | let {
|
864 | type: s_type,
|
865 | value: w_value,
|
866 | } = g_src;
|
867 |
|
868 | if(!(s_type in H_COMPILERS)) {
|
869 | console.assert(`syntax object type ${s_type} not mapped`);
|
870 | }
|
871 |
|
872 | let g_compiled = H_COMPILERS[s_type].call(this, w_value);
|
873 |
|
874 |
|
875 | if(g_compiled.hooks) {
|
876 | this.push_hook(g_compiled.hooks());
|
877 | }
|
878 |
|
879 |
|
880 | if(g_compiled.prefixes) {
|
881 | this._b_capture_prefixes = true;
|
882 | }
|
883 |
|
884 | g_compiled.auto = (sj_target) => {
|
885 | let sj_compiled = g_compiled.gen(sj_target);
|
886 |
|
887 | if(g_compiled.tags) {
|
888 | let sj_pretag = pretag(g_compiled, sj_target);
|
889 |
|
890 | return wrap(sj_pretag+sj_compiled, sj_pretag);
|
891 | }
|
892 | else {
|
893 | return sj_compiled;
|
894 | }
|
895 | };
|
896 |
|
897 | return g_compiled;
|
898 | }
|
899 | }
|
900 |
|
901 |
|
902 | class Pattern {
|
903 | constructor(k_context, g_source) {
|
904 | let x_rank = 0;
|
905 | let c_decay = 0;
|
906 |
|
907 | let a_conditions = this._a_conditions = [];
|
908 | let a_destructures = this._a_destructures = [];
|
909 | this._g_graph = null;
|
910 |
|
911 | k_context._c_patterns += 1;
|
912 |
|
913 | if(g_source.graph) {
|
914 | let g_compiled = k_context.compile(g_source.graph);
|
915 |
|
916 | x_rank += g_compiled.rank / (Math.pow(2, c_decay++));
|
917 |
|
918 | let b_default_graph = XM_TERM_TAG_DEFAULT_GRAPH === g_compiled.tags;
|
919 |
|
920 | if(b_default_graph) {
|
921 | this._g_graph = {
|
922 | compiled: g_compiled,
|
923 | condition: g_compiled.auto( `kt_graph`),
|
924 | variable: 'b_default_graph',
|
925 | default: true,
|
926 | };
|
927 |
|
928 | a_conditions.push('b_default_graph');
|
929 | }
|
930 | else {
|
931 | let si_var_graph = k_context.acquire('b_graph_pass');
|
932 |
|
933 | this._g_graph = {
|
934 | compiled: g_compiled,
|
935 | condition: g_compiled.auto( `kt_graph`),
|
936 | variable: b_default_graph? 'b_default_graph': si_var_graph,
|
937 | default: b_default_graph,
|
938 | };
|
939 |
|
940 | a_conditions.push(si_var_graph);
|
941 | }
|
942 | }
|
943 |
|
944 | if(g_source.object) {
|
945 | let g_compiled = k_context.compile(g_source.object);
|
946 |
|
947 | x_rank += g_compiled.rank / (Math.pow(2, c_decay++));
|
948 |
|
949 | a_conditions.push(g_compiled.auto( `kt_object`));
|
950 |
|
951 | a_destructures.push('object');
|
952 | }
|
953 |
|
954 | if(g_source.subject) {
|
955 | let g_compiled = k_context.compile(g_source.subject);
|
956 |
|
957 | x_rank += g_compiled.rank / (Math.pow(2, c_decay++));
|
958 |
|
959 | a_conditions.push(g_compiled.auto( `kt_subject`));
|
960 |
|
961 | a_destructures.push('subject');
|
962 | }
|
963 |
|
964 | if(g_source.predicate) {
|
965 | let g_compiled = k_context.compile(g_source.predicate);
|
966 |
|
967 | x_rank += g_compiled.rank / (Math.pow(2, c_decay++));
|
968 |
|
969 | a_conditions.push(g_compiled.auto( `kt_predicate`));
|
970 |
|
971 | a_destructures.push('predicate');
|
972 | }
|
973 |
|
974 | this._x_rank = x_rank;
|
975 | }
|
976 |
|
977 | get destructures() {
|
978 | return this._a_destructures;
|
979 | }
|
980 |
|
981 | get graph() {
|
982 | return this._g_graph;
|
983 | }
|
984 |
|
985 | get rank() {
|
986 | return this._x_rank;
|
987 | }
|
988 |
|
989 | get conditions() {
|
990 | return this._a_conditions.join('\n\t && ');
|
991 | }
|
992 | }
|
993 |
|
994 | const gobble = (s_text) => {
|
995 | let m_indent = /^(?:\s*\n)+([ \t]*)/.exec(s_text);
|
996 | if(m_indent) {
|
997 | let r_indent = new RegExp('\\n'+m_indent[1], 'g');
|
998 | return s_text.trim().replace(r_indent, '\n');
|
999 | }
|
1000 | else {
|
1001 | return s_text;
|
1002 | }
|
1003 | };
|
1004 |
|
1005 | function prepare(a_sources) {
|
1006 | let k_context = new Context();
|
1007 |
|
1008 | let a_patterns = a_sources
|
1009 | .map(g_source => new Pattern(k_context, g_source))
|
1010 | .sort(F_SORT_GEN_RANK);
|
1011 |
|
1012 | let sj_init = '';
|
1013 | let sj_events = '';
|
1014 |
|
1015 | let a_graphs = [];
|
1016 |
|
1017 |
|
1018 | for(let k_pattern of a_patterns) {
|
1019 |
|
1020 | if(k_pattern.graph) {
|
1021 | a_graphs.push(k_pattern.graph);
|
1022 | }
|
1023 |
|
1024 |
|
1025 | if(k_pattern.declarations) {
|
1026 | sj_init += k_pattern.declarations;
|
1027 | }
|
1028 | }
|
1029 |
|
1030 |
|
1031 | {
|
1032 | let s_branches = '';
|
1033 |
|
1034 |
|
1035 | let h_prefix_cases = k_context.prefix_handlers;
|
1036 | for(let si_prefix in h_prefix_cases) {
|
1037 | s_branches += `
|
1038 | case '${si_prefix}': {
|
1039 | ${h_prefix_cases[si_prefix].join('\n')}
|
1040 | break;
|
1041 | }
|
1042 | `;
|
1043 | }
|
1044 |
|
1045 | if(s_branches || k_context.capture_prefixes) {
|
1046 | sj_init += `let h_prefixes = {};\n`;
|
1047 |
|
1048 | sj_events += `
|
1049 | ds_reader.on('prefix', (si_prefix, p_iri) => {
|
1050 | ${k_context.capture_prefixes? /* syntax: js */ `h_prefixes[si_prefix] = p_iri;`: ''}
|
1051 |
|
1052 | switch(si_prefix) {
|
1053 | ${s_branches}
|
1054 | }
|
1055 | });
|
1056 | `;
|
1057 | }
|
1058 | }
|
1059 |
|
1060 |
|
1061 | if(a_graphs.length) {
|
1062 |
|
1063 | let a_graphs_nond = [];
|
1064 |
|
1065 |
|
1066 | let b_default_graph = false;
|
1067 | {
|
1068 |
|
1069 | for(let g_graph of a_graphs) {
|
1070 | if(g_graph.default) {
|
1071 | b_default_graph = true;
|
1072 | }
|
1073 | else {
|
1074 | a_graphs_nond.push(g_graph);
|
1075 | }
|
1076 | }
|
1077 |
|
1078 |
|
1079 | if(b_default_graph) {
|
1080 | sj_init += `let b_default_graph = true;\n`;
|
1081 | }
|
1082 | }
|
1083 |
|
1084 | sj_init += `const KT_DEFAULT_GRAPH = factory.defaultGraph();\n`;
|
1085 | sj_init += a_graphs_nond.map(g => `let ${g.variable}_init = (kt_graph => (${g.condition}))(KT_DEFAULT_GRAPH);\n`);
|
1086 | sj_init += a_graphs_nond.map(g => `let ${g.variable} = ${g.variable}_init;\n`);
|
1087 |
|
1088 | sj_events = `
|
1089 | ds_reader.on('enter', (kt_graph) => {
|
1090 | ${a_graphs_nond.map(g => /* syntax: js */ `
|
1091 | if(${g.condition}) {
|
1092 | ${g.variable} = true;
|
1093 | ${b_default_graph? /* syntax: js */ `b_default_graph = false;`: ''}
|
1094 | }
|
1095 | `)}
|
1096 |
|
1097 | ${b_default_graph
|
1098 | ? /* syntax: js */ `
|
1099 | if(kt_graph.isDefaultGraph) {
|
1100 | b_default_graph = true;
|
1101 | }
|
1102 | `
|
1103 | : ''}
|
1104 | });
|
1105 |
|
1106 | ds_reader.on('exit', (kt_graph) => {
|
1107 | ${a_graphs_nond.map(g => /* syntax: js */ `
|
1108 | ${g.variable} = ${g.variable}_init;
|
1109 | `)}
|
1110 |
|
1111 | ${b_default_graph
|
1112 | ? /* syntax: js */ `
|
1113 | if(!kt_graph.isDefaultGraph) {
|
1114 | b_default_graph = false;
|
1115 | }
|
1116 | `
|
1117 | : ''}
|
1118 | });
|
1119 | `;
|
1120 | }
|
1121 |
|
1122 |
|
1123 | let sj_destructures = Array.from(
|
1124 | a_patterns.reduce((as_out, k) => new Set([...as_out, ...k.destructures]), k_context.destructures),
|
1125 | ).map(s => `let kt_${s} = g_quad.${s};`).join('\n');
|
1126 |
|
1127 | let sj_filter = gobble( `
|
1128 | ${k_context.declarations}
|
1129 | ${sj_init}
|
1130 |
|
1131 | let ds_filter = new stream.Transform.QuadsToOther({
|
1132 | objectMode: true,
|
1133 |
|
1134 | transform(g_quad, s_encoding, fke_transform) {
|
1135 | ${sj_destructures}
|
1136 | let b_cond = ${a_patterns.map(k => wrap(k.conditions, a_patterns.length > 1)).join('\n\t ||')};
|
1137 |
|
1138 | // condition passed
|
1139 | if(b_cond) {
|
1140 | this.push(g_quad);
|
1141 | }
|
1142 |
|
1143 | // done with quad
|
1144 | fke_transform();
|
1145 | },
|
1146 | });
|
1147 |
|
1148 | ds_filter.on('pipe', (ds_reader) => {
|
1149 | ${sj_events}
|
1150 | });
|
1151 |
|
1152 | return ds_filter;
|
1153 | `);
|
1154 |
|
1155 | return sj_filter;
|
1156 | }
|
1157 |
|
1158 | module.exports = {
|
1159 | prepare,
|
1160 | };
|