UNPKG

23.5 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, '\\\'').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 fsj_tag = null;
248
249 // minimum assertion tags
250 if(k_term && k_term.tags) {
251 xm_tags |= k_term.tags;
252 }
253
254 // tags given
255 if(xm_tags && XM_TERM_TAG_ANY !== xm_tags) {
256 // direct tag mapping
257 if(xm_tags in H_TEST_RANGE) {
258 fsj_tag = H_TEST_RANGE[xm_tags];
259 }
260 // composite tag
261 else {
262 let a_selectors = [];
263
264 // each test range in order
265 for(let [sm_test, fsj_select] of Object.entries(H_TEST_RANGE)) {
266 let xm_test = +sm_test;
267
268 // mask covers case
269 if(xm_test === (xm_test & xm_tags)) {
270 // reduce case
271 xm_tags &= ~xm_test;
272
273 // apply selector
274 a_selectors.push(fsj_select);
275
276 // done
277 if(!xm_tags) break;
278 }
279 }
280
281 // failed to cover case
282 if(xm_tags) {
283 console.assert(`case not covered: ${xm_tags}`);
284 }
285
286 // save tag selector
287 fsj_tag = sj_target => a_selectors.map(fsj => fsj(sj_target)).join(' && ');
288 }
289 }
290
291 // tag and term
292 if(fsj_tag && k_term) {
293 return {
294 rank: X_RANK_PROPERTY_ACCESS,
295
296 gen(sj_target) {
297 return /* syntax: js */ `${fsj_tag(sj_target)} && ${k_term.gen(sj_target)}`;
298 },
299 };
300 }
301 else {
302 debugger;
303 }
304 },
305
306 iri(p_iri) {
307 return {
308 tags: XM_TERM_TAG_NODE_NAMED,
309
310 rank: X_RANK_STRING_EQUALS,
311
312 gen(sj_target) {
313 return /* syntax: js */ `'${escape_str(p_iri)}' === ${sj_target}.value`;
314 },
315 };
316 },
317
318 pname(g_pname) {
319 let {
320 prefix: si_prefix,
321 local: s_suffix,
322 } = g_pname;
323
324 let si_var_iri = this.acquire('p_iri');
325
326 return {
327 tags: XM_TERM_TAG_NODE_NAMED,
328
329 rank: X_RANK_STRING_EQUALS,
330
331 hooks() {
332 return track_prefix(si_var_iri, si_prefix, s_suffix);
333 },
334
335 gen(sj_target) {
336 return /* syntax: js */ `${si_var_iri} === ${sj_target}.value`;
337 },
338 };
339 },
340
341 language(s_lang) {
342 return {
343 // no need for checking `.isLanguaged` property since `.language` can
344 // be undefined
345 // tags: XM_TERM_TAG_LITERAL_LANGUAGED,
346
347 rank: X_RANK_STRING_EQUALS,
348
349 gen(sj_target) {
350 return /* syntax: js */ `'${escape_str(s_lang).toLowerCase()}' === ${sj_target}.language`;
351 },
352 };
353 },
354
355 datatype(g_datatype) {
356 let k_datatype = this.compile(g_datatype);
357
358 return {
359 tags: XM_TERM_TAG_LITERAL_DATATYPED,
360
361 rank: X_RANK_PROPERTY_ACCESS,
362
363 gen(sj_target) {
364 return k_datatype.toString(/* syntax: js */ `${sj_target}.datatype`);
365 },
366 };
367 },
368
369 literal(g_literal) {
370 let {
371 contents: s_contents,
372 post: g_post,
373 } = g_literal;
374
375 let f_contents = sj_target => /* syntax: js */ `'${escape_str(s_contents)}' === ${sj_target}.value`;
376
377 if(g_post) {
378 let k_post = this.compile(g_post);
379
380 return {
381 tags: k_post.tags,
382
383 rank: [X_RANK_STRING_EQUALS, k_post.rank].reduce(F_REDUCE_DECAY, 0),
384
385 gen(sj_target) {
386 return /* syntax: js */ `${f_contents(sj_target)} && ${k_post.gen(sj_target)}`;
387 },
388 };
389 }
390 else {
391 return {
392 tags: XM_TERM_TAG_LITERAL_SIMPLE,
393
394 rank: X_RANK_STRING_EQUALS,
395
396 gen(sj_contents) {
397 return f_contents(sj_contents);
398 },
399 };
400 }
401 },
402
403 default_graph() {
404 return {
405 tags: XM_TERM_TAG_DEFAULT_GRAPH,
406
407 rank: 0,
408
409 gen: () => 'true',
410 };
411 },
412
413 anon() {
414 return {
415 tags: XM_TERM_TAG_NODE_BLANK,
416
417 rank: 0,
418
419 gen: () => 'true',
420 };
421 },
422
423 bnode(s_label) {
424 if(s_label) {
425 return {
426 tags: XM_TERM_TAG_NODE_BLANK,
427
428 rank: X_RANK_STRING_EQUALS,
429
430 gen(sj_target) {
431 return /* syntax: js */ `'${escape_str(s_label)}' === ${sj_target}.value`;
432 },
433 };
434 }
435 else {
436 return {
437 tags: XM_TERM_TAG_NODE_BLANK,
438
439 rank: 0,
440
441 gen: () => 'true',
442 };
443 }
444 },
445
446 regex(g_regex) {
447 let r_pattern = g_regex.pattern;
448 let s_source = r_pattern.source;
449
450 // consie term
451 if(g_regex.concise) {
452 let m_plain = R_PLAIN_REGEX.exec(s_source);
453
454 // plain regex
455 if(m_plain && r_pattern.flags.indexOf('i') < 0) {
456 let [, s_anchor_start, s_text, s_anchor_end] = m_plain;
457
458 // anchor start
459 if(s_anchor_start) {
460 // concise term type
461 switch(s_text[0]) {
462 // language tag
463 case '@':
464 case '^': {
465 let b_datatype = '^' === s_text[0];
466 let b_prefixed_datatype = b_datatype && '>' !== s_text[1];
467 let sj_prefixes = b_prefixed_datatype? 'h_prefixes': '';
468
469 let xm_tags = b_datatype? XM_TERM_TAG_LITERAL_DATATYPED: XM_TERM_TAG_LITERAL_LANGUAGED;
470
471 // exact
472 if(s_anchor_end) {
473 return {
474 tags: xm_tags,
475
476 prefixes: !!sj_prefixes,
477
478 ...(b_prefixed_datatype
479 ? {
480 rank: X_RANK_CONCISE_PREFIXES_STRING_EQUALS,
481 }
482 : {
483 rank: X_RANK_CONCISE_STRING_EQUALS,
484 }),
485
486 gen(sj_target) {
487 return /* syntax: js */ `'${escape_str(s_text)}' === ${sj_target}.concise(${sj_prefixes})`;
488 },
489 };
490 }
491 // starts with
492 else {
493 // content delimiter
494 let i_contents = s_text.indexOf('"');
495 if(i_contents >= 0) {
496 let s_contents = s_text.slice(i_contents);
497
498 // yes contents
499 if(s_contents) {
500 return {
501 tags: xm_tags,
502
503 prefixes: !!sj_prefixes,
504
505 rank: b_prefixed_datatype? X_RANK_CONCISE_PREFIXES_STRING_INDEX_OF: X_RANK_CONCISE_STRING_INDEX_OF,
506
507 gen(sj_target) {
508 return /* syntax: js */ `0 === ${sj_target}.concise(${sj_prefixes}).indexOf('${escape_str(s_text)}')`;
509 },
510 };
511 }
512 // no contents given; exact datatype
513 else if(b_datatype) {
514 return {
515 tags: xm_tags,
516
517 prefixes: !!sj_prefixes,
518
519 rank: b_prefixed_datatype? X_RANK_CONCISE_PREFIXES_STRING_EQUALS: X_RANK_CONCISE_STRING_EQUALS,
520
521 gen(sj_target) {
522 return /* syntax: js */ `'${escape_str(s_text.slice(1))}' === ${sj_target}.datatype.concise(${sj_prefixes})`;
523 },
524 };
525 }
526 // exact language
527 else {
528 return {
529 tags: xm_tags,
530
531 rank: X_RANK_CONCISE_STRING_INDEX_OF,
532
533 gen(sj_target) {
534 return /* syntax: js */ `'${escape_str(s_text.slice(1))}' === ${sj_target}.language`;
535 },
536 };
537 }
538 }
539 // no content delimiter; match start of datatype
540 else if(b_datatype) {
541 return {
542 tags: xm_tags,
543
544 prefixes: !!sj_prefixes,
545
546 rank: b_prefixed_datatype? X_RANK_CONCISE_PREFIXES_STRING_INDEX_OF: X_RANK_CONCISE_STRING_INDEX_OF,
547
548 gen(sj_target) {
549 return /* syntax: js */ `0 === ${sj_target}.datatype.concise(${sj_prefixes}).indexOf('${escape_str(s_text)}')`;
550 },
551 };
552 }
553 // start of language
554 else {
555 return {
556 tags: xm_tags,
557
558 rank: X_RANK_STRING_INDEX_OF,
559
560 gen(sj_target) {
561 return /* syntax: js */ `0 === ${sj_target}.language.indexOf('${escape_str(s_text.slice(1))}')`;
562 },
563 };
564 }
565 }
566 }
567
568 // simple literal
569 case '"': {
570 let s_contents = s_text.slice(1);
571
572 // exact contents
573 if(s_anchor_end) {
574 return {
575 tags: XM_TERM_TAG_LITERAL_SIMPLE,
576
577 rank: X_RANK_STRING_EQUALS,
578
579 gen(sj_target) {
580 return /* syntax: js */ `'${s_contents}' === ${sj_target}.value`;
581 },
582 };
583 }
584 // starts with
585 else {
586 return {
587 tags: XM_TERM_TAG_LITERAL_SIMPLE,
588
589 rank: X_RANK_STRING_INDEX_OF,
590
591 gen(sj_target) {
592 return /* syntax: js */ `0 === ${sj_target}.value.indexOf('${s_contents}')`;
593 },
594 };
595 }
596 }
597
598 // absolute iri
599 case '>': {
600 let p_iri = s_text.slice(1);
601
602 // exact contents
603 if(s_anchor_end) {
604 return {
605 tags: XM_TERM_TAG_LITERAL_SIMPLE,
606
607 rank: X_RANK_STRING_EQUALS,
608
609 gen(sj_target) {
610 return /* syntax: js */ `'${p_iri}' === ${sj_target}.value`;
611 },
612 };
613 }
614 // starts with
615 else {
616 return {
617 tags: XM_TERM_TAG_LITERAL_SIMPLE,
618
619 rank: X_RANK_STRING_INDEX_OF,
620
621 gen(sj_target) {
622 return /* syntax: js */ `0 === ${sj_target}.value.indexOf('${p_iri}')`;
623 },
624 };
625 }
626 }
627
628 // prefixed node
629 default: {
630 let i_colon = s_text.indexOf(':');
631 let si_prefix = s_text.slice(0, i_colon);
632 let s_suffix = s_text.slice(i_colon+1);
633
634 let si_var_iri = this.acquire('p_iri');
635
636 // exact
637 if(s_anchor_end) {
638 // f_test = (kt, g) => kt.value === g.prefixes[si_prefix]+s_suffix;
639 return {
640 tags: XM_TERM_TAG_NODE_NAMED,
641
642 rank: X_RANK_STRING_EQUALS,
643
644 hooks() {
645 return track_prefix(si_var_iri, si_prefix, s_suffix);
646 },
647
648 gen(sj_target) {
649 return /* syntax: js */ `${si_var_iri} === ${sj_target}.value`;
650 },
651 };
652 }
653 // starts with
654 else {
655 // f_test = (kt, g) => kt.value.startsWith(g.prefixes[si_prefix]+s_suffix);
656 return {
657 tags: XM_TERM_TAG_NODE_NAMED,
658
659 rank: X_RANK_STRING_INDEX_OF,
660
661 hooks() {
662 return track_prefix(si_var_iri, si_prefix, s_suffix);
663 },
664
665 gen(sj_target) {
666 return /* syntax: js */ `0 === ${sj_target}.value.indexOf(${si_var_iri})`;
667 },
668 };
669 }
670 }
671 }
672 }
673 // ends with
674 else if(s_anchor_end) {
675 // f_test = kt => kt.value.endsWith(s_text);
676 return {
677 rank: X_RANK_STRING_ENDS_WITH,
678
679 gen(sj_target) {
680 return /* syntax: js */ `${sj_target}.concise().endsWith('${escape_str(s_text)}')`;
681 },
682 };
683 }
684 // contains
685 else {
686 // f_test = kt => kt.value.indexOf(s_text) > -1;
687 return {
688 rank: X_RANK_STRING_INDEX_OF,
689
690 gen(sj_target) {
691 return /* syntax: js */ `${sj_target}.concise().indexOf('${escape_str(s_text)}') > -1`;
692 },
693 };
694 }
695 }
696 // not plain regex
697 else {
698 let si_var_regex = this.acquire('rt_term');
699
700 return {
701 rank: X_RANK_CONCISE_REGEX,
702
703 prefixes: true,
704
705 hooks() {
706 return {
707 declare: /* syntax: js */ `let ${si_var_regex} = /${s_source}/${r_pattern.flags};`,
708 };
709 },
710
711 gen(sj_target) {
712 return /* syntax: js */ `${si_var_regex}.test(${sj_target}.concise(h_prefixes))`;
713 },
714 };
715 }
716 }
717 // not concise term
718 else {
719 let si_var_regex = this.acquire('rt_term');
720
721 return {
722 rank: X_RANK_REGEX,
723
724 hooks() {
725 return {
726 declare: /* syntax: js */ `let ${si_var_regex} = /${s_source}/${r_pattern.flags};`,
727 };
728 },
729
730 gen(sj_target) {
731 return /* syntax: js */ `${si_var_regex}.test(${sj_target}.value)`;
732 },
733 };
734 }
735 },
736
737 ref(s_ref) {
738 this._as_destructures.add(s_ref);
739
740 return {
741 rank: X_RANK_TERM_COMPARE,
742
743 gen(sj_target) {
744 return /* syntax: js */ `${sj_target}.equals(kt_${s_ref})`;
745 },
746 };
747 },
748};
749
750
751class Context {
752 constructor() {
753 this._c_vars =0;
754 this._s_declare = '';
755 this._h_prefix_handlers = {};
756 this._b_capture_prefixes = false;
757 this._as_destructures = new Set();
758 this._a_refs = [];
759 this._c_patterns = 0;
760 }
761
762 start_pattern() {
763 this._a_refs.push({});
764 this._c_patterns += 1;
765 }
766
767 acquire(s_var) {
768 return `${s_var}_${this._c_vars++}`;
769 }
770
771 push_hook(g_hook) {
772 if(g_hook.declare) {
773 this._s_declare += g_hook.declare;
774 }
775
776 if(g_hook.prefix) {
777 let h_handlers = this._h_prefix_handlers;
778
779 let h_prefix = g_hook.prefix;
780 for(let si_prefix in h_prefix) {
781 (h_handlers[si_prefix] = h_handlers[si_prefix] || [])
782 .push(h_prefix[si_prefix]);
783 }
784 }
785 }
786
787 get destructures() {
788 return this._as_destructures;
789 }
790
791 get declarations() {
792 return this._s_declare;
793 }
794
795 get prefix_handlers() {
796 return this._h_prefix_handlers;
797 }
798
799 get capture_prefixes() {
800 return this._b_capture_prefixes;
801 }
802
803 compile(g_src) {
804 let {
805 type: s_type,
806 value: w_value,
807 } = g_src;
808
809 if(!(s_type in H_COMPILERS)) {
810 console.assert(`syntax object type ${s_type} not mapped`);
811 }
812
813 let g_compiled = H_COMPILERS[s_type].call(this, w_value);
814
815 // special handling for hooks
816 if(g_compiled.hooks) {
817 this.push_hook(g_compiled.hooks());
818 }
819
820 // prefixes
821 if(g_compiled.prefixes) {
822 this._b_capture_prefixes = true;
823 }
824
825 g_compiled.auto = (sj_target) => {
826 let sj_compiled = g_compiled.gen(sj_target);
827
828 if(g_compiled.tags) {
829 let sj_pretag = pretag(g_compiled, sj_target);
830
831 return wrap(sj_pretag+sj_compiled, sj_pretag);
832 }
833 else {
834 return sj_compiled;
835 }
836 };
837
838 return g_compiled;
839 }
840}
841
842
843class Pattern {
844 constructor(k_context, g_source) {
845 let x_rank = 0;
846 let c_decay = 0;
847
848 let a_conditions = this._a_conditions = [];
849 let a_destructures = this._a_destructures = [];
850 this._g_graph = null;
851
852 k_context._c_patterns += 1;
853
854 if(g_source.graph) {
855 let g_compiled = k_context.compile(g_source.graph);
856
857 x_rank += g_compiled.rank / (Math.pow(2, c_decay++));
858
859 let b_default_graph = XM_TERM_TAG_DEFAULT_GRAPH === g_compiled.tags;
860
861 if(b_default_graph) {
862 this._g_graph = {
863 compiled: g_compiled,
864 condition: g_compiled.auto(/* syntax: js */ `kt_graph`),
865 variable: 'b_default_graph',
866 default: true,
867 };
868
869 a_conditions.push('b_default_graph');
870 }
871 else {
872 let si_var_graph = k_context.acquire('b_graph_pass');
873
874 this._g_graph = {
875 compiled: g_compiled,
876 condition: g_compiled.auto(/* syntax: js */ `kt_graph`),
877 variable: b_default_graph? 'b_default_graph': si_var_graph,
878 default: b_default_graph,
879 };
880
881 a_conditions.push(si_var_graph);
882 }
883 }
884
885 if(g_source.object) {
886 let g_compiled = k_context.compile(g_source.object);
887
888 x_rank += g_compiled.rank / (Math.pow(2, c_decay++));
889
890 a_conditions.push(g_compiled.auto(/* syntax: js */ `kt_object`));
891
892 a_destructures.push('object');
893 }
894
895 if(g_source.subject) {
896 let g_compiled = k_context.compile(g_source.subject);
897
898 x_rank += g_compiled.rank / (Math.pow(2, c_decay++));
899
900 a_conditions.push(g_compiled.auto(/* syntax: js */ `kt_subject`));
901
902 a_destructures.push('subject');
903 }
904
905 if(g_source.predicate) {
906 let g_compiled = k_context.compile(g_source.predicate);
907
908 x_rank += g_compiled.rank / (Math.pow(2, c_decay++));
909
910 a_conditions.push(g_compiled.auto(/* syntax: js */ `kt_predicate`));
911
912 a_destructures.push('predicate');
913 }
914
915 this._x_rank = x_rank;
916 }
917
918 get destructures() {
919 return this._a_destructures;
920 }
921
922 get graph() {
923 return this._g_graph;
924 }
925
926 get rank() {
927 return this._x_rank;
928 }
929
930 get conditions() {
931 return this._a_conditions.join('\n\t && ');
932 }
933}
934
935const gobble = (s_text) => {
936 let m_indent = /^(?:\s*\n)+([ \t]*)/.exec(s_text);
937 if(m_indent) {
938 let r_indent = new RegExp('\\n'+m_indent[1], 'g');
939 return s_text.trim().replace(r_indent, '\n');
940 }
941 else {
942 return s_text;
943 }
944};
945
946function prepare(a_sources) {
947 let k_context = new Context();
948
949 let a_patterns = a_sources
950 .map(g_source => new Pattern(k_context, g_source))
951 .sort(F_SORT_GEN_RANK);
952
953 let sj_init = '';
954 let sj_events = '';
955
956 let a_graphs = [];
957
958 // each pattern
959 for(let k_pattern of a_patterns) {
960 // graphs
961 if(k_pattern.graph) {
962 a_graphs.push(k_pattern.graph);
963 }
964
965 // declarations
966 if(k_pattern.declarations) {
967 sj_init += k_pattern.declarations;
968 }
969 }
970
971 // prefix events
972 {
973 let s_branches = '';
974
975 // prefix cases
976 let h_prefix_cases = k_context.prefix_handlers;
977 for(let si_prefix in h_prefix_cases) {
978 s_branches += /* syntax: js.switch */ `
979 case '${si_prefix}': {
980 ${h_prefix_cases[si_prefix].join('\n')}
981 break;
982 }
983 `;
984 }
985
986 if(s_branches || k_context.capture_prefixes) {
987 sj_init += /* syntax: js */ `let h_prefixes = {};\n`;
988
989 sj_events += /* syntax: js */ `
990 ds_reader.on('prefix', (si_prefix, p_iri) => {
991 ${k_context.capture_prefixes? /* syntax: js */ `h_prefixes[si_prefix] = p_iri;`: ''}
992
993 switch(si_prefix) {
994 ${s_branches}
995 }
996 });
997 `;
998 }
999 }
1000
1001 // graph events
1002 if(a_graphs.length) {
1003 // filter non-defaults
1004 let a_graphs_nond = [];
1005
1006 // default graph present
1007 let b_default_graph = false;
1008 {
1009 // each graph
1010 for(let g_graph of a_graphs) {
1011 if(g_graph.default) {
1012 b_default_graph = true;
1013 }
1014 else {
1015 a_graphs_nond.push(g_graph);
1016 }
1017 }
1018
1019 // default graph
1020 if(b_default_graph) {
1021 sj_init += /* syntax: js */ `let b_default_graph = true;\n`;
1022 }
1023 }
1024
1025 sj_init += /* syntax: js */ `const KT_DEFAULT_GRAPH = factory.defaultGraph();\n`;
1026 sj_init += a_graphs_nond.map(g => /* syntax: js */ `let ${g.variable}_init = (kt_graph => (${g.condition}))(KT_DEFAULT_GRAPH);\n`);
1027 sj_init += a_graphs_nond.map(g => /* syntax: js */ `let ${g.variable} = ${g.variable}_init;\n`);
1028
1029 sj_events = /* syntax: js */ `
1030 ds_reader.on('enter', (kt_graph) => {
1031 ${a_graphs_nond.map(g => /* syntax: js */ `
1032 if(${g.condition}) {
1033 ${g.variable} = true;
1034 ${b_default_graph? /* syntax: js */ `b_default_graph = false;`: ''}
1035 }
1036 `)}
1037
1038 ${b_default_graph
1039 ? /* syntax: js */ `
1040 if(kt_graph.isDefaultGraph) {
1041 b_default_graph = true;
1042 }
1043 `
1044 : ''}
1045 });
1046
1047 ds_reader.on('exit', (kt_graph) => {
1048 ${a_graphs_nond.map(g => /* syntax: js */ `
1049 ${g.variable} = ${g.variable}_init;
1050 `)}
1051
1052 ${b_default_graph
1053 ? /* syntax: js */ `
1054 if(!kt_graph.isDefaultGraph) {
1055 b_default_graph = false;
1056 }
1057 `
1058 : ''}
1059 });
1060 `;
1061 }
1062
1063 // destructures
1064 let sj_destructures = Array.from(
1065 a_patterns.reduce((as_out, k) => new Set([...as_out, ...k.destructures]), k_context.destructures),
1066 ).map(s => /* syntax: js */ `let kt_${s} = g_quad.${s};`).join('\n');
1067 // final filter
1068 let sj_filter = gobble(/* syntax: js */ `
1069 ${k_context.declarations}
1070 ${sj_init}
1071
1072 let ds_filter = new stream.Transform.QuadsToOther({
1073 objectMode: true,
1074
1075 transform(g_quad, s_encoding, fke_transform) {
1076 ${sj_destructures}
1077 let b_cond = ${a_patterns.map(k => wrap(k.conditions, a_patterns.length > 1)).join('\n\t ||')};
1078
1079 // condition passed
1080 if(b_cond) {
1081 this.push(g_quad);
1082 }
1083
1084 // done with quad
1085 fke_transform();
1086 },
1087 });
1088
1089 ds_filter.on('pipe', (ds_reader) => {
1090 ${sj_events}
1091 });
1092
1093 return ds_filter;
1094 `);
1095
1096 return sj_filter;
1097}
1098
1099module.exports = {
1100 prepare,
1101};