UNPKG

24.8 kBJavaScriptView Raw
1const {
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
13const X_RANK_PROPERTY_ACCESS = 1;
14const X_RANK_STRING_EQUALS = 3;
15const X_RANK_STRING_INDEX_OF = 3.5;
16const X_RANK_STRING_ENDS_WITH = 5;
17const X_RANK_CONCISE_STRING_EQUALS = 6;
18const X_RANK_CONCISE_PREFIXES_STRING_EQUALS = 6.5;
19const X_RANK_CONCISE_STRING_INDEX_OF = 7;
20const X_RANK_CONCISE_PREFIXES_STRING_INDEX_OF = 7.5;
21const X_RANK_REGEX = 11;
22const X_RANK_CONCISE_REGEX = 12;
23const X_RANK_TERM_COMPARE = 5.5;
24
25const F_SORT_GEN_RANK = (g_a, g_b) => g_a.rank - g_b.rank;
26
27const F_REDUCE_DECAY = (x_total, x_rank) => x_total + (x_rank / 2);
28
29const R_PLAIN_REGEX = /^(\^?)((?:\\.|[^.[\]{}?*+$()|])*)(\$?)$/;
30
31const RT_TEXT_PREFIX = /^[^~>_^@"*?]/;
32
33const H_TEST_RANGE = {
34 [XM_TERM_TAG_DEFAULT_GRAPH]: {
35 rank: X_RANK_PROPERTY_ACCESS,
36
37 gen: sj => /* syntax: js */ `${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 /* syntax: js */ `(${sj}.isNamedNode || ${sj}.isBlankNode)`;
45 },
46 },
47
48 [XM_TERM_TAG_LITERAL]: {
49 rank: X_RANK_PROPERTY_ACCESS,
50
51 gen(sj) {
52 return /* syntax: js */ `${sj}.isLiteral`;
53 },
54 },
55
56 [XM_TERM_TAG_NODE_NAMED]: {
57 rank: X_RANK_PROPERTY_ACCESS,
58
59 gen(sj) {
60 return /* syntax: js */ `${sj}.isNamedNode`;
61 },
62 },
63
64 [XM_TERM_TAG_NODE_BLANK]: {
65 rank: X_RANK_PROPERTY_ACCESS,
66
67 gen(sj) {
68 return /* syntax: js */ `${sj}.isBlankNode`;
69 },
70 },
71
72 [XM_TERM_TAG_LITERAL_SIMPLE]: {
73 rank: X_RANK_PROPERTY_ACCESS,
74
75 gen(sj) {
76 return /* syntax: js */ `${sj}.isSimple`;
77 },
78 },
79
80 [XM_TERM_TAG_LITERAL_LANGUAGED]: {
81 rank: X_RANK_PROPERTY_ACCESS,
82
83 gen(sj) {
84 return /* syntax: js */ `${sj}.isLanguaged`;
85 },
86 },
87
88 [XM_TERM_TAG_LITERAL_DATATYPED]: {
89 rank: X_RANK_PROPERTY_ACCESS,
90
91 gen(sj) {
92 return /* syntax: js */ `${sj}.isDatatyped`;
93 },
94 },
95};
96
97
98function compile_tags(xm_tags) {
99 // tags given
100 if(xm_tags && XM_TERM_TAG_ANY !== xm_tags) {
101 // direct tag mapping
102 if(xm_tags in H_TEST_RANGE) {
103 return H_TEST_RANGE[xm_tags];
104 }
105 // composite tag
106 else {
107 let a_selectors = [];
108
109 // each test range in order
110 for(let [sm_test, g_select] of Object.entries(H_TEST_RANGE)) {
111 let xm_test = +sm_test;
112
113 // mask covers case
114 if(xm_test === (xm_test & xm_tags)) {
115 // reduce case
116 xm_tags &= ~xm_test;
117
118 // apply selector
119 a_selectors.push(g_select);
120
121 // done
122 if(!xm_tags) break;
123 }
124 }
125
126 // failed to cover case
127 if(xm_tags) {
128 console.assert(`case not covered: ${xm_tags}`);
129 }
130
131 // save tag selector
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
144const pretag = (g, sj_target) => g.tags? compile_tags(g.tags).gen(sj_target)+' && ': '';
145
146const wrap = (s, b) => b? `(${s})`: s;
147
148const escape_str = s => s.replace(/\\(.)/g, '$1').replace(/'/g, '\\\'').replace(/\r\n\v\f/g, '');
149
150const track_prefix = (si_var_iri, si_prefix, s_suffix) => ({
151 declare: /* syntax: js */ `let ${si_var_iri} = null;`,
152
153 // subscribe to prefix change events
154 prefix: {
155 [escape_str(si_prefix)]: /* syntax: js */ `${si_var_iri} = p_iri+'${escape_str(s_suffix)}';`,
156 },
157});
158
159const H_COMPILERS = {
160 and(a_items) {
161 let a_compiled = a_items.map(g => this.compile(g));
162
163 // tags are not tied tight to conditions by default
164 let b_tight_tags = false;
165
166 // all entries are the same
167 if(a_compiled.slice(1).every(g => g === a_compiled[0].tags)) {
168 // simply add to conditions
169 a_compiled.push(compile_tags(a_compiled[0].tags));
170 }
171 // not all entries are the same
172 else {
173 // keep test tight to condition
174 b_tight_tags = true;
175 }
176
177 // sort compiled by rank
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 // compile term if present
244 let k_term = g_term? this.compile(g_range.term): null;
245
246 // tag selector
247 let x_rank = 0;
248 let fsj_tag = null;
249
250 // minimum assertion tags
251 if(k_term && k_term.tags) {
252 xm_tags |= k_term.tags;
253 }
254
255 // tags given
256 if(xm_tags && XM_TERM_TAG_ANY !== xm_tags) {
257 // direct tag mapping
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 // composite tag
265 else {
266 let a_selectors = [];
267
268 // each test range in order
269 for(let [sm_test, g_select] of Object.entries(H_TEST_RANGE)) {
270 let xm_test = +sm_test;
271
272 // mask covers case
273 if(xm_test === (xm_test & xm_tags)) {
274 // reduce case
275 xm_tags &= ~xm_test;
276
277 // apply selector
278 a_selectors.push(g_select);
279
280 // done
281 if(!xm_tags) break;
282 }
283 }
284
285 // failed to cover case
286 if(xm_tags) {
287 console.assert(`case not covered: ${xm_tags}`);
288 }
289
290 // assess rank
291 x_rank = a_selectors.map(g => g.rank).reduce(F_REDUCE_DECAY, 0);
292
293 // save tag selector
294 fsj_tag = sj_target => a_selectors.map(g => g.gen(sj_target)).join(' && ');
295 }
296 }
297
298 // tag
299 if(fsj_tag) {
300 // term
301 if(k_term) {
302 return {
303 rank: X_RANK_PROPERTY_ACCESS,
304
305 gen(sj_target) {
306 return /* syntax: js */ `${fsj_tag(sj_target)} && ${k_term.gen(sj_target)}`;
307 },
308 };
309 }
310 // tag only
311 else {
312 return {
313 rank: x_rank,
314 gen: fsj_tag,
315 };
316 }
317 }
318 // no tag
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 /* syntax: js */ `'${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 /* syntax: js */ `${si_var_iri} === ${sj_target}.value`;
359 },
360 };
361 },
362
363 language(s_lang) {
364 return {
365 // no need for checking `.isLanguaged` property since `.language` can
366 // be undefined
367 // tags: XM_TERM_TAG_LITERAL_LANGUAGED,
368
369 rank: X_RANK_STRING_EQUALS,
370
371 gen(sj_target) {
372 return /* syntax: js */ `'${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(/* syntax: js */ `${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 => /* syntax: js */ `'${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 /* syntax: js */ `${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 /* syntax: js */ `'${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 // concise/verbose term
473 if(g_regex.concise || g_regex.verbose) {
474 let m_plain = R_PLAIN_REGEX.exec(s_source);
475
476 // plain regex
477 if(m_plain && r_pattern.flags.indexOf('i') < 0) {
478 let [, s_anchor_start, s_text, s_anchor_end] = m_plain;
479
480 // anchor start
481 if(s_anchor_start) {
482 // concise term type
483 switch(s_text[0]) {
484 // language tag or datatype
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 // string pattern
494 let s_pattern = b_datatype? escape_str(s_text.slice(1)): escape_str(s_text);
495
496 // exact
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 /* syntax: js */ `'${s_pattern}' === ${sj_target}.concise(${sj_prefixes})`;
509 },
510 }
511 : {
512 rank: X_RANK_CONCISE_STRING_EQUALS,
513
514 gen(sj_target) {
515 return /* syntax: js */ `'${s_pattern}' === ${sj_target}.concise()`;
516 },
517 }),
518 };
519 }
520 // starts with
521 else {
522 // content delimiter
523 let i_contents = s_text.indexOf('"');
524 if(i_contents >= 0) {
525 let s_contents = s_text.slice(i_contents);
526
527 // yes contents
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 /* syntax: js */ `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 /* syntax: js */ `0 === ${sj_target}.concise().indexOf('${s_pattern}')`;
547 },
548 },
549 };
550 }
551 // no contents given; exact datatype
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 /* syntax: js */ `'${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 /* syntax: js */ `'${s_pattern}' === ${sj_target}.datatype.concise()`;
571 },
572 },
573 };
574 }
575 // exact language
576 else {
577 return {
578 tags: xm_tags,
579
580 rank: X_RANK_CONCISE_STRING_INDEX_OF,
581
582 gen(sj_target) {
583 return /* syntax: js */ `'${s_pattern}' === ${sj_target}.language`;
584 },
585 };
586 }
587 }
588 // no content delimiter; match start of datatype
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 /* syntax: js */ `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 /* syntax: js */ `0 === ${sj_target}.datatype.concise().indexOf('${s_pattern}')`;
608 },
609 },
610 };
611 }
612 // start of language
613 else {
614 return {
615 tags: xm_tags,
616
617 rank: X_RANK_STRING_INDEX_OF,
618
619 gen(sj_target) {
620 return /* syntax: js */ `0 === ${sj_target}.language.indexOf('${s_pattern}')`;
621 },
622 };
623 }
624 }
625 }
626
627 // simple literal
628 case '"': {
629 let s_contents = s_text.slice(1);
630
631 // exact contents
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 /* syntax: js */ `'${s_contents}' === ${sj_target}.value`;
640 },
641 };
642 }
643 // starts with
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 /* syntax: js */ `0 === ${sj_target}.value.indexOf('${s_contents}')`;
652 },
653 };
654 }
655 }
656
657 // absolute iri
658 case '>': {
659 let p_iri = s_text.slice(1);
660
661 // exact contents
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 /* syntax: js */ `'${p_iri}' === ${sj_target}.value`;
670 },
671 };
672 }
673 // starts with
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 /* syntax: js */ `0 === ${sj_target}.value.indexOf('${p_iri}')`;
682 },
683 };
684 }
685 }
686
687 // prefixed node
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 // exact
696 if(s_anchor_end) {
697 // f_test = (kt, g) => kt.value === g.prefixes[si_prefix]+s_suffix;
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 /* syntax: js */ `${si_var_iri} === ${sj_target}.value`;
709 },
710 };
711 }
712 // starts with
713 else {
714 // f_test = (kt, g) => kt.value.startsWith(g.prefixes[si_prefix]+s_suffix);
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 /* syntax: js */ `0 === ${sj_target}.value.indexOf(${si_var_iri})`;
726 },
727 };
728 }
729 }
730 }
731 }
732 // ends with
733 else if(s_anchor_end) {
734 // f_test = kt => kt.value.endsWith(s_text);
735 return {
736 rank: X_RANK_STRING_ENDS_WITH,
737
738 gen(sj_target) {
739 return /* syntax: js */ `${sj_target}.concise().endsWith('${escape_str(s_text)}')`;
740 },
741 };
742 }
743 // contains
744 else {
745 // f_test = kt => kt.value.indexOf(s_text) > -1;
746 return {
747 rank: X_RANK_STRING_INDEX_OF,
748
749 gen(sj_target) {
750 return /* syntax: js */ `${sj_target}.concise().indexOf('${escape_str(s_text)}') > -1`;
751 },
752 };
753 }
754 }
755 // not plain regex
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: /* syntax: js */ `let ${si_var_regex} = /${s_source}/${r_pattern.flags};`,
767 };
768 },
769
770 gen(sj_target) {
771 return /* syntax: js */ `${si_var_regex}.test(${sj_target}.concise(${g_regex.verbose? '': 'h_prefixes'}))`;
772 },
773 };
774 }
775 }
776 // not concise term
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: /* syntax: js */ `let ${si_var_regex} = /${s_source}/${r_pattern.flags};`,
786 };
787 },
788
789 gen(sj_target) {
790 return /* syntax: js */ `${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 /* syntax: js */ `${sj_target}.equals(kt_${s_ref})`;
804 },
805 };
806 },
807};
808
809
810class 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 // special handling for hooks
875 if(g_compiled.hooks) {
876 this.push_hook(g_compiled.hooks());
877 }
878
879 // prefixes
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
902class 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(/* syntax: js */ `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(/* syntax: js */ `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(/* syntax: js */ `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(/* syntax: js */ `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(/* syntax: js */ `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
994const 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
1005function 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 // each pattern
1018 for(let k_pattern of a_patterns) {
1019 // graphs
1020 if(k_pattern.graph) {
1021 a_graphs.push(k_pattern.graph);
1022 }
1023
1024 // declarations
1025 if(k_pattern.declarations) {
1026 sj_init += k_pattern.declarations;
1027 }
1028 }
1029
1030 // prefix events
1031 {
1032 let s_branches = '';
1033
1034 // prefix cases
1035 let h_prefix_cases = k_context.prefix_handlers;
1036 for(let si_prefix in h_prefix_cases) {
1037 s_branches += /* syntax: js.switch */ `
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 += /* syntax: js */ `let h_prefixes = {};\n`;
1047
1048 sj_events += /* syntax: js */ `
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 // graph events
1061 if(a_graphs.length) {
1062 // filter non-defaults
1063 let a_graphs_nond = [];
1064
1065 // default graph present
1066 let b_default_graph = false;
1067 {
1068 // each graph
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 // default graph
1079 if(b_default_graph) {
1080 sj_init += /* syntax: js */ `let b_default_graph = true;\n`;
1081 }
1082 }
1083
1084 sj_init += /* syntax: js */ `const KT_DEFAULT_GRAPH = factory.defaultGraph();\n`;
1085 sj_init += a_graphs_nond.map(g => /* syntax: js */ `let ${g.variable}_init = (kt_graph => (${g.condition}))(KT_DEFAULT_GRAPH);\n`);
1086 sj_init += a_graphs_nond.map(g => /* syntax: js */ `let ${g.variable} = ${g.variable}_init;\n`);
1087
1088 sj_events = /* syntax: js */ `
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 // destructures
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 => /* syntax: js */ `let kt_${s} = g_quad.${s};`).join('\n');
1126 // final filter
1127 let sj_filter = gobble(/* syntax: js */ `
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
1158module.exports = {
1159 prepare,
1160};