eyeling 1.5.39 → 1.5.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/eyeling.js +525 -839
  2. package/package.json +1 -1
package/eyeling.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
2
+ 'use strict';
3
3
 
4
4
  /*
5
5
  * eyeling.js — a minimal Notation3 (N3) reasoner in JavaScript
@@ -16,29 +16,29 @@
16
16
  * 5) Print only newly derived forward facts with explanations.
17
17
  */
18
18
 
19
- const { version } = require("./package.json");
20
- const nodeCrypto = require("crypto");
19
+ const { version } = require('./package.json');
20
+ const nodeCrypto = require('crypto');
21
21
 
22
22
  // ============================================================================
23
23
  // Namespace constants
24
24
  // ============================================================================
25
25
 
26
- const RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
27
- const RDFS_NS = "http://www.w3.org/2000/01/rdf-schema#";
28
- const OWL_NS = "http://www.w3.org/2002/07/owl#";
29
- const XSD_NS = "http://www.w3.org/2001/XMLSchema#";
30
- const CRYPTO_NS = "http://www.w3.org/2000/10/swap/crypto#";
31
- const MATH_NS = "http://www.w3.org/2000/10/swap/math#";
32
- const TIME_NS = "http://www.w3.org/2000/10/swap/time#";
33
- const LIST_NS = "http://www.w3.org/2000/10/swap/list#";
34
- const LOG_NS = "http://www.w3.org/2000/10/swap/log#";
35
- const STRING_NS = "http://www.w3.org/2000/10/swap/string#";
36
- const SKOLEM_NS = "https://eyereasoner.github.io/.well-known/genid/";
37
- const RDF_JSON_DT = RDF_NS + "JSON";
26
+ const RDF_NS = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
27
+ const RDFS_NS = 'http://www.w3.org/2000/01/rdf-schema#';
28
+ const OWL_NS = 'http://www.w3.org/2002/07/owl#';
29
+ const XSD_NS = 'http://www.w3.org/2001/XMLSchema#';
30
+ const CRYPTO_NS = 'http://www.w3.org/2000/10/swap/crypto#';
31
+ const MATH_NS = 'http://www.w3.org/2000/10/swap/math#';
32
+ const TIME_NS = 'http://www.w3.org/2000/10/swap/time#';
33
+ const LIST_NS = 'http://www.w3.org/2000/10/swap/list#';
34
+ const LOG_NS = 'http://www.w3.org/2000/10/swap/log#';
35
+ const STRING_NS = 'http://www.w3.org/2000/10/swap/string#';
36
+ const SKOLEM_NS = 'https://eyereasoner.github.io/.well-known/genid/';
37
+ const RDF_JSON_DT = RDF_NS + 'JSON';
38
38
 
39
39
  function isRdfJsonDatatype(dt) {
40
40
  // dt comes from literalParts() and may be expanded or prefixed depending on parsing/printing.
41
- return dt === null || dt === RDF_JSON_DT || dt === "rdf:JSON";
41
+ return dt === null || dt === RDF_JSON_DT || dt === 'rdf:JSON';
42
42
  }
43
43
 
44
44
  function termToJsonText(t) {
@@ -52,9 +52,9 @@ function termToJsonText(t) {
52
52
  function makeRdfJsonLiteral(jsonText) {
53
53
  // Prefer a readable long literal when safe; fall back to short if needed.
54
54
  if (!jsonText.includes('"""')) {
55
- return new Literal('"""' + jsonText + '"""^^<' + RDF_JSON_DT + ">");
55
+ return new Literal('"""' + jsonText + '"""^^<' + RDF_JSON_DT + '>');
56
56
  }
57
- return new Literal(JSON.stringify(jsonText) + "^^<" + RDF_JSON_DT + ">");
57
+ return new Literal(JSON.stringify(jsonText) + '^^<' + RDF_JSON_DT + '>');
58
58
  }
59
59
 
60
60
  // For a single reasoning run, this maps a canonical representation
@@ -81,7 +81,7 @@ function normalizeDateTimeLex(s) {
81
81
  // Accept: 2025-... , "2025-..." , "2025-..."^^xsd:dateTime , "..."^^<...>
82
82
  if (s == null) return null;
83
83
  let t = String(s).trim();
84
- const caret = t.indexOf("^^");
84
+ const caret = t.indexOf('^^');
85
85
  if (caret >= 0) t = t.slice(0, caret).trim();
86
86
  if (t.startsWith('"') && t.endsWith('"') && t.length >= 2) t = t.slice(1, -1);
87
87
  return t.trim();
@@ -91,7 +91,7 @@ function utcIsoDateTimeStringFromEpochSeconds(sec) {
91
91
  const ms = sec * 1000;
92
92
  const d = new Date(ms);
93
93
  function pad(n, w = 2) {
94
- return String(n).padStart(w, "0");
94
+ return String(n).padStart(w, '0');
95
95
  }
96
96
  const year = d.getUTCFullYear();
97
97
  const month = d.getUTCMonth() + 1;
@@ -100,22 +100,8 @@ function utcIsoDateTimeStringFromEpochSeconds(sec) {
100
100
  const min = d.getUTCMinutes();
101
101
  const s2 = d.getUTCSeconds();
102
102
  const ms2 = d.getUTCMilliseconds();
103
- const msPart = ms2 ? "." + String(ms2).padStart(3, "0") : "";
104
- return (
105
- pad(year, 4) +
106
- "-" +
107
- pad(month) +
108
- "-" +
109
- pad(day) +
110
- "T" +
111
- pad(hour) +
112
- ":" +
113
- pad(min) +
114
- ":" +
115
- pad(s2) +
116
- msPart +
117
- "+00:00"
118
- );
103
+ const msPart = ms2 ? '.' + String(ms2).padStart(3, '0') : '';
104
+ return pad(year, 4) + '-' + pad(month) + '-' + pad(day) + 'T' + pad(hour) + ':' + pad(min) + ':' + pad(s2) + msPart + '+00:00';
119
105
  }
120
106
 
121
107
  function getNowLex() {
@@ -150,22 +136,10 @@ function deterministicSkolemIdFromKey(key) {
150
136
  h4 = (h4 * 0x01000193) >>> 0;
151
137
  }
152
138
 
153
- const hex = [h1, h2, h3, h4]
154
- .map((h) => h.toString(16).padStart(8, "0"))
155
- .join(""); // 32 hex chars
139
+ const hex = [h1, h2, h3, h4].map((h) => h.toString(16).padStart(8, '0')).join(''); // 32 hex chars
156
140
 
157
141
  // Format like a UUID: 8-4-4-4-12
158
- return (
159
- hex.slice(0, 8) +
160
- "-" +
161
- hex.slice(8, 12) +
162
- "-" +
163
- hex.slice(12, 16) +
164
- "-" +
165
- hex.slice(16, 20) +
166
- "-" +
167
- hex.slice(20)
168
- );
142
+ return hex.slice(0, 8) + '-' + hex.slice(8, 12) + '-' + hex.slice(12, 16) + '-' + hex.slice(16, 20) + '-' + hex.slice(20);
169
143
  }
170
144
 
171
145
  let runLocalTimeCache = null;
@@ -299,82 +273,82 @@ function lex(inputText) {
299
273
  }
300
274
 
301
275
  // 2) Comments starting with '#'
302
- if (c === "#") {
303
- while (i < n && chars[i] !== "\n" && chars[i] !== "\r") i++;
276
+ if (c === '#') {
277
+ while (i < n && chars[i] !== '\n' && chars[i] !== '\r') i++;
304
278
  continue;
305
279
  }
306
280
 
307
281
  // 3) Two-character operators: => and <=
308
- if (c === "=") {
309
- if (peek(1) === ">") {
310
- tokens.push(new Token("OpImplies"));
282
+ if (c === '=') {
283
+ if (peek(1) === '>') {
284
+ tokens.push(new Token('OpImplies'));
311
285
  i += 2;
312
286
  continue;
313
287
  } else {
314
288
  // N3 syntactic sugar: '=' means owl:sameAs
315
- tokens.push(new Token("Equals"));
289
+ tokens.push(new Token('Equals'));
316
290
  i += 1;
317
291
  continue;
318
292
  }
319
293
  }
320
294
 
321
- if (c === "<") {
322
- if (peek(1) === "=") {
323
- tokens.push(new Token("OpImpliedBy"));
295
+ if (c === '<') {
296
+ if (peek(1) === '=') {
297
+ tokens.push(new Token('OpImpliedBy'));
324
298
  i += 2;
325
299
  continue;
326
300
  }
327
301
  // N3 predicate inversion: "<-" (swap subject/object for this predicate)
328
- if (peek(1) === "-") {
329
- tokens.push(new Token("OpPredInvert"));
302
+ if (peek(1) === '-') {
303
+ tokens.push(new Token('OpPredInvert'));
330
304
  i += 2;
331
305
  continue;
332
306
  }
333
307
  // Otherwise IRIREF <...>
334
308
  i++; // skip '<'
335
309
  const iriChars = [];
336
- while (i < n && chars[i] !== ">") {
310
+ while (i < n && chars[i] !== '>') {
337
311
  iriChars.push(chars[i]);
338
312
  i++;
339
313
  }
340
- if (i >= n || chars[i] !== ">") {
341
- throw new Error("Unterminated IRI <...>");
314
+ if (i >= n || chars[i] !== '>') {
315
+ throw new Error('Unterminated IRI <...>');
342
316
  }
343
317
  i++; // skip '>'
344
- const iri = iriChars.join("");
345
- tokens.push(new Token("IriRef", iri));
318
+ const iri = iriChars.join('');
319
+ tokens.push(new Token('IriRef', iri));
346
320
  continue;
347
321
  }
348
322
 
349
323
  // 4) Path + datatype operators: !, ^, ^^
350
- if (c === "!") {
351
- tokens.push(new Token("OpPathFwd"));
324
+ if (c === '!') {
325
+ tokens.push(new Token('OpPathFwd'));
352
326
  i += 1;
353
327
  continue;
354
328
  }
355
- if (c === "^") {
356
- if (peek(1) === "^") {
357
- tokens.push(new Token("HatHat"));
329
+ if (c === '^') {
330
+ if (peek(1) === '^') {
331
+ tokens.push(new Token('HatHat'));
358
332
  i += 2;
359
333
  continue;
360
334
  }
361
- tokens.push(new Token("OpPathRev"));
335
+ tokens.push(new Token('OpPathRev'));
362
336
  i += 1;
363
337
  continue;
364
338
  }
365
339
 
366
340
  // 5) Single-character punctuation
367
- if ("{}()[];,.".includes(c)) {
341
+ if ('{}()[];,.'.includes(c)) {
368
342
  const mapping = {
369
- "{": "LBrace",
370
- "}": "RBrace",
371
- "(": "LParen",
372
- ")": "RParen",
373
- "[": "LBracket",
374
- "]": "RBracket",
375
- ";": "Semicolon",
376
- ",": "Comma",
377
- ".": "Dot",
343
+ '{': 'LBrace',
344
+ '}': 'RBrace',
345
+ '(': 'LParen',
346
+ ')': 'RParen',
347
+ '[': 'LBracket',
348
+ ']': 'RBracket',
349
+ ';': 'Semicolon',
350
+ ',': 'Comma',
351
+ '.': 'Dot',
378
352
  };
379
353
  tokens.push(new Token(mapping[c]));
380
354
  i++;
@@ -397,22 +371,21 @@ function lex(inputText) {
397
371
  }
398
372
  let cc = chars[i];
399
373
  i++;
400
- if (cc === "\\") {
374
+ if (cc === '\\') {
401
375
  // Preserve escapes verbatim (same behavior as short strings)
402
376
  if (i < n) {
403
377
  const esc = chars[i];
404
378
  i++;
405
- sChars.push("\\");
379
+ sChars.push('\\');
406
380
  sChars.push(esc);
407
381
  }
408
382
  continue;
409
383
  }
410
384
  sChars.push(cc);
411
385
  }
412
- if (!closed)
413
- throw new Error('Unterminated long string literal """..."""');
414
- const s = '"""' + sChars.join("") + '"""';
415
- tokens.push(new Token("Literal", s));
386
+ if (!closed) throw new Error('Unterminated long string literal """..."""');
387
+ const s = '"""' + sChars.join('') + '"""';
388
+ tokens.push(new Token('Literal', s));
416
389
  continue;
417
390
  }
418
391
 
@@ -422,11 +395,11 @@ function lex(inputText) {
422
395
  while (i < n) {
423
396
  let cc = chars[i];
424
397
  i++;
425
- if (cc === "\\") {
398
+ if (cc === '\\') {
426
399
  if (i < n) {
427
400
  const esc = chars[i];
428
401
  i++;
429
- sChars.push("\\");
402
+ sChars.push('\\');
430
403
  sChars.push(esc);
431
404
  }
432
405
  continue;
@@ -434,13 +407,13 @@ function lex(inputText) {
434
407
  if (cc === '"') break;
435
408
  sChars.push(cc);
436
409
  }
437
- const s = '"' + sChars.join("") + '"';
438
- tokens.push(new Token("Literal", s));
410
+ const s = '"' + sChars.join('') + '"';
411
+ tokens.push(new Token('Literal', s));
439
412
  continue;
440
413
  }
441
414
 
442
415
  // Variable ?name
443
- if (c === "?") {
416
+ if (c === '?') {
444
417
  i++;
445
418
  const nameChars = [];
446
419
  let cc;
@@ -448,19 +421,16 @@ function lex(inputText) {
448
421
  nameChars.push(cc);
449
422
  i++;
450
423
  }
451
- const name = nameChars.join("");
452
- tokens.push(new Token("Var", name));
424
+ const name = nameChars.join('');
425
+ tokens.push(new Token('Var', name));
453
426
  continue;
454
427
  }
455
428
 
456
429
  // Directives: @prefix, @base (and language tags after string literals)
457
- if (c === "@") {
430
+ if (c === '@') {
458
431
  const prevTok = tokens.length ? tokens[tokens.length - 1] : null;
459
432
  const prevWasQuotedLiteral =
460
- prevTok &&
461
- prevTok.typ === "Literal" &&
462
- typeof prevTok.value === "string" &&
463
- prevTok.value.startsWith('"');
433
+ prevTok && prevTok.typ === 'Literal' && typeof prevTok.value === 'string' && prevTok.value.startsWith('"');
464
434
 
465
435
  i++; // consume '@'
466
436
 
@@ -476,8 +446,8 @@ function lex(inputText) {
476
446
  tagChars.push(cc);
477
447
  i++;
478
448
  }
479
- while (peek() === "-") {
480
- tagChars.push("-");
449
+ while (peek() === '-') {
450
+ tagChars.push('-');
481
451
  i++; // consume '-'
482
452
  const segChars = [];
483
453
  while ((cc = peek()) !== null && /[A-Za-z0-9]/.test(cc)) {
@@ -485,13 +455,11 @@ function lex(inputText) {
485
455
  i++;
486
456
  }
487
457
  if (!segChars.length) {
488
- throw new Error(
489
- "Invalid language tag (expected [A-Za-z0-9]+ after '-')",
490
- );
458
+ throw new Error("Invalid language tag (expected [A-Za-z0-9]+ after '-')");
491
459
  }
492
460
  tagChars.push(...segChars);
493
461
  }
494
- tokens.push(new Token("LangTag", tagChars.join("")));
462
+ tokens.push(new Token('LangTag', tagChars.join('')));
495
463
  continue;
496
464
  }
497
465
 
@@ -502,18 +470,15 @@ function lex(inputText) {
502
470
  wordChars.push(cc);
503
471
  i++;
504
472
  }
505
- const word = wordChars.join("");
506
- if (word === "prefix") tokens.push(new Token("AtPrefix"));
507
- else if (word === "base") tokens.push(new Token("AtBase"));
473
+ const word = wordChars.join('');
474
+ if (word === 'prefix') tokens.push(new Token('AtPrefix'));
475
+ else if (word === 'base') tokens.push(new Token('AtBase'));
508
476
  else throw new Error(`Unknown directive @${word}`);
509
477
  continue;
510
478
  }
511
479
 
512
480
  // 6) Numeric literal (integer or float)
513
- if (
514
- /[0-9]/.test(c) ||
515
- (c === "-" && peek(1) !== null && /[0-9]/.test(peek(1)))
516
- ) {
481
+ if (/[0-9]/.test(c) || (c === '-' && peek(1) !== null && /[0-9]/.test(peek(1)))) {
517
482
  const numChars = [c];
518
483
  i++;
519
484
  while (i < n) {
@@ -523,9 +488,9 @@ function lex(inputText) {
523
488
  i++;
524
489
  continue;
525
490
  }
526
- if (cc === ".") {
491
+ if (cc === '.') {
527
492
  if (i + 1 < n && /[0-9]/.test(chars[i + 1])) {
528
- numChars.push(".");
493
+ numChars.push('.');
529
494
  i++;
530
495
  continue;
531
496
  } else {
@@ -534,7 +499,7 @@ function lex(inputText) {
534
499
  }
535
500
  break;
536
501
  }
537
- tokens.push(new Token("Literal", numChars.join("")));
502
+ tokens.push(new Token('Literal', numChars.join('')));
538
503
  continue;
539
504
  }
540
505
 
@@ -548,17 +513,17 @@ function lex(inputText) {
548
513
  if (!wordChars.length) {
549
514
  throw new Error(`Unexpected char: ${JSON.stringify(c)}`);
550
515
  }
551
- const word = wordChars.join("");
552
- if (word === "true" || word === "false") {
553
- tokens.push(new Token("Literal", word));
516
+ const word = wordChars.join('');
517
+ if (word === 'true' || word === 'false') {
518
+ tokens.push(new Token('Literal', word));
554
519
  } else if ([...word].every((ch) => /[0-9.\-]/.test(ch))) {
555
- tokens.push(new Token("Literal", word));
520
+ tokens.push(new Token('Literal', word));
556
521
  } else {
557
- tokens.push(new Token("Ident", word));
522
+ tokens.push(new Token('Ident', word));
558
523
  }
559
524
  }
560
525
 
561
- tokens.push(new Token("EOF"));
526
+ tokens.push(new Token('EOF'));
562
527
  return tokens;
563
528
  }
564
529
 
@@ -573,16 +538,16 @@ class PrefixEnv {
573
538
 
574
539
  static newDefault() {
575
540
  const m = {};
576
- m["rdf"] = RDF_NS;
577
- m["rdfs"] = RDFS_NS;
578
- m["xsd"] = XSD_NS;
579
- m["log"] = LOG_NS;
580
- m["math"] = MATH_NS;
581
- m["string"] = STRING_NS;
582
- m["list"] = LIST_NS;
583
- m["time"] = TIME_NS;
584
- m["genid"] = SKOLEM_NS;
585
- m[""] = "";
541
+ m['rdf'] = RDF_NS;
542
+ m['rdfs'] = RDFS_NS;
543
+ m['xsd'] = XSD_NS;
544
+ m['log'] = LOG_NS;
545
+ m['math'] = MATH_NS;
546
+ m['string'] = STRING_NS;
547
+ m['list'] = LIST_NS;
548
+ m['time'] = TIME_NS;
549
+ m['genid'] = SKOLEM_NS;
550
+ m[''] = '';
586
551
  return new PrefixEnv(m);
587
552
  }
588
553
 
@@ -591,9 +556,9 @@ class PrefixEnv {
591
556
  }
592
557
 
593
558
  expandQName(q) {
594
- if (q.includes(":")) {
595
- const [p, local] = q.split(":", 2);
596
- const base = this.map[p] || "";
559
+ if (q.includes(':')) {
560
+ const [p, local] = q.split(':', 2);
561
+ const base = this.map[p] || '';
597
562
  if (base) return base + local;
598
563
  return q;
599
564
  }
@@ -613,7 +578,7 @@ class PrefixEnv {
613
578
  }
614
579
  if (best === null) return null;
615
580
  const [p, local] = best;
616
- if (p === "") return `:${local}`;
581
+ if (p === '') return `:${local}`;
617
582
  return `${p}:${local}`;
618
583
  }
619
584
 
@@ -745,7 +710,7 @@ class Parser {
745
710
 
746
711
  expectDot() {
747
712
  const tok = this.next();
748
- if (tok.typ !== "Dot") {
713
+ if (tok.typ !== 'Dot') {
749
714
  throw new Error(`Expected '.', got ${tok.toString()}`);
750
715
  }
751
716
  }
@@ -755,21 +720,21 @@ class Parser {
755
720
  const forwardRules = [];
756
721
  const backwardRules = [];
757
722
 
758
- while (this.peek().typ !== "EOF") {
759
- if (this.peek().typ === "AtPrefix") {
723
+ while (this.peek().typ !== 'EOF') {
724
+ if (this.peek().typ === 'AtPrefix') {
760
725
  this.next();
761
726
  this.parsePrefixDirective();
762
- } else if (this.peek().typ === "AtBase") {
727
+ } else if (this.peek().typ === 'AtBase') {
763
728
  this.next();
764
729
  this.parseBaseDirective();
765
730
  } else {
766
731
  const first = this.parseTerm();
767
- if (this.peek().typ === "OpImplies") {
732
+ if (this.peek().typ === 'OpImplies') {
768
733
  this.next();
769
734
  const second = this.parseTerm();
770
735
  this.expectDot();
771
736
  forwardRules.push(this.makeRule(first, second, true));
772
- } else if (this.peek().typ === "OpImpliedBy") {
737
+ } else if (this.peek().typ === 'OpImpliedBy') {
773
738
  this.next();
774
739
  const second = this.parseTerm();
775
740
  this.expectDot();
@@ -777,21 +742,15 @@ class Parser {
777
742
  } else {
778
743
  let more;
779
744
 
780
- if (this.peek().typ === "Dot") {
745
+ if (this.peek().typ === 'Dot') {
781
746
  // Allow a bare blank-node property list statement, e.g. `[ a :Statement ].`
782
747
  const lastTok = this.toks[this.pos - 1];
783
- if (
784
- this.pendingTriples.length > 0 &&
785
- lastTok &&
786
- lastTok.typ === "RBracket"
787
- ) {
748
+ if (this.pendingTriples.length > 0 && lastTok && lastTok.typ === 'RBracket') {
788
749
  more = this.pendingTriples;
789
750
  this.pendingTriples = [];
790
751
  this.next(); // consume '.'
791
752
  } else {
792
- throw new Error(
793
- `Unexpected '.' after term; missing predicate/object list`,
794
- );
753
+ throw new Error(`Unexpected '.' after term; missing predicate/object list`);
795
754
  }
796
755
  } else {
797
756
  more = this.parsePredicateObjectList(first);
@@ -800,17 +759,9 @@ class Parser {
800
759
 
801
760
  // normalize explicit log:implies / log:impliedBy at top-level
802
761
  for (const tr of more) {
803
- if (
804
- isLogImplies(tr.p) &&
805
- tr.s instanceof FormulaTerm &&
806
- tr.o instanceof FormulaTerm
807
- ) {
762
+ if (isLogImplies(tr.p) && tr.s instanceof FormulaTerm && tr.o instanceof FormulaTerm) {
808
763
  forwardRules.push(this.makeRule(tr.s, tr.o, true));
809
- } else if (
810
- isLogImpliedBy(tr.p) &&
811
- tr.s instanceof FormulaTerm &&
812
- tr.o instanceof FormulaTerm
813
- ) {
764
+ } else if (isLogImpliedBy(tr.p) && tr.s instanceof FormulaTerm && tr.o instanceof FormulaTerm) {
814
765
  backwardRules.push(this.makeRule(tr.s, tr.o, false));
815
766
  } else {
816
767
  triples.push(tr);
@@ -825,26 +776,26 @@ class Parser {
825
776
 
826
777
  parsePrefixDirective() {
827
778
  const tok = this.next();
828
- if (tok.typ !== "Ident") {
779
+ if (tok.typ !== 'Ident') {
829
780
  throw new Error(`Expected prefix name, got ${tok.toString()}`);
830
781
  }
831
- const pref = tok.value || "";
832
- const prefName = pref.endsWith(":") ? pref.slice(0, -1) : pref;
782
+ const pref = tok.value || '';
783
+ const prefName = pref.endsWith(':') ? pref.slice(0, -1) : pref;
833
784
 
834
- if (this.peek().typ === "Dot") {
785
+ if (this.peek().typ === 'Dot') {
835
786
  this.next();
836
787
  if (!this.prefixes.map.hasOwnProperty(prefName)) {
837
- this.prefixes.set(prefName, "");
788
+ this.prefixes.set(prefName, '');
838
789
  }
839
790
  return;
840
791
  }
841
792
 
842
793
  const tok2 = this.next();
843
794
  let iri;
844
- if (tok2.typ === "IriRef") {
845
- iri = tok2.value || "";
846
- } else if (tok2.typ === "Ident") {
847
- iri = this.prefixes.expandQName(tok2.value || "");
795
+ if (tok2.typ === 'IriRef') {
796
+ iri = tok2.value || '';
797
+ } else if (tok2.typ === 'Ident') {
798
+ iri = this.prefixes.expandQName(tok2.value || '');
848
799
  } else {
849
800
  throw new Error(`Expected IRI after @prefix, got ${tok2.toString()}`);
850
801
  }
@@ -855,30 +806,28 @@ class Parser {
855
806
  parseBaseDirective() {
856
807
  const tok = this.next();
857
808
  let iri;
858
- if (tok.typ === "IriRef") {
859
- iri = tok.value || "";
860
- } else if (tok.typ === "Ident") {
861
- iri = tok.value || "";
809
+ if (tok.typ === 'IriRef') {
810
+ iri = tok.value || '';
811
+ } else if (tok.typ === 'Ident') {
812
+ iri = tok.value || '';
862
813
  } else {
863
814
  throw new Error(`Expected IRI after @base, got ${tok.toString()}`);
864
815
  }
865
816
  this.expectDot();
866
- this.prefixes.set("", iri);
817
+ this.prefixes.set('', iri);
867
818
  }
868
819
 
869
820
  parseTerm() {
870
821
  let t = this.parsePathItem();
871
822
 
872
- while (this.peek().typ === "OpPathFwd" || this.peek().typ === "OpPathRev") {
823
+ while (this.peek().typ === 'OpPathFwd' || this.peek().typ === 'OpPathRev') {
873
824
  const dir = this.next().typ; // OpPathFwd | OpPathRev
874
825
  const pred = this.parsePathItem();
875
826
 
876
827
  this.blankCounter += 1;
877
828
  const bn = new Blank(`_:b${this.blankCounter}`);
878
829
 
879
- this.pendingTriples.push(
880
- dir === "OpPathFwd" ? new Triple(t, pred, bn) : new Triple(bn, pred, t),
881
- );
830
+ this.pendingTriples.push(dir === 'OpPathFwd' ? new Triple(t, pred, bn) : new Triple(bn, pred, t));
882
831
 
883
832
  t = bn;
884
833
  }
@@ -891,81 +840,75 @@ class Parser {
891
840
  const typ = tok.typ;
892
841
  const val = tok.value;
893
842
 
894
- if (typ === "Equals") {
895
- return new Iri(OWL_NS + "sameAs");
843
+ if (typ === 'Equals') {
844
+ return new Iri(OWL_NS + 'sameAs');
896
845
  }
897
846
 
898
- if (typ === "IriRef") {
899
- return new Iri(val || "");
847
+ if (typ === 'IriRef') {
848
+ return new Iri(val || '');
900
849
  }
901
850
 
902
- if (typ === "Ident") {
903
- const name = val || "";
904
- if (name === "a") {
905
- return new Iri(RDF_NS + "type");
906
- } else if (name.startsWith("_:")) {
851
+ if (typ === 'Ident') {
852
+ const name = val || '';
853
+ if (name === 'a') {
854
+ return new Iri(RDF_NS + 'type');
855
+ } else if (name.startsWith('_:')) {
907
856
  return new Blank(name);
908
- } else if (name.includes(":")) {
857
+ } else if (name.includes(':')) {
909
858
  return new Iri(this.prefixes.expandQName(name));
910
859
  } else {
911
860
  return new Iri(name);
912
861
  }
913
862
  }
914
863
 
915
- if (typ === "Literal") {
916
- let s = val || "";
864
+ if (typ === 'Literal') {
865
+ let s = val || '';
917
866
 
918
867
  // Optional language tag: "..."@en, per N3 LANGTAG production.
919
- if (this.peek().typ === "LangTag") {
868
+ if (this.peek().typ === 'LangTag') {
920
869
  // Only quoted string literals can carry a language tag.
921
870
  if (!(s.startsWith('"') && s.endsWith('"'))) {
922
- throw new Error(
923
- "Language tag is only allowed on quoted string literals",
924
- );
871
+ throw new Error('Language tag is only allowed on quoted string literals');
925
872
  }
926
873
  const langTok = this.next();
927
- const lang = langTok.value || "";
874
+ const lang = langTok.value || '';
928
875
  s = `${s}@${lang}`;
929
876
 
930
877
  // N3/Turtle: language tags and datatypes are mutually exclusive.
931
- if (this.peek().typ === "HatHat") {
932
- throw new Error(
933
- "A literal cannot have both a language tag (@...) and a datatype (^^...)",
934
- );
878
+ if (this.peek().typ === 'HatHat') {
879
+ throw new Error('A literal cannot have both a language tag (@...) and a datatype (^^...)');
935
880
  }
936
881
  }
937
882
 
938
- if (this.peek().typ === "HatHat") {
883
+ if (this.peek().typ === 'HatHat') {
939
884
  this.next();
940
885
  const dtTok = this.next();
941
886
  let dtIri;
942
- if (dtTok.typ === "IriRef") {
943
- dtIri = dtTok.value || "";
944
- } else if (dtTok.typ === "Ident") {
945
- const qn = dtTok.value || "";
946
- if (qn.includes(":")) dtIri = this.prefixes.expandQName(qn);
887
+ if (dtTok.typ === 'IriRef') {
888
+ dtIri = dtTok.value || '';
889
+ } else if (dtTok.typ === 'Ident') {
890
+ const qn = dtTok.value || '';
891
+ if (qn.includes(':')) dtIri = this.prefixes.expandQName(qn);
947
892
  else dtIri = qn;
948
893
  } else {
949
- throw new Error(
950
- `Expected datatype after ^^, got ${dtTok.toString()}`,
951
- );
894
+ throw new Error(`Expected datatype after ^^, got ${dtTok.toString()}`);
952
895
  }
953
896
  s = `${s}^^<${dtIri}>`;
954
897
  }
955
898
  return new Literal(s);
956
899
  }
957
900
 
958
- if (typ === "Var") return new Var(val || "");
959
- if (typ === "LParen") return this.parseList();
960
- if (typ === "LBracket") return this.parseBlank();
961
- if (typ === "LBrace") return this.parseFormula();
901
+ if (typ === 'Var') return new Var(val || '');
902
+ if (typ === 'LParen') return this.parseList();
903
+ if (typ === 'LBracket') return this.parseBlank();
904
+ if (typ === 'LBrace') return this.parseFormula();
962
905
 
963
906
  throw new Error(`Unexpected term token: ${tok.toString()}`);
964
907
  }
965
908
 
966
909
  parseList() {
967
910
  const elems = [];
968
- while (this.peek().typ !== "RParen") {
911
+ while (this.peek().typ !== 'RParen') {
969
912
  elems.push(this.parseTerm());
970
913
  }
971
914
  this.next(); // consume ')'
@@ -974,7 +917,7 @@ class Parser {
974
917
 
975
918
  parseBlank() {
976
919
  // [] or [ ... ] property list
977
- if (this.peek().typ === "RBracket") {
920
+ if (this.peek().typ === 'RBracket') {
978
921
  this.next();
979
922
  this.blankCounter += 1;
980
923
  return new Blank(`_:b${this.blankCounter}`);
@@ -989,10 +932,10 @@ class Parser {
989
932
  // Verb (can also be 'a')
990
933
  let pred;
991
934
  let invert = false;
992
- if (this.peek().typ === "Ident" && (this.peek().value || "") === "a") {
935
+ if (this.peek().typ === 'Ident' && (this.peek().value || '') === 'a') {
993
936
  this.next();
994
- pred = new Iri(RDF_NS + "type");
995
- } else if (this.peek().typ === "OpPredInvert") {
937
+ pred = new Iri(RDF_NS + 'type');
938
+ } else if (this.peek().typ === 'OpPredInvert') {
996
939
  this.next(); // consume "<-"
997
940
  pred = this.parseTerm();
998
941
  invert = true;
@@ -1002,33 +945,27 @@ class Parser {
1002
945
 
1003
946
  // Object list: o1, o2, ...
1004
947
  const objs = [this.parseTerm()];
1005
- while (this.peek().typ === "Comma") {
948
+ while (this.peek().typ === 'Comma') {
1006
949
  this.next();
1007
950
  objs.push(this.parseTerm());
1008
951
  }
1009
952
 
1010
953
  for (const o of objs) {
1011
- this.pendingTriples.push(
1012
- invert ? new Triple(o, pred, subj) : new Triple(subj, pred, o),
1013
- );
954
+ this.pendingTriples.push(invert ? new Triple(o, pred, subj) : new Triple(subj, pred, o));
1014
955
  }
1015
956
 
1016
- if (this.peek().typ === "Semicolon") {
957
+ if (this.peek().typ === 'Semicolon') {
1017
958
  this.next();
1018
- if (this.peek().typ === "RBracket") break;
959
+ if (this.peek().typ === 'RBracket') break;
1019
960
  continue;
1020
961
  }
1021
962
  break;
1022
963
  }
1023
964
 
1024
- if (this.peek().typ === "RBracket") {
965
+ if (this.peek().typ === 'RBracket') {
1025
966
  this.next();
1026
967
  } else {
1027
- throw new Error(
1028
- `Expected ']' at end of blank node property list, got ${JSON.stringify(
1029
- this.peek(),
1030
- )}`,
1031
- );
968
+ throw new Error(`Expected ']' at end of blank node property list, got ${JSON.stringify(this.peek())}`);
1032
969
  }
1033
970
 
1034
971
  return new Blank(id);
@@ -1036,49 +973,45 @@ class Parser {
1036
973
 
1037
974
  parseFormula() {
1038
975
  const triples = [];
1039
- while (this.peek().typ !== "RBrace") {
976
+ while (this.peek().typ !== 'RBrace') {
1040
977
  const left = this.parseTerm();
1041
- if (this.peek().typ === "OpImplies") {
978
+ if (this.peek().typ === 'OpImplies') {
1042
979
  this.next();
1043
980
  const right = this.parseTerm();
1044
- const pred = new Iri(LOG_NS + "implies");
981
+ const pred = new Iri(LOG_NS + 'implies');
1045
982
  triples.push(new Triple(left, pred, right));
1046
- if (this.peek().typ === "Dot") this.next();
1047
- else if (this.peek().typ === "RBrace") {
983
+ if (this.peek().typ === 'Dot') this.next();
984
+ else if (this.peek().typ === 'RBrace') {
1048
985
  // ok
1049
986
  } else {
1050
987
  throw new Error(`Expected '.' or '}', got ${this.peek().toString()}`);
1051
988
  }
1052
- } else if (this.peek().typ === "OpImpliedBy") {
989
+ } else if (this.peek().typ === 'OpImpliedBy') {
1053
990
  this.next();
1054
991
  const right = this.parseTerm();
1055
- const pred = new Iri(LOG_NS + "impliedBy");
992
+ const pred = new Iri(LOG_NS + 'impliedBy');
1056
993
  triples.push(new Triple(left, pred, right));
1057
- if (this.peek().typ === "Dot") this.next();
1058
- else if (this.peek().typ === "RBrace") {
994
+ if (this.peek().typ === 'Dot') this.next();
995
+ else if (this.peek().typ === 'RBrace') {
1059
996
  // ok
1060
997
  } else {
1061
998
  throw new Error(`Expected '.' or '}', got ${this.peek().toString()}`);
1062
999
  }
1063
1000
  } else {
1064
1001
  // Allow a bare blank-node property list statement inside a formula, e.g. `{ [ a :X ]. }`
1065
- if (this.peek().typ === "Dot" || this.peek().typ === "RBrace") {
1002
+ if (this.peek().typ === 'Dot' || this.peek().typ === 'RBrace') {
1066
1003
  const lastTok = this.toks[this.pos - 1];
1067
- if (
1068
- this.pendingTriples.length > 0 &&
1069
- lastTok &&
1070
- lastTok.typ === "RBracket"
1071
- ) {
1004
+ if (this.pendingTriples.length > 0 && lastTok && lastTok.typ === 'RBracket') {
1072
1005
  triples.push(...this.pendingTriples);
1073
1006
  this.pendingTriples = [];
1074
- if (this.peek().typ === "Dot") this.next();
1007
+ if (this.peek().typ === 'Dot') this.next();
1075
1008
  continue;
1076
1009
  }
1077
1010
  }
1078
1011
 
1079
1012
  triples.push(...this.parsePredicateObjectList(left));
1080
- if (this.peek().typ === "Dot") this.next();
1081
- else if (this.peek().typ === "RBrace") {
1013
+ if (this.peek().typ === 'Dot') this.next();
1014
+ else if (this.peek().typ === 'RBrace') {
1082
1015
  // ok
1083
1016
  } else {
1084
1017
  throw new Error(`Expected '.' or '}', got ${this.peek().toString()}`);
@@ -1102,10 +1035,10 @@ class Parser {
1102
1035
  let verb;
1103
1036
  let invert = false;
1104
1037
 
1105
- if (this.peek().typ === "Ident" && (this.peek().value || "") === "a") {
1038
+ if (this.peek().typ === 'Ident' && (this.peek().value || '') === 'a') {
1106
1039
  this.next();
1107
- verb = new Iri(RDF_NS + "type");
1108
- } else if (this.peek().typ === "OpPredInvert") {
1040
+ verb = new Iri(RDF_NS + 'type');
1041
+ } else if (this.peek().typ === 'OpPredInvert') {
1109
1042
  this.next(); // "<-"
1110
1043
  verb = this.parseTerm();
1111
1044
  invert = true;
@@ -1126,9 +1059,9 @@ class Parser {
1126
1059
  out.push(new Triple(invert ? o : subject, verb, invert ? subject : o));
1127
1060
  }
1128
1061
 
1129
- if (this.peek().typ === "Semicolon") {
1062
+ if (this.peek().typ === 'Semicolon') {
1130
1063
  this.next();
1131
- if (this.peek().typ === "Dot") break;
1064
+ if (this.peek().typ === 'Dot') break;
1132
1065
  continue;
1133
1066
  }
1134
1067
  break;
@@ -1139,7 +1072,7 @@ class Parser {
1139
1072
 
1140
1073
  parseObjectList() {
1141
1074
  const objs = [this.parseTerm()];
1142
- while (this.peek().typ === "Comma") {
1075
+ while (this.peek().typ === 'Comma') {
1143
1076
  this.next();
1144
1077
  objs.push(this.parseTerm());
1145
1078
  }
@@ -1159,7 +1092,7 @@ class Parser {
1159
1092
 
1160
1093
  let isFuse = false;
1161
1094
  if (isForward) {
1162
- if (conclTerm instanceof Literal && conclTerm.value === "false") {
1095
+ if (conclTerm instanceof Literal && conclTerm.value === 'false') {
1163
1096
  isFuse = true;
1164
1097
  }
1165
1098
  }
@@ -1167,7 +1100,7 @@ class Parser {
1167
1100
  let rawPremise;
1168
1101
  if (premiseTerm instanceof FormulaTerm) {
1169
1102
  rawPremise = premiseTerm.triples;
1170
- } else if (premiseTerm instanceof Literal && premiseTerm.value === "true") {
1103
+ } else if (premiseTerm instanceof Literal && premiseTerm.value === 'true') {
1171
1104
  rawPremise = [];
1172
1105
  } else {
1173
1106
  rawPremise = [];
@@ -1176,7 +1109,7 @@ class Parser {
1176
1109
  let rawConclusion;
1177
1110
  if (conclTerm instanceof FormulaTerm) {
1178
1111
  rawConclusion = conclTerm.triples;
1179
- } else if (conclTerm instanceof Literal && conclTerm.value === "false") {
1112
+ } else if (conclTerm instanceof Literal && conclTerm.value === 'false') {
1180
1113
  rawConclusion = [];
1181
1114
  } else {
1182
1115
  rawConclusion = [];
@@ -1188,9 +1121,7 @@ class Parser {
1188
1121
  const [premise0, conclusion] = liftBlankRuleVars(rawPremise, rawConclusion);
1189
1122
 
1190
1123
  // Reorder constraints for *forward* rules.
1191
- const premise = isForward
1192
- ? reorderPremiseForConstraints(premise0)
1193
- : premise0;
1124
+ const premise = isForward ? reorderPremiseForConstraints(premise0) : premise0;
1194
1125
 
1195
1126
  return new Rule(premise, conclusion, isForward, isFuse, headBlankLabels);
1196
1127
  }
@@ -1221,12 +1152,7 @@ function liftBlankRuleVars(premise, conclusion) {
1221
1152
  }
1222
1153
  if (t instanceof FormulaTerm) {
1223
1154
  const triples = t.triples.map(
1224
- (tr) =>
1225
- new Triple(
1226
- convertTerm(tr.s, mapping, counter),
1227
- convertTerm(tr.p, mapping, counter),
1228
- convertTerm(tr.o, mapping, counter),
1229
- ),
1155
+ (tr) => new Triple(convertTerm(tr.s, mapping, counter), convertTerm(tr.p, mapping, counter), convertTerm(tr.o, mapping, counter)),
1230
1156
  );
1231
1157
  return new FormulaTerm(triples);
1232
1158
  }
@@ -1234,11 +1160,7 @@ function liftBlankRuleVars(premise, conclusion) {
1234
1160
  }
1235
1161
 
1236
1162
  function convertTriple(tr, mapping, counter) {
1237
- return new Triple(
1238
- convertTerm(tr.s, mapping, counter),
1239
- convertTerm(tr.p, mapping, counter),
1240
- convertTerm(tr.o, mapping, counter),
1241
- );
1163
+ return new Triple(convertTerm(tr.s, mapping, counter), convertTerm(tr.p, mapping, counter), convertTerm(tr.o, mapping, counter));
1242
1164
  }
1243
1165
 
1244
1166
  const mapping = {};
@@ -1263,28 +1185,18 @@ function skolemizeTermForHeadBlanks(t, headBlankLabels, mapping, skCounter) {
1263
1185
  }
1264
1186
 
1265
1187
  if (t instanceof ListTerm) {
1266
- return new ListTerm(
1267
- t.elems.map((e) =>
1268
- skolemizeTermForHeadBlanks(e, headBlankLabels, mapping, skCounter),
1269
- ),
1270
- );
1188
+ return new ListTerm(t.elems.map((e) => skolemizeTermForHeadBlanks(e, headBlankLabels, mapping, skCounter)));
1271
1189
  }
1272
1190
 
1273
1191
  if (t instanceof OpenListTerm) {
1274
1192
  return new OpenListTerm(
1275
- t.prefix.map((e) =>
1276
- skolemizeTermForHeadBlanks(e, headBlankLabels, mapping, skCounter),
1277
- ),
1193
+ t.prefix.map((e) => skolemizeTermForHeadBlanks(e, headBlankLabels, mapping, skCounter)),
1278
1194
  t.tailVar,
1279
1195
  );
1280
1196
  }
1281
1197
 
1282
1198
  if (t instanceof FormulaTerm) {
1283
- return new FormulaTerm(
1284
- t.triples.map((tr) =>
1285
- skolemizeTripleForHeadBlanks(tr, headBlankLabels, mapping, skCounter),
1286
- ),
1287
- );
1199
+ return new FormulaTerm(t.triples.map((tr) => skolemizeTripleForHeadBlanks(tr, headBlankLabels, mapping, skCounter)));
1288
1200
  }
1289
1201
 
1290
1202
  return t;
@@ -1372,8 +1284,7 @@ function alphaEqTermInFormula(a, b, vmap, bmap) {
1372
1284
  if (a instanceof ListTerm && b instanceof ListTerm) {
1373
1285
  if (a.elems.length !== b.elems.length) return false;
1374
1286
  for (let i = 0; i < a.elems.length; i++) {
1375
- if (!alphaEqTermInFormula(a.elems[i], b.elems[i], vmap, bmap))
1376
- return false;
1287
+ if (!alphaEqTermInFormula(a.elems[i], b.elems[i], vmap, bmap)) return false;
1377
1288
  }
1378
1289
  return true;
1379
1290
  }
@@ -1381,8 +1292,7 @@ function alphaEqTermInFormula(a, b, vmap, bmap) {
1381
1292
  if (a instanceof OpenListTerm && b instanceof OpenListTerm) {
1382
1293
  if (a.prefix.length !== b.prefix.length) return false;
1383
1294
  for (let i = 0; i < a.prefix.length; i++) {
1384
- if (!alphaEqTermInFormula(a.prefix[i], b.prefix[i], vmap, bmap))
1385
- return false;
1295
+ if (!alphaEqTermInFormula(a.prefix[i], b.prefix[i], vmap, bmap)) return false;
1386
1296
  }
1387
1297
  // tailVar is a var-name string, so treat it as renamable too
1388
1298
  return alphaEqVarName(a.tailVar, b.tailVar, vmap);
@@ -1398,9 +1308,7 @@ function alphaEqTermInFormula(a, b, vmap, bmap) {
1398
1308
 
1399
1309
  function alphaEqTripleInFormula(a, b, vmap, bmap) {
1400
1310
  return (
1401
- alphaEqTermInFormula(a.s, b.s, vmap, bmap) &&
1402
- alphaEqTermInFormula(a.p, b.p, vmap, bmap) &&
1403
- alphaEqTermInFormula(a.o, b.o, vmap, bmap)
1311
+ alphaEqTermInFormula(a.s, b.s, vmap, bmap) && alphaEqTermInFormula(a.p, b.p, vmap, bmap) && alphaEqTermInFormula(a.o, b.o, vmap, bmap)
1404
1312
  );
1405
1313
  }
1406
1314
 
@@ -1419,8 +1327,7 @@ function alphaEqFormulaTriples(xs, ys) {
1419
1327
  if (used[j]) continue;
1420
1328
  const y = ys[j];
1421
1329
  // Cheap pruning when both predicates are IRIs.
1422
- if (x.p instanceof Iri && y.p instanceof Iri && x.p.value !== y.p.value)
1423
- continue;
1330
+ if (x.p instanceof Iri && y.p instanceof Iri && x.p.value !== y.p.value) continue;
1424
1331
 
1425
1332
  const v2 = { ...vmap };
1426
1333
  const b2 = { ...bmap };
@@ -1458,8 +1365,7 @@ function alphaEqTerm(a, b, bmap) {
1458
1365
  return true;
1459
1366
  }
1460
1367
  if (a instanceof OpenListTerm && b instanceof OpenListTerm) {
1461
- if (a.tailVar !== b.tailVar || a.prefix.length !== b.prefix.length)
1462
- return false;
1368
+ if (a.tailVar !== b.tailVar || a.prefix.length !== b.prefix.length) return false;
1463
1369
  for (let i = 0; i < a.prefix.length; i++) {
1464
1370
  if (!alphaEqTerm(a.prefix[i], b.prefix[i], bmap)) return false;
1465
1371
  }
@@ -1474,11 +1380,7 @@ function alphaEqTerm(a, b, bmap) {
1474
1380
 
1475
1381
  function alphaEqTriple(a, b) {
1476
1382
  const bmap = {};
1477
- return (
1478
- alphaEqTerm(a.s, b.s, bmap) &&
1479
- alphaEqTerm(a.p, b.p, bmap) &&
1480
- alphaEqTerm(a.o, b.o, bmap)
1481
- );
1383
+ return alphaEqTerm(a.s, b.s, bmap) && alphaEqTerm(a.p, b.p, bmap) && alphaEqTerm(a.o, b.o, bmap);
1482
1384
  }
1483
1385
 
1484
1386
  function hasAlphaEquiv(triples, tr) {
@@ -1499,8 +1401,8 @@ function hasAlphaEquiv(triples, tr) {
1499
1401
  // - __wildHeadPred: Rule[] (non-IRI head predicate)
1500
1402
 
1501
1403
  function termFastKey(t) {
1502
- if (t instanceof Iri) return "I:" + t.value;
1503
- if (t instanceof Literal) return "L:" + t.value;
1404
+ if (t instanceof Iri) return 'I:' + t.value;
1405
+ if (t instanceof Literal) return 'L:' + t.value;
1504
1406
  return null;
1505
1407
  }
1506
1408
 
@@ -1509,23 +1411,23 @@ function tripleFastKey(tr) {
1509
1411
  const kp = termFastKey(tr.p);
1510
1412
  const ko = termFastKey(tr.o);
1511
1413
  if (ks === null || kp === null || ko === null) return null;
1512
- return ks + "\t" + kp + "\t" + ko;
1414
+ return ks + '\t' + kp + '\t' + ko;
1513
1415
  }
1514
1416
 
1515
1417
  function ensureFactIndexes(facts) {
1516
1418
  if (facts.__byPred && facts.__byPO && facts.__keySet) return;
1517
1419
 
1518
- Object.defineProperty(facts, "__byPred", {
1420
+ Object.defineProperty(facts, '__byPred', {
1519
1421
  value: new Map(),
1520
1422
  enumerable: false,
1521
1423
  writable: true,
1522
1424
  });
1523
- Object.defineProperty(facts, "__byPO", {
1425
+ Object.defineProperty(facts, '__byPO', {
1524
1426
  value: new Map(),
1525
1427
  enumerable: false,
1526
1428
  writable: true,
1527
1429
  });
1528
- Object.defineProperty(facts, "__keySet", {
1430
+ Object.defineProperty(facts, '__keySet', {
1529
1431
  value: new Set(),
1530
1432
  enumerable: false,
1531
1433
  writable: true,
@@ -1620,12 +1522,12 @@ function pushFactIndexed(facts, tr) {
1620
1522
  function ensureBackRuleIndexes(backRules) {
1621
1523
  if (backRules.__byHeadPred && backRules.__wildHeadPred) return;
1622
1524
 
1623
- Object.defineProperty(backRules, "__byHeadPred", {
1525
+ Object.defineProperty(backRules, '__byHeadPred', {
1624
1526
  value: new Map(),
1625
1527
  enumerable: false,
1626
1528
  writable: true,
1627
1529
  });
1628
- Object.defineProperty(backRules, "__wildHeadPred", {
1530
+ Object.defineProperty(backRules, '__wildHeadPred', {
1629
1531
  value: [],
1630
1532
  enumerable: false,
1631
1533
  writable: true,
@@ -1655,19 +1557,19 @@ function indexBackRule(backRules, r) {
1655
1557
  // ============================================================================
1656
1558
 
1657
1559
  function isRdfTypePred(p) {
1658
- return p instanceof Iri && p.value === RDF_NS + "type";
1560
+ return p instanceof Iri && p.value === RDF_NS + 'type';
1659
1561
  }
1660
1562
 
1661
1563
  function isOwlSameAsPred(t) {
1662
- return t instanceof Iri && t.value === OWL_NS + "sameAs";
1564
+ return t instanceof Iri && t.value === OWL_NS + 'sameAs';
1663
1565
  }
1664
1566
 
1665
1567
  function isLogImplies(p) {
1666
- return p instanceof Iri && p.value === LOG_NS + "implies";
1568
+ return p instanceof Iri && p.value === LOG_NS + 'implies';
1667
1569
  }
1668
1570
 
1669
1571
  function isLogImpliedBy(p) {
1670
- return p instanceof Iri && p.value === LOG_NS + "impliedBy";
1572
+ return p instanceof Iri && p.value === LOG_NS + 'impliedBy';
1671
1573
  }
1672
1574
 
1673
1575
  // ============================================================================
@@ -1680,44 +1582,40 @@ function isConstraintBuiltin(tr) {
1680
1582
 
1681
1583
  // math: numeric comparisons (no new bindings, just tests)
1682
1584
  if (
1683
- v === MATH_NS + "equalTo" ||
1684
- v === MATH_NS + "greaterThan" ||
1685
- v === MATH_NS + "lessThan" ||
1686
- v === MATH_NS + "notEqualTo" ||
1687
- v === MATH_NS + "notGreaterThan" ||
1688
- v === MATH_NS + "notLessThan"
1585
+ v === MATH_NS + 'equalTo' ||
1586
+ v === MATH_NS + 'greaterThan' ||
1587
+ v === MATH_NS + 'lessThan' ||
1588
+ v === MATH_NS + 'notEqualTo' ||
1589
+ v === MATH_NS + 'notGreaterThan' ||
1590
+ v === MATH_NS + 'notLessThan'
1689
1591
  ) {
1690
1592
  return true;
1691
1593
  }
1692
1594
 
1693
1595
  // list: membership test with no bindings
1694
- if (v === LIST_NS + "notMember") {
1596
+ if (v === LIST_NS + 'notMember') {
1695
1597
  return true;
1696
1598
  }
1697
1599
 
1698
1600
  // log: tests that are purely constraints (no new bindings)
1699
- if (
1700
- v === LOG_NS + "forAllIn" ||
1701
- v === LOG_NS + "notEqualTo" ||
1702
- v === LOG_NS + "notIncludes"
1703
- ) {
1601
+ if (v === LOG_NS + 'forAllIn' || v === LOG_NS + 'notEqualTo' || v === LOG_NS + 'notIncludes') {
1704
1602
  return true;
1705
1603
  }
1706
1604
 
1707
1605
  // string: relational / membership style tests (no bindings)
1708
1606
  if (
1709
- v === STRING_NS + "contains" ||
1710
- v === STRING_NS + "containsIgnoringCase" ||
1711
- v === STRING_NS + "endsWith" ||
1712
- v === STRING_NS + "equalIgnoringCase" ||
1713
- v === STRING_NS + "greaterThan" ||
1714
- v === STRING_NS + "lessThan" ||
1715
- v === STRING_NS + "matches" ||
1716
- v === STRING_NS + "notEqualIgnoringCase" ||
1717
- v === STRING_NS + "notGreaterThan" ||
1718
- v === STRING_NS + "notLessThan" ||
1719
- v === STRING_NS + "notMatches" ||
1720
- v === STRING_NS + "startsWith"
1607
+ v === STRING_NS + 'contains' ||
1608
+ v === STRING_NS + 'containsIgnoringCase' ||
1609
+ v === STRING_NS + 'endsWith' ||
1610
+ v === STRING_NS + 'equalIgnoringCase' ||
1611
+ v === STRING_NS + 'greaterThan' ||
1612
+ v === STRING_NS + 'lessThan' ||
1613
+ v === STRING_NS + 'matches' ||
1614
+ v === STRING_NS + 'notEqualIgnoringCase' ||
1615
+ v === STRING_NS + 'notGreaterThan' ||
1616
+ v === STRING_NS + 'notLessThan' ||
1617
+ v === STRING_NS + 'notMatches' ||
1618
+ v === STRING_NS + 'startsWith'
1721
1619
  ) {
1722
1620
  return true;
1723
1621
  }
@@ -1751,15 +1649,9 @@ function reorderPremiseForConstraints(premise) {
1751
1649
  function containsVarTerm(t, v) {
1752
1650
  if (t instanceof Var) return t.name === v;
1753
1651
  if (t instanceof ListTerm) return t.elems.some((e) => containsVarTerm(e, v));
1754
- if (t instanceof OpenListTerm)
1755
- return t.prefix.some((e) => containsVarTerm(e, v)) || t.tailVar === v;
1652
+ if (t instanceof OpenListTerm) return t.prefix.some((e) => containsVarTerm(e, v)) || t.tailVar === v;
1756
1653
  if (t instanceof FormulaTerm)
1757
- return t.triples.some(
1758
- (tr) =>
1759
- containsVarTerm(tr.s, v) ||
1760
- containsVarTerm(tr.p, v) ||
1761
- containsVarTerm(tr.o, v),
1762
- );
1654
+ return t.triples.some((tr) => containsVarTerm(tr.s, v) || containsVarTerm(tr.p, v) || containsVarTerm(tr.o, v));
1763
1655
  return false;
1764
1656
  }
1765
1657
 
@@ -1767,28 +1659,21 @@ function isGroundTermInFormula(t) {
1767
1659
  // EYE-style: variables inside formula terms are treated as local placeholders,
1768
1660
  // so they don't make the *surrounding triple* non-ground.
1769
1661
  if (t instanceof OpenListTerm) return false;
1770
- if (t instanceof ListTerm)
1771
- return t.elems.every((e) => isGroundTermInFormula(e));
1772
- if (t instanceof FormulaTerm)
1773
- return t.triples.every((tr) => isGroundTripleInFormula(tr));
1662
+ if (t instanceof ListTerm) return t.elems.every((e) => isGroundTermInFormula(e));
1663
+ if (t instanceof FormulaTerm) return t.triples.every((tr) => isGroundTripleInFormula(tr));
1774
1664
  // Iri/Literal/Blank/Var are all OK inside formulas
1775
1665
  return true;
1776
1666
  }
1777
1667
 
1778
1668
  function isGroundTripleInFormula(tr) {
1779
- return (
1780
- isGroundTermInFormula(tr.s) &&
1781
- isGroundTermInFormula(tr.p) &&
1782
- isGroundTermInFormula(tr.o)
1783
- );
1669
+ return isGroundTermInFormula(tr.s) && isGroundTermInFormula(tr.p) && isGroundTermInFormula(tr.o);
1784
1670
  }
1785
1671
 
1786
1672
  function isGroundTerm(t) {
1787
1673
  if (t instanceof Var) return false;
1788
1674
  if (t instanceof ListTerm) return t.elems.every((e) => isGroundTerm(e));
1789
1675
  if (t instanceof OpenListTerm) return false;
1790
- if (t instanceof FormulaTerm)
1791
- return t.triples.every((tr) => isGroundTripleInFormula(tr));
1676
+ if (t instanceof FormulaTerm) return t.triples.every((tr) => isGroundTripleInFormula(tr));
1792
1677
  return true;
1793
1678
  }
1794
1679
 
@@ -1801,19 +1686,14 @@ function isGroundTriple(tr) {
1801
1686
  // robust to seeing vars/open lists anyway.
1802
1687
  function skolemKeyFromTerm(t) {
1803
1688
  function enc(u) {
1804
- if (u instanceof Iri) return ["I", u.value];
1805
- if (u instanceof Literal) return ["L", u.value];
1806
- if (u instanceof Blank) return ["B", u.label];
1807
- if (u instanceof Var) return ["V", u.name];
1808
- if (u instanceof ListTerm) return ["List", u.elems.map(enc)];
1809
- if (u instanceof OpenListTerm)
1810
- return ["OpenList", u.prefix.map(enc), u.tailVar];
1811
- if (u instanceof FormulaTerm)
1812
- return [
1813
- "Formula",
1814
- u.triples.map((tr) => [enc(tr.s), enc(tr.p), enc(tr.o)]),
1815
- ];
1816
- return ["Other", String(u)];
1689
+ if (u instanceof Iri) return ['I', u.value];
1690
+ if (u instanceof Literal) return ['L', u.value];
1691
+ if (u instanceof Blank) return ['B', u.label];
1692
+ if (u instanceof Var) return ['V', u.name];
1693
+ if (u instanceof ListTerm) return ['List', u.elems.map(enc)];
1694
+ if (u instanceof OpenListTerm) return ['OpenList', u.prefix.map(enc), u.tailVar];
1695
+ if (u instanceof FormulaTerm) return ['Formula', u.triples.map((tr) => [enc(tr.s), enc(tr.p), enc(tr.o)])];
1696
+ return ['Other', String(u)];
1817
1697
  }
1818
1698
  return JSON.stringify(enc(t));
1819
1699
  }
@@ -1861,10 +1741,7 @@ function applySubstTerm(t, s) {
1861
1741
  if (tailApplied instanceof ListTerm) {
1862
1742
  return new ListTerm(newPrefix.concat(tailApplied.elems));
1863
1743
  } else if (tailApplied instanceof OpenListTerm) {
1864
- return new OpenListTerm(
1865
- newPrefix.concat(tailApplied.prefix),
1866
- tailApplied.tailVar,
1867
- );
1744
+ return new OpenListTerm(newPrefix.concat(tailApplied.prefix), tailApplied.tailVar);
1868
1745
  } else {
1869
1746
  return new OpenListTerm(newPrefix, t.tailVar);
1870
1747
  }
@@ -1881,11 +1758,7 @@ function applySubstTerm(t, s) {
1881
1758
  }
1882
1759
 
1883
1760
  function applySubstTriple(tr, s) {
1884
- return new Triple(
1885
- applySubstTerm(tr.s, s),
1886
- applySubstTerm(tr.p, s),
1887
- applySubstTerm(tr.o, s),
1888
- );
1761
+ return new Triple(applySubstTerm(tr.s, s), applySubstTerm(tr.p, s), applySubstTerm(tr.o, s));
1889
1762
  }
1890
1763
 
1891
1764
  function unifyOpenWithList(prefix, tailv, ys, subst) {
@@ -1919,8 +1792,7 @@ function unifyFormulaTriples(xs, ys, subst) {
1919
1792
  const y = ys[j];
1920
1793
 
1921
1794
  // Cheap pruning when both predicates are IRIs.
1922
- if (x.p instanceof Iri && y.p instanceof Iri && x.p.value !== y.p.value)
1923
- continue;
1795
+ if (x.p instanceof Iri && y.p instanceof Iri && x.p.value !== y.p.value) continue;
1924
1796
 
1925
1797
  const s2 = unifyTriple(x, y, s); // IMPORTANT: use `s`, not {}
1926
1798
  if (s2 === null) continue;
@@ -1956,12 +1828,9 @@ function unifyTerm(a, b, subst) {
1956
1828
  }
1957
1829
 
1958
1830
  // Exact matches
1959
- if (a instanceof Iri && b instanceof Iri && a.value === b.value)
1960
- return { ...subst };
1961
- if (a instanceof Literal && b instanceof Literal && a.value === b.value)
1962
- return { ...subst };
1963
- if (a instanceof Blank && b instanceof Blank && a.label === b.label)
1964
- return { ...subst };
1831
+ if (a instanceof Iri && b instanceof Iri && a.value === b.value) return { ...subst };
1832
+ if (a instanceof Literal && b instanceof Literal && a.value === b.value) return { ...subst };
1833
+ if (a instanceof Blank && b instanceof Blank && a.label === b.label) return { ...subst };
1965
1834
 
1966
1835
  // Open list vs concrete list
1967
1836
  if (a instanceof OpenListTerm && b instanceof ListTerm) {
@@ -1973,8 +1842,7 @@ function unifyTerm(a, b, subst) {
1973
1842
 
1974
1843
  // Open list vs open list (same tail var)
1975
1844
  if (a instanceof OpenListTerm && b instanceof OpenListTerm) {
1976
- if (a.tailVar !== b.tailVar || a.prefix.length !== b.prefix.length)
1977
- return null;
1845
+ if (a.tailVar !== b.tailVar || a.prefix.length !== b.prefix.length) return null;
1978
1846
  let s2 = { ...subst };
1979
1847
  for (let i = 0; i < a.prefix.length; i++) {
1980
1848
  s2 = unifyTerm(a.prefix[i], b.prefix[i], s2);
@@ -2040,14 +1908,14 @@ function literalParts(lit) {
2040
1908
  // Also strip an optional language tag from the lexical form:
2041
1909
  // "\"hello\"@en" -> "\"hello\""
2042
1910
  // "\"hello\"@en^^<...>" is rejected earlier in the parser.
2043
- const idx = lit.indexOf("^^");
1911
+ const idx = lit.indexOf('^^');
2044
1912
  let lex = lit;
2045
1913
  let dt = null;
2046
1914
 
2047
1915
  if (idx >= 0) {
2048
1916
  lex = lit.slice(0, idx);
2049
1917
  dt = lit.slice(idx + 2).trim();
2050
- if (dt.startsWith("<") && dt.endsWith(">")) {
1918
+ if (dt.startsWith('<') && dt.endsWith('>')) {
2051
1919
  dt = dt.slice(1, -1);
2052
1920
  }
2053
1921
  }
@@ -2055,11 +1923,7 @@ function literalParts(lit) {
2055
1923
  // Strip LANGTAG from the lexical form when present.
2056
1924
  if (lex.length >= 2 && lex[0] === '"') {
2057
1925
  const lastQuote = lex.lastIndexOf('"');
2058
- if (
2059
- lastQuote > 0 &&
2060
- lastQuote < lex.length - 1 &&
2061
- lex[lastQuote + 1] === "@"
2062
- ) {
1926
+ if (lastQuote > 0 && lastQuote < lex.length - 1 && lex[lastQuote + 1] === '@') {
2063
1927
  const lang = lex.slice(lastQuote + 2);
2064
1928
  if (/^[A-Za-z]+(?:-[A-Za-z0-9]+)*$/.test(lang)) {
2065
1929
  lex = lex.slice(0, lastQuote + 1);
@@ -2119,17 +1983,17 @@ function termToJsStringDecoded(t) {
2119
1983
 
2120
1984
  function jsonPointerUnescape(seg) {
2121
1985
  // RFC6901: ~1 -> '/', ~0 -> '~'
2122
- let out = "";
1986
+ let out = '';
2123
1987
  for (let i = 0; i < seg.length; i++) {
2124
1988
  const c = seg[i];
2125
- if (c !== "~") {
1989
+ if (c !== '~') {
2126
1990
  out += c;
2127
1991
  continue;
2128
1992
  }
2129
1993
  if (i + 1 >= seg.length) return null;
2130
1994
  const n = seg[i + 1];
2131
- if (n === "0") out += "~";
2132
- else if (n === "1") out += "/";
1995
+ if (n === '0') out += '~';
1996
+ else if (n === '1') out += '/';
2133
1997
  else return null;
2134
1998
  i++;
2135
1999
  }
@@ -2137,13 +2001,13 @@ function jsonPointerUnescape(seg) {
2137
2001
  }
2138
2002
 
2139
2003
  function jsonToTerm(v) {
2140
- if (v === null) return makeStringLiteral("null");
2141
- if (typeof v === "string") return makeStringLiteral(v);
2142
- if (typeof v === "number") return new Literal(String(v));
2143
- if (typeof v === "boolean") return new Literal(v ? "true" : "false");
2004
+ if (v === null) return makeStringLiteral('null');
2005
+ if (typeof v === 'string') return makeStringLiteral(v);
2006
+ if (typeof v === 'number') return new Literal(String(v));
2007
+ if (typeof v === 'boolean') return new Literal(v ? 'true' : 'false');
2144
2008
  if (Array.isArray(v)) return new ListTerm(v.map(jsonToTerm));
2145
2009
 
2146
- if (v && typeof v === "object") {
2010
+ if (v && typeof v === 'object') {
2147
2011
  return makeRdfJsonLiteral(JSON.stringify(v));
2148
2012
  }
2149
2013
  return null;
@@ -2153,7 +2017,7 @@ function jsonPointerLookup(jsonText, pointer) {
2153
2017
  let ptr = pointer;
2154
2018
 
2155
2019
  // Support URI fragment form "#/a/b"
2156
- if (ptr.startsWith("#")) {
2020
+ if (ptr.startsWith('#')) {
2157
2021
  try {
2158
2022
  ptr = decodeURIComponent(ptr.slice(1));
2159
2023
  } catch {
@@ -2178,17 +2042,17 @@ function jsonPointerLookup(jsonText, pointer) {
2178
2042
 
2179
2043
  let cur = entry.parsed;
2180
2044
 
2181
- if (ptr === "") {
2045
+ if (ptr === '') {
2182
2046
  const t = jsonToTerm(cur);
2183
2047
  entry.ptrCache.set(ptr, t);
2184
2048
  return t;
2185
2049
  }
2186
- if (!ptr.startsWith("/")) {
2050
+ if (!ptr.startsWith('/')) {
2187
2051
  entry.ptrCache.set(ptr, null);
2188
2052
  return null;
2189
2053
  }
2190
2054
 
2191
- const parts = ptr.split("/").slice(1);
2055
+ const parts = ptr.split('/').slice(1);
2192
2056
  for (const raw of parts) {
2193
2057
  const seg = jsonPointerUnescape(raw);
2194
2058
  if (seg === null) {
@@ -2207,7 +2071,7 @@ function jsonPointerLookup(jsonText, pointer) {
2207
2071
  return null;
2208
2072
  }
2209
2073
  cur = cur[idx];
2210
- } else if (cur !== null && typeof cur === "object") {
2074
+ } else if (cur !== null && typeof cur === 'object') {
2211
2075
  if (!Object.prototype.hasOwnProperty.call(cur, seg)) {
2212
2076
  entry.ptrCache.set(ptr, null);
2213
2077
  return null;
@@ -2227,23 +2091,23 @@ function jsonPointerLookup(jsonText, pointer) {
2227
2091
  // Tiny subset of sprintf: supports only %s and %%.
2228
2092
  // Good enough for most N3 string:format use cases that just splice strings.
2229
2093
  function simpleStringFormat(fmt, args) {
2230
- let out = "";
2094
+ let out = '';
2231
2095
  let argIndex = 0;
2232
2096
 
2233
2097
  for (let i = 0; i < fmt.length; i++) {
2234
2098
  const ch = fmt[i];
2235
- if (ch === "%" && i + 1 < fmt.length) {
2099
+ if (ch === '%' && i + 1 < fmt.length) {
2236
2100
  const spec = fmt[i + 1];
2237
2101
 
2238
- if (spec === "s") {
2239
- const arg = argIndex < args.length ? args[argIndex++] : "";
2102
+ if (spec === 's') {
2103
+ const arg = argIndex < args.length ? args[argIndex++] : '';
2240
2104
  out += arg;
2241
2105
  i++;
2242
2106
  continue;
2243
2107
  }
2244
2108
 
2245
- if (spec === "%") {
2246
- out += "%";
2109
+ if (spec === '%') {
2110
+ out += '%';
2247
2111
  i++;
2248
2112
  continue;
2249
2113
  }
@@ -2260,44 +2124,38 @@ function simpleStringFormat(fmt, args) {
2260
2124
  // -----------------------------------------------------------------------------
2261
2125
  // Strict numeric literal parsing for math: builtins
2262
2126
  // -----------------------------------------------------------------------------
2263
- const XSD_DECIMAL_DT = XSD_NS + "decimal";
2264
- const XSD_DOUBLE_DT = XSD_NS + "double";
2265
- const XSD_FLOAT_DT = XSD_NS + "float";
2266
- const XSD_INTEGER_DT = XSD_NS + "integer";
2127
+ const XSD_DECIMAL_DT = XSD_NS + 'decimal';
2128
+ const XSD_DOUBLE_DT = XSD_NS + 'double';
2129
+ const XSD_FLOAT_DT = XSD_NS + 'float';
2130
+ const XSD_INTEGER_DT = XSD_NS + 'integer';
2267
2131
 
2268
2132
  // Integer-derived datatypes from XML Schema Part 2 (and commonly used ones).
2269
2133
  const XSD_INTEGER_DERIVED_DTS = new Set([
2270
2134
  XSD_INTEGER_DT,
2271
- XSD_NS + "nonPositiveInteger",
2272
- XSD_NS + "negativeInteger",
2273
- XSD_NS + "long",
2274
- XSD_NS + "int",
2275
- XSD_NS + "short",
2276
- XSD_NS + "byte",
2277
- XSD_NS + "nonNegativeInteger",
2278
- XSD_NS + "unsignedLong",
2279
- XSD_NS + "unsignedInt",
2280
- XSD_NS + "unsignedShort",
2281
- XSD_NS + "unsignedByte",
2282
- XSD_NS + "positiveInteger",
2135
+ XSD_NS + 'nonPositiveInteger',
2136
+ XSD_NS + 'negativeInteger',
2137
+ XSD_NS + 'long',
2138
+ XSD_NS + 'int',
2139
+ XSD_NS + 'short',
2140
+ XSD_NS + 'byte',
2141
+ XSD_NS + 'nonNegativeInteger',
2142
+ XSD_NS + 'unsignedLong',
2143
+ XSD_NS + 'unsignedInt',
2144
+ XSD_NS + 'unsignedShort',
2145
+ XSD_NS + 'unsignedByte',
2146
+ XSD_NS + 'positiveInteger',
2283
2147
  ]);
2284
2148
 
2285
2149
  function isQuotedLexical(lex) {
2286
2150
  // Note: the lexer stores long strings with literal delimiters: """..."""
2287
2151
  return (
2288
- (lex.length >= 2 && lex[0] === '"' && lex[lex.length - 1] === '"') ||
2289
- (lex.length >= 6 && lex.startsWith('"""') && lex.endsWith('"""'))
2152
+ (lex.length >= 2 && lex[0] === '"' && lex[lex.length - 1] === '"') || (lex.length >= 6 && lex.startsWith('"""') && lex.endsWith('"""'))
2290
2153
  );
2291
2154
  }
2292
2155
 
2293
2156
  function isXsdNumericDatatype(dt) {
2294
2157
  if (dt === null) return false;
2295
- return (
2296
- dt === XSD_DECIMAL_DT ||
2297
- dt === XSD_DOUBLE_DT ||
2298
- dt === XSD_FLOAT_DT ||
2299
- XSD_INTEGER_DERIVED_DTS.has(dt)
2300
- );
2158
+ return dt === XSD_DECIMAL_DT || dt === XSD_DOUBLE_DT || dt === XSD_FLOAT_DT || XSD_INTEGER_DERIVED_DTS.has(dt);
2301
2159
  }
2302
2160
 
2303
2161
  function isXsdIntegerDatatype(dt) {
@@ -2389,9 +2247,9 @@ function formatNum(n) {
2389
2247
  function parseXsdDateTerm(t) {
2390
2248
  if (!(t instanceof Literal)) return null;
2391
2249
  const [lex, dt] = literalParts(t.value);
2392
- if (dt !== XSD_NS + "date") return null;
2250
+ if (dt !== XSD_NS + 'date') return null;
2393
2251
  const val = stripQuotes(lex);
2394
- const d = new Date(val + "T00:00:00Z");
2252
+ const d = new Date(val + 'T00:00:00Z');
2395
2253
  if (Number.isNaN(d.getTime())) return null;
2396
2254
  return d;
2397
2255
  }
@@ -2399,7 +2257,7 @@ function parseXsdDateTerm(t) {
2399
2257
  function parseXsdDatetimeTerm(t) {
2400
2258
  if (!(t instanceof Literal)) return null;
2401
2259
  const [lex, dt] = literalParts(t.value);
2402
- if (dt !== XSD_NS + "dateTime") return null;
2260
+ if (dt !== XSD_NS + 'dateTime') return null;
2403
2261
  const val = stripQuotes(lex);
2404
2262
  const d = new Date(val);
2405
2263
  if (Number.isNaN(d.getTime())) return null;
@@ -2414,9 +2272,9 @@ function parseDatetimeLike(t) {
2414
2272
 
2415
2273
  function parseIso8601DurationToSeconds(s) {
2416
2274
  if (!s) return null;
2417
- if (s[0] !== "P") return null;
2275
+ if (s[0] !== 'P') return null;
2418
2276
  const it = s.slice(1);
2419
- let num = "";
2277
+ let num = '';
2420
2278
  let inTime = false;
2421
2279
  let years = 0,
2422
2280
  months = 0,
@@ -2427,7 +2285,7 @@ function parseIso8601DurationToSeconds(s) {
2427
2285
  seconds = 0;
2428
2286
 
2429
2287
  for (const c of it) {
2430
- if (c === "T") {
2288
+ if (c === 'T') {
2431
2289
  inTime = true;
2432
2290
  continue;
2433
2291
  }
@@ -2438,25 +2296,19 @@ function parseIso8601DurationToSeconds(s) {
2438
2296
  if (!num) return null;
2439
2297
  const val = Number(num);
2440
2298
  if (Number.isNaN(val)) return null;
2441
- num = "";
2442
- if (!inTime && c === "Y") years += val;
2443
- else if (!inTime && c === "M") months += val;
2444
- else if (!inTime && c === "W") weeks += val;
2445
- else if (!inTime && c === "D") days += val;
2446
- else if (inTime && c === "H") hours += val;
2447
- else if (inTime && c === "M") minutes += val;
2448
- else if (inTime && c === "S") seconds += val;
2299
+ num = '';
2300
+ if (!inTime && c === 'Y') years += val;
2301
+ else if (!inTime && c === 'M') months += val;
2302
+ else if (!inTime && c === 'W') weeks += val;
2303
+ else if (!inTime && c === 'D') days += val;
2304
+ else if (inTime && c === 'H') hours += val;
2305
+ else if (inTime && c === 'M') minutes += val;
2306
+ else if (inTime && c === 'S') seconds += val;
2449
2307
  else return null;
2450
2308
  }
2451
2309
 
2452
2310
  const totalDays =
2453
- years * 365.2425 +
2454
- months * 30.436875 +
2455
- weeks * 7.0 +
2456
- days +
2457
- hours / 24.0 +
2458
- minutes / (24.0 * 60.0) +
2459
- seconds / (24.0 * 3600.0);
2311
+ years * 365.2425 + months * 30.436875 + weeks * 7.0 + days + hours / 24.0 + minutes / (24.0 * 60.0) + seconds / (24.0 * 3600.0);
2460
2312
 
2461
2313
  return totalDays * 86400.0;
2462
2314
  }
@@ -2465,10 +2317,10 @@ function parseNumericForCompareTerm(t) {
2465
2317
  // Strict: only accept xsd numeric literals, xsd:duration, xsd:date, xsd:dateTime
2466
2318
  // (or untyped numeric tokens).
2467
2319
  const bi = parseIntLiteral(t);
2468
- if (bi !== null) return { kind: "bigint", value: bi };
2320
+ if (bi !== null) return { kind: 'bigint', value: bi };
2469
2321
 
2470
2322
  const nDur = parseNumOrDuration(t);
2471
- if (nDur !== null) return { kind: "number", value: nDur };
2323
+ if (nDur !== null) return { kind: 'number', value: nDur };
2472
2324
  return null;
2473
2325
  }
2474
2326
 
@@ -2476,25 +2328,25 @@ function cmpNumericInfo(aInfo, bInfo, op) {
2476
2328
  // op is one of ">", "<", ">=", "<="
2477
2329
  if (!aInfo || !bInfo) return false;
2478
2330
 
2479
- if (aInfo.kind === "bigint" && bInfo.kind === "bigint") {
2480
- if (op === ">") return aInfo.value > bInfo.value;
2481
- if (op === "<") return aInfo.value < bInfo.value;
2482
- if (op === ">=") return aInfo.value >= bInfo.value;
2483
- if (op === "<=") return aInfo.value <= bInfo.value;
2484
- if (op === "==") return aInfo.value == bInfo.value;
2485
- if (op === "!=") return aInfo.value != bInfo.value;
2331
+ if (aInfo.kind === 'bigint' && bInfo.kind === 'bigint') {
2332
+ if (op === '>') return aInfo.value > bInfo.value;
2333
+ if (op === '<') return aInfo.value < bInfo.value;
2334
+ if (op === '>=') return aInfo.value >= bInfo.value;
2335
+ if (op === '<=') return aInfo.value <= bInfo.value;
2336
+ if (op === '==') return aInfo.value == bInfo.value;
2337
+ if (op === '!=') return aInfo.value != bInfo.value;
2486
2338
  return false;
2487
2339
  }
2488
2340
 
2489
- const a = typeof aInfo.value === "bigint" ? Number(aInfo.value) : aInfo.value;
2490
- const b = typeof bInfo.value === "bigint" ? Number(bInfo.value) : bInfo.value;
2341
+ const a = typeof aInfo.value === 'bigint' ? Number(aInfo.value) : aInfo.value;
2342
+ const b = typeof bInfo.value === 'bigint' ? Number(bInfo.value) : bInfo.value;
2491
2343
 
2492
- if (op === ">") return a > b;
2493
- if (op === "<") return a < b;
2494
- if (op === ">=") return a >= b;
2495
- if (op === "<=") return a <= b;
2496
- if (op === "==") return a == b;
2497
- if (op === "!=") return a != b;
2344
+ if (op === '>') return a > b;
2345
+ if (op === '<') return a < b;
2346
+ if (op === '>=') return a >= b;
2347
+ if (op === '<=') return a <= b;
2348
+ if (op === '==') return a == b;
2349
+ if (op === '!=') return a != b;
2498
2350
  return false;
2499
2351
  }
2500
2352
 
@@ -2505,11 +2357,11 @@ function parseNumOrDuration(t) {
2505
2357
  // xsd:duration
2506
2358
  if (t instanceof Literal) {
2507
2359
  const [lex, dt] = literalParts(t.value);
2508
- if (dt === XSD_NS + "duration") {
2360
+ if (dt === XSD_NS + 'duration') {
2509
2361
  const val = stripQuotes(lex);
2510
- const negative = val.startsWith("-");
2362
+ const negative = val.startsWith('-');
2511
2363
  const core = negative ? val.slice(1) : val;
2512
- if (!core.startsWith("P")) return null;
2364
+ if (!core.startsWith('P')) return null;
2513
2365
  const secs = parseIso8601DurationToSeconds(core);
2514
2366
  if (secs === null) return null;
2515
2367
  return negative ? -secs : secs;
@@ -2568,10 +2420,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2568
2420
  const [lex, _dt] = literalParts(t.value);
2569
2421
  const input = stripQuotes(lex);
2570
2422
  try {
2571
- const digest = nodeCrypto
2572
- .createHash(algo)
2573
- .update(input, "utf8")
2574
- .digest("hex");
2423
+ const digest = nodeCrypto.createHash(algo).update(input, 'utf8').digest('hex');
2575
2424
  // plain string literal with the hex digest
2576
2425
  return new Literal(JSON.stringify(digest));
2577
2426
  } catch (e) {
@@ -2585,8 +2434,8 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2585
2434
 
2586
2435
  // crypto:sha
2587
2436
  // true iff ?o is the SHA-1 hash of the subject string.
2588
- if (g.p instanceof Iri && g.p.value === CRYPTO_NS + "sha") {
2589
- const lit = hashLiteral(g.s, "sha1");
2437
+ if (g.p instanceof Iri && g.p.value === CRYPTO_NS + 'sha') {
2438
+ const lit = hashLiteral(g.s, 'sha1');
2590
2439
  if (!lit) return [];
2591
2440
  if (g.o instanceof Var) {
2592
2441
  const s2 = { ...subst };
@@ -2598,8 +2447,8 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2598
2447
  }
2599
2448
 
2600
2449
  // crypto:md5
2601
- if (g.p instanceof Iri && g.p.value === CRYPTO_NS + "md5") {
2602
- const lit = hashLiteral(g.s, "md5");
2450
+ if (g.p instanceof Iri && g.p.value === CRYPTO_NS + 'md5') {
2451
+ const lit = hashLiteral(g.s, 'md5');
2603
2452
  if (!lit) return [];
2604
2453
  if (g.o instanceof Var) {
2605
2454
  const s2 = { ...subst };
@@ -2611,8 +2460,8 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2611
2460
  }
2612
2461
 
2613
2462
  // crypto:sha256
2614
- if (g.p instanceof Iri && g.p.value === CRYPTO_NS + "sha256") {
2615
- const lit = hashLiteral(g.s, "sha256");
2463
+ if (g.p instanceof Iri && g.p.value === CRYPTO_NS + 'sha256') {
2464
+ const lit = hashLiteral(g.s, 'sha256');
2616
2465
  if (!lit) return [];
2617
2466
  if (g.o instanceof Var) {
2618
2467
  const s2 = { ...subst };
@@ -2624,8 +2473,8 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2624
2473
  }
2625
2474
 
2626
2475
  // crypto:sha512
2627
- if (g.p instanceof Iri && g.p.value === CRYPTO_NS + "sha512") {
2628
- const lit = hashLiteral(g.s, "sha512");
2476
+ if (g.p instanceof Iri && g.p.value === CRYPTO_NS + 'sha512') {
2477
+ const lit = hashLiteral(g.s, 'sha512');
2629
2478
  if (!lit) return [];
2630
2479
  if (g.o instanceof Var) {
2631
2480
  const s2 = { ...subst };
@@ -2641,97 +2490,91 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2641
2490
  // -----------------------------------------------------------------
2642
2491
 
2643
2492
  // math:greaterThan
2644
- if (g.p instanceof Iri && g.p.value === MATH_NS + "greaterThan") {
2493
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'greaterThan') {
2645
2494
  const aInfo = parseNumericForCompareTerm(g.s);
2646
2495
  const bInfo = parseNumericForCompareTerm(g.o);
2647
- if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, ">"))
2648
- return [{ ...subst }];
2496
+ if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, '>')) return [{ ...subst }];
2649
2497
 
2650
2498
  if (g.s instanceof ListTerm && g.s.elems.length === 2) {
2651
2499
  const a2 = parseNumericForCompareTerm(g.s.elems[0]);
2652
2500
  const b2 = parseNumericForCompareTerm(g.s.elems[1]);
2653
- if (a2 && b2 && cmpNumericInfo(a2, b2, ">")) return [{ ...subst }];
2501
+ if (a2 && b2 && cmpNumericInfo(a2, b2, '>')) return [{ ...subst }];
2654
2502
  }
2655
2503
  return [];
2656
2504
  }
2657
2505
 
2658
2506
  // math:lessThan
2659
- if (g.p instanceof Iri && g.p.value === MATH_NS + "lessThan") {
2507
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'lessThan') {
2660
2508
  const aInfo = parseNumericForCompareTerm(g.s);
2661
2509
  const bInfo = parseNumericForCompareTerm(g.o);
2662
- if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, "<"))
2663
- return [{ ...subst }];
2510
+ if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, '<')) return [{ ...subst }];
2664
2511
 
2665
2512
  if (g.s instanceof ListTerm && g.s.elems.length === 2) {
2666
2513
  const a2 = parseNumericForCompareTerm(g.s.elems[0]);
2667
2514
  const b2 = parseNumericForCompareTerm(g.s.elems[1]);
2668
- if (a2 && b2 && cmpNumericInfo(a2, b2, "<")) return [{ ...subst }];
2515
+ if (a2 && b2 && cmpNumericInfo(a2, b2, '<')) return [{ ...subst }];
2669
2516
  }
2670
2517
  return [];
2671
2518
  }
2672
2519
 
2673
2520
  // math:notLessThan
2674
- if (g.p instanceof Iri && g.p.value === MATH_NS + "notLessThan") {
2521
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'notLessThan') {
2675
2522
  const aInfo = parseNumericForCompareTerm(g.s);
2676
2523
  const bInfo = parseNumericForCompareTerm(g.o);
2677
- if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, ">="))
2678
- return [{ ...subst }];
2524
+ if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, '>=')) return [{ ...subst }];
2679
2525
 
2680
2526
  if (g.s instanceof ListTerm && g.s.elems.length === 2) {
2681
2527
  const a2 = parseNumericForCompareTerm(g.s.elems[0]);
2682
2528
  const b2 = parseNumericForCompareTerm(g.s.elems[1]);
2683
- if (a2 && b2 && cmpNumericInfo(a2, b2, ">=")) return [{ ...subst }];
2529
+ if (a2 && b2 && cmpNumericInfo(a2, b2, '>=')) return [{ ...subst }];
2684
2530
  }
2685
2531
  return [];
2686
2532
  }
2687
2533
 
2688
2534
  // math:notGreaterThan
2689
- if (g.p instanceof Iri && g.p.value === MATH_NS + "notGreaterThan") {
2535
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'notGreaterThan') {
2690
2536
  const aInfo = parseNumericForCompareTerm(g.s);
2691
2537
  const bInfo = parseNumericForCompareTerm(g.o);
2692
- if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, "<="))
2693
- return [{ ...subst }];
2538
+ if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, '<=')) return [{ ...subst }];
2694
2539
 
2695
2540
  if (g.s instanceof ListTerm && g.s.elems.length === 2) {
2696
2541
  const a2 = parseNumericForCompareTerm(g.s.elems[0]);
2697
2542
  const b2 = parseNumericForCompareTerm(g.s.elems[1]);
2698
- if (a2 && b2 && cmpNumericInfo(a2, b2, "<=")) return [{ ...subst }];
2543
+ if (a2 && b2 && cmpNumericInfo(a2, b2, '<=')) return [{ ...subst }];
2699
2544
  }
2700
2545
  return [];
2701
2546
  }
2702
2547
 
2703
2548
  // math:equalTo
2704
- if (g.p instanceof Iri && g.p.value === MATH_NS + "equalTo") {
2549
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'equalTo') {
2705
2550
  const aInfo = parseNumericForCompareTerm(g.s);
2706
2551
  const bInfo = parseNumericForCompareTerm(g.o);
2707
- if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, "=="))
2708
- return [{ ...subst }];
2552
+ if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, '==')) return [{ ...subst }];
2709
2553
 
2710
2554
  if (g.s instanceof ListTerm && g.s.elems.length === 2) {
2711
2555
  const a2 = parseNumericForCompareTerm(g.s.elems[0]);
2712
2556
  const b2 = parseNumericForCompareTerm(g.s.elems[1]);
2713
- if (a2 && b2 && cmpNumericInfo(a2, b2, "==")) return [{ ...subst }];
2557
+ if (a2 && b2 && cmpNumericInfo(a2, b2, '==')) return [{ ...subst }];
2714
2558
  }
2715
2559
  return [];
2716
2560
  }
2717
2561
 
2718
2562
  // math:notEqualTo
2719
- if (g.p instanceof Iri && g.p.value === MATH_NS + "notEqualTo") {
2563
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'notEqualTo') {
2720
2564
  const aInfo = parseNumericForCompareTerm(g.s);
2721
2565
  const bInfo = parseNumericForCompareTerm(g.o);
2722
- if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, "!="))
2723
- return [{ ...subst }];
2566
+ if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, '!=')) return [{ ...subst }];
2724
2567
 
2725
2568
  if (g.s instanceof ListTerm && g.s.elems.length === 2) {
2726
2569
  const a2 = parseNumericForCompareTerm(g.s.elems[0]);
2727
2570
  const b2 = parseNumericForCompareTerm(g.s.elems[1]);
2728
- if (a2 && b2 && cmpNumericInfo(a2, b2, "!=")) return [{ ...subst }];
2571
+ if (a2 && b2 && cmpNumericInfo(a2, b2, '!=')) return [{ ...subst }];
2729
2572
  }
2730
2573
  return [];
2731
2574
  }
2732
2575
 
2733
2576
  // math:sum
2734
- if (g.p instanceof Iri && g.p.value === MATH_NS + "sum") {
2577
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'sum') {
2735
2578
  if (g.s instanceof ListTerm && g.s.elems.length >= 2) {
2736
2579
  const xs = g.s.elems;
2737
2580
  const values = [];
@@ -2742,7 +2585,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2742
2585
  }
2743
2586
 
2744
2587
  let lit;
2745
- const allBig = values.every((v) => typeof v === "bigint");
2588
+ const allBig = values.every((v) => typeof v === 'bigint');
2746
2589
  if (allBig) {
2747
2590
  let total = 0n;
2748
2591
  for (const v of values) total += v;
@@ -2750,7 +2593,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2750
2593
  } else {
2751
2594
  let total = 0.0;
2752
2595
  for (const v of values) {
2753
- total += typeof v === "bigint" ? Number(v) : v;
2596
+ total += typeof v === 'bigint' ? Number(v) : v;
2754
2597
  }
2755
2598
  lit = new Literal(formatNum(total));
2756
2599
  }
@@ -2767,7 +2610,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2767
2610
  }
2768
2611
 
2769
2612
  // math:product
2770
- if (g.p instanceof Iri && g.p.value === MATH_NS + "product") {
2613
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'product') {
2771
2614
  if (g.s instanceof ListTerm && g.s.elems.length >= 2) {
2772
2615
  const xs = g.s.elems;
2773
2616
  const values = [];
@@ -2778,7 +2621,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2778
2621
  }
2779
2622
 
2780
2623
  let lit;
2781
- const allBig = values.every((v) => typeof v === "bigint");
2624
+ const allBig = values.every((v) => typeof v === 'bigint');
2782
2625
  if (allBig) {
2783
2626
  let prod = 1n;
2784
2627
  for (const v of values) prod *= v;
@@ -2786,7 +2629,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2786
2629
  } else {
2787
2630
  let prod = 1.0;
2788
2631
  for (const v of values) {
2789
- prod *= typeof v === "bigint" ? Number(v) : v;
2632
+ prod *= typeof v === 'bigint' ? Number(v) : v;
2790
2633
  }
2791
2634
  lit = new Literal(formatNum(prod));
2792
2635
  }
@@ -2804,7 +2647,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2804
2647
  }
2805
2648
 
2806
2649
  // math:difference
2807
- if (g.p instanceof Iri && g.p.value === MATH_NS + "difference") {
2650
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'difference') {
2808
2651
  if (g.s instanceof ListTerm && g.s.elems.length === 2) {
2809
2652
  const [a0, b0] = g.s.elems;
2810
2653
 
@@ -2859,7 +2702,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2859
2702
  }
2860
2703
 
2861
2704
  // math:quotient
2862
- if (g.p instanceof Iri && g.p.value === MATH_NS + "quotient") {
2705
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'quotient') {
2863
2706
  if (g.s instanceof ListTerm && g.s.elems.length === 2) {
2864
2707
  const a = parseNum(g.s.elems[0]);
2865
2708
  const b = parseNum(g.s.elems[1]);
@@ -2881,7 +2724,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2881
2724
  // math:integerQuotient
2882
2725
  // Schema: ( $a $b ) math:integerQuotient $q
2883
2726
  // Cwm: divide first integer by second integer, ignoring remainder. :contentReference[oaicite:1]{index=1}
2884
- if (g.p instanceof Iri && g.p.value === MATH_NS + "integerQuotient") {
2727
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'integerQuotient') {
2885
2728
  if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
2886
2729
  const [a0, b0] = g.s.elems;
2887
2730
 
@@ -2920,7 +2763,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2920
2763
  }
2921
2764
 
2922
2765
  // math:exponentiation
2923
- if (g.p instanceof Iri && g.p.value === MATH_NS + "exponentiation") {
2766
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'exponentiation') {
2924
2767
  if (g.s instanceof ListTerm && g.s.elems.length === 2) {
2925
2768
  const a = parseNum(g.s.elems[0]);
2926
2769
  const b0 = g.s.elems[1];
@@ -2954,7 +2797,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2954
2797
  }
2955
2798
 
2956
2799
  // math:negation
2957
- if (g.p instanceof Iri && g.p.value === MATH_NS + "negation") {
2800
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'negation') {
2958
2801
  const a = parseNum(g.s);
2959
2802
  if (a !== null && g.o instanceof Var) {
2960
2803
  const s2 = { ...subst };
@@ -2976,7 +2819,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2976
2819
  }
2977
2820
 
2978
2821
  // math:absoluteValue
2979
- if (g.p instanceof Iri && g.p.value === MATH_NS + "absoluteValue") {
2822
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'absoluteValue') {
2980
2823
  const a = parseNum(g.s);
2981
2824
  if (a !== null && g.o instanceof Var) {
2982
2825
  const s2 = { ...subst };
@@ -2991,7 +2834,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2991
2834
  }
2992
2835
 
2993
2836
  // math:cos
2994
- if (g.p instanceof Iri && g.p.value === MATH_NS + "cos") {
2837
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'cos') {
2995
2838
  const a = parseNum(g.s);
2996
2839
  if (a !== null) {
2997
2840
  const cVal = Math.cos(a);
@@ -3008,7 +2851,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3008
2851
  }
3009
2852
 
3010
2853
  // math:sin
3011
- if (g.p instanceof Iri && g.p.value === MATH_NS + "sin") {
2854
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'sin') {
3012
2855
  const a = parseNum(g.s);
3013
2856
  if (a !== null) {
3014
2857
  const cVal = Math.sin(a);
@@ -3025,7 +2868,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3025
2868
  }
3026
2869
 
3027
2870
  // math:acos
3028
- if (g.p instanceof Iri && g.p.value === MATH_NS + "acos") {
2871
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'acos') {
3029
2872
  const a = parseNum(g.s);
3030
2873
  if (a !== null) {
3031
2874
  const cVal = Math.acos(a);
@@ -3044,7 +2887,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3044
2887
  }
3045
2888
 
3046
2889
  // math:asin
3047
- if (g.p instanceof Iri && g.p.value === MATH_NS + "asin") {
2890
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'asin') {
3048
2891
  const a = parseNum(g.s);
3049
2892
  if (a !== null) {
3050
2893
  const cVal = Math.asin(a);
@@ -3063,7 +2906,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3063
2906
  }
3064
2907
 
3065
2908
  // math:atan
3066
- if (g.p instanceof Iri && g.p.value === MATH_NS + "atan") {
2909
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'atan') {
3067
2910
  const a = parseNum(g.s);
3068
2911
  if (a !== null) {
3069
2912
  const cVal = Math.atan(a);
@@ -3083,9 +2926,9 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3083
2926
 
3084
2927
  // math:cosh
3085
2928
  // Hyperbolic cosine
3086
- if (g.p instanceof Iri && g.p.value === MATH_NS + "cosh") {
2929
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'cosh') {
3087
2930
  const a = parseNum(g.s);
3088
- if (a !== null && typeof Math.cosh === "function") {
2931
+ if (a !== null && typeof Math.cosh === 'function') {
3089
2932
  const cVal = Math.cosh(a);
3090
2933
  if (Number.isFinite(cVal)) {
3091
2934
  if (g.o instanceof Var) {
@@ -3103,7 +2946,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3103
2946
 
3104
2947
  // math:degrees
3105
2948
  // Convert radians -> degrees
3106
- if (g.p instanceof Iri && g.p.value === MATH_NS + "degrees") {
2949
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'degrees') {
3107
2950
  const a = parseNum(g.s);
3108
2951
  if (a !== null) {
3109
2952
  const cVal = (a * 180.0) / Math.PI;
@@ -3124,7 +2967,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3124
2967
  // math:remainder
3125
2968
  // Subject is a list (dividend divisor); object is the remainder.
3126
2969
  // Schema: ( $a $b ) math:remainder $r
3127
- if (g.p instanceof Iri && g.p.value === MATH_NS + "remainder") {
2970
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'remainder') {
3128
2971
  if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
3129
2972
  const a = parseNum(g.s.elems[0]);
3130
2973
  const b = parseNum(g.s.elems[1]);
@@ -3146,7 +2989,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3146
2989
  // Round to nearest integer.
3147
2990
  // If there are two such numbers, then the one closest to positive infinity is returned.
3148
2991
  // Schema: $s math:rounded $o
3149
- if (g.p instanceof Iri && g.p.value === MATH_NS + "rounded") {
2992
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'rounded') {
3150
2993
  const a = parseNum(g.s);
3151
2994
  if (a === null) return [];
3152
2995
  const rVal = Math.round(a);
@@ -3162,9 +3005,9 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3162
3005
  }
3163
3006
 
3164
3007
  // math:sinh
3165
- if (g.p instanceof Iri && g.p.value === MATH_NS + "sinh") {
3008
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'sinh') {
3166
3009
  const a = parseNum(g.s);
3167
- if (a !== null && typeof Math.sinh === "function") {
3010
+ if (a !== null && typeof Math.sinh === 'function') {
3168
3011
  const cVal = Math.sinh(a);
3169
3012
  if (Number.isFinite(cVal)) {
3170
3013
  if (g.o instanceof Var) {
@@ -3181,7 +3024,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3181
3024
  }
3182
3025
 
3183
3026
  // math:tan
3184
- if (g.p instanceof Iri && g.p.value === MATH_NS + "tan") {
3027
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'tan') {
3185
3028
  const a = parseNum(g.s);
3186
3029
  if (a !== null) {
3187
3030
  const cVal = Math.tan(a);
@@ -3200,9 +3043,9 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3200
3043
  }
3201
3044
 
3202
3045
  // math:tanh
3203
- if (g.p instanceof Iri && g.p.value === MATH_NS + "tanh") {
3046
+ if (g.p instanceof Iri && g.p.value === MATH_NS + 'tanh') {
3204
3047
  const a = parseNum(g.s);
3205
- if (a !== null && typeof Math.tanh === "function") {
3048
+ if (a !== null && typeof Math.tanh === 'function') {
3206
3049
  const cVal = Math.tanh(a);
3207
3050
  if (Number.isFinite(cVal)) {
3208
3051
  if (g.o instanceof Var) {
@@ -3224,7 +3067,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3224
3067
 
3225
3068
  // time:localTime
3226
3069
  // "" time:localTime ?D. binds ?D to “now” as xsd:dateTime.
3227
- if (g.p instanceof Iri && g.p.value === TIME_NS + "localTime") {
3070
+ if (g.p instanceof Iri && g.p.value === TIME_NS + 'localTime') {
3228
3071
  const now = getNowLex();
3229
3072
 
3230
3073
  if (g.o instanceof Var) {
@@ -3246,7 +3089,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3246
3089
  // list:append
3247
3090
  // true if and only if $o is the concatenation of all lists $s.i.
3248
3091
  // Schema: ( $s.i?[*] )+ list:append $o?
3249
- if (g.p instanceof Iri && g.p.value === LIST_NS + "append") {
3092
+ if (g.p instanceof Iri && g.p.value === LIST_NS + 'append') {
3250
3093
  if (!(g.s instanceof ListTerm)) return [];
3251
3094
  const parts = g.s.elems;
3252
3095
  if (g.o instanceof ListTerm) {
@@ -3270,7 +3113,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3270
3113
  // list:first
3271
3114
  // true iff $s is a list and $o is the first member of that list.
3272
3115
  // Schema: $s+ list:first $o-
3273
- if (g.p instanceof Iri && g.p.value === LIST_NS + "first") {
3116
+ if (g.p instanceof Iri && g.p.value === LIST_NS + 'first') {
3274
3117
  if (!(g.s instanceof ListTerm)) return [];
3275
3118
  if (!g.s.elems.length) return [];
3276
3119
  const first = g.s.elems[0];
@@ -3281,7 +3124,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3281
3124
  // list:rest
3282
3125
  // true iff $s is a (non-empty) list and $o is the rest (tail) of that list.
3283
3126
  // Schema: $s+ list:rest $o-
3284
- if (g.p instanceof Iri && g.p.value === LIST_NS + "rest") {
3127
+ if (g.p instanceof Iri && g.p.value === LIST_NS + 'rest') {
3285
3128
  // Closed list: (a b c) -> (b c)
3286
3129
  if (g.s instanceof ListTerm) {
3287
3130
  if (!g.s.elems.length) return [];
@@ -3310,7 +3153,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3310
3153
 
3311
3154
  // rdf:first (alias of list:first)
3312
3155
  // Schema: $s+ rdf:first $o-
3313
- if (g.p instanceof Iri && g.p.value === RDF_NS + "first") {
3156
+ if (g.p instanceof Iri && g.p.value === RDF_NS + 'first') {
3314
3157
  if (!(g.s instanceof ListTerm)) return [];
3315
3158
  if (!g.s.elems.length) return [];
3316
3159
  const first = g.s.elems[0];
@@ -3320,7 +3163,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3320
3163
 
3321
3164
  // rdf:rest (alias of list:rest)
3322
3165
  // Schema: $s+ rdf:rest $o-
3323
- if (g.p instanceof Iri && g.p.value === RDF_NS + "rest") {
3166
+ if (g.p instanceof Iri && g.p.value === RDF_NS + 'rest') {
3324
3167
  // Closed list: (a b c) -> (b c)
3325
3168
  if (g.s instanceof ListTerm) {
3326
3169
  if (!g.s.elems.length) return [];
@@ -3346,7 +3189,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3346
3189
  // Multi-solution builtin:
3347
3190
  // For a list subject $s, generate solutions by unifying $o with (index value).
3348
3191
  // This allows $o to be a variable (e.g., ?Y) or a pattern (e.g., (?i "Dewey")).
3349
- if (g.p instanceof Iri && g.p.value === LIST_NS + "iterate") {
3192
+ if (g.p instanceof Iri && g.p.value === LIST_NS + 'iterate') {
3350
3193
  if (!(g.s instanceof ListTerm)) return [];
3351
3194
  const xs = g.s.elems;
3352
3195
  const outs = [];
@@ -3362,7 +3205,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3362
3205
  // list:last
3363
3206
  // true iff $s is a list and $o is the last member of that list.
3364
3207
  // Schema: $s+ list:last $o-
3365
- if (g.p instanceof Iri && g.p.value === LIST_NS + "last") {
3208
+ if (g.p instanceof Iri && g.p.value === LIST_NS + 'last') {
3366
3209
  if (!(g.s instanceof ListTerm)) return [];
3367
3210
  const xs = g.s.elems;
3368
3211
  if (!xs.length) return [];
@@ -3374,7 +3217,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3374
3217
  // list:memberAt
3375
3218
  // true iff $s.1 is a list, $s.2 is a valid index, and $o is the member at that index.
3376
3219
  // Schema: ( $s.1+ $s.2?[*] )+ list:memberAt $o?[*]
3377
- if (g.p instanceof Iri && g.p.value === LIST_NS + "memberAt") {
3220
+ if (g.p instanceof Iri && g.p.value === LIST_NS + 'memberAt') {
3378
3221
  if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
3379
3222
  const [listTerm, indexTerm] = g.s.elems;
3380
3223
  if (!(listTerm instanceof ListTerm)) return [];
@@ -3395,7 +3238,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3395
3238
  // list:remove
3396
3239
  // true iff $s.1 is a list and $o is that list with all occurrences of $s.2 removed.
3397
3240
  // Schema: ( $s.1+ $s.2+ )+ list:remove $o-
3398
- if (g.p instanceof Iri && g.p.value === LIST_NS + "remove") {
3241
+ if (g.p instanceof Iri && g.p.value === LIST_NS + 'remove') {
3399
3242
  if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
3400
3243
  const [listTerm, itemTerm] = g.s.elems;
3401
3244
  if (!(listTerm instanceof ListTerm)) return [];
@@ -3410,7 +3253,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3410
3253
  }
3411
3254
 
3412
3255
  // list:member
3413
- if (g.p instanceof Iri && g.p.value === LIST_NS + "member") {
3256
+ if (g.p instanceof Iri && g.p.value === LIST_NS + 'member') {
3414
3257
  if (!(g.s instanceof ListTerm)) return [];
3415
3258
  const outs = [];
3416
3259
  for (const x of g.s.elems) {
@@ -3421,7 +3264,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3421
3264
  }
3422
3265
 
3423
3266
  // list:in
3424
- if (g.p instanceof Iri && g.p.value === LIST_NS + "in") {
3267
+ if (g.p instanceof Iri && g.p.value === LIST_NS + 'in') {
3425
3268
  if (!(g.o instanceof ListTerm)) return [];
3426
3269
  const outs = [];
3427
3270
  for (const x of g.o.elems) {
@@ -3432,7 +3275,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3432
3275
  }
3433
3276
 
3434
3277
  // list:length
3435
- if (g.p instanceof Iri && g.p.value === LIST_NS + "length") {
3278
+ if (g.p instanceof Iri && g.p.value === LIST_NS + 'length') {
3436
3279
  if (!(g.s instanceof ListTerm)) return [];
3437
3280
  const nTerm = new Literal(String(g.s.elems.length));
3438
3281
  const s2 = unifyTerm(g.o, nTerm, subst);
@@ -3440,7 +3283,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3440
3283
  }
3441
3284
 
3442
3285
  // list:notMember
3443
- if (g.p instanceof Iri && g.p.value === LIST_NS + "notMember") {
3286
+ if (g.p instanceof Iri && g.p.value === LIST_NS + 'notMember') {
3444
3287
  if (!(g.s instanceof ListTerm)) return [];
3445
3288
  for (const el of g.s.elems) {
3446
3289
  if (unifyTerm(g.o, el, subst) !== null) return [];
@@ -3449,7 +3292,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3449
3292
  }
3450
3293
 
3451
3294
  // list:reverse
3452
- if (g.p instanceof Iri && g.p.value === LIST_NS + "reverse") {
3295
+ if (g.p instanceof Iri && g.p.value === LIST_NS + 'reverse') {
3453
3296
  if (g.s instanceof ListTerm) {
3454
3297
  const rev = [...g.s.elems].reverse();
3455
3298
  const rterm = new ListTerm(rev);
@@ -3466,7 +3309,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3466
3309
  }
3467
3310
 
3468
3311
  // list:sort
3469
- if (g.p instanceof Iri && g.p.value === LIST_NS + "sort") {
3312
+ if (g.p instanceof Iri && g.p.value === LIST_NS + 'sort') {
3470
3313
  function cmpTermForSort(a, b) {
3471
3314
  if (a instanceof Literal && b instanceof Literal) {
3472
3315
  const [lexA] = literalParts(a.value);
@@ -3534,7 +3377,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3534
3377
  }
3535
3378
 
3536
3379
  // list:map
3537
- if (g.p instanceof Iri && g.p.value === LIST_NS + "map") {
3380
+ if (g.p instanceof Iri && g.p.value === LIST_NS + 'map') {
3538
3381
  if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
3539
3382
  const [inputTerm, predTerm] = g.s.elems;
3540
3383
  if (!(inputTerm instanceof ListTerm)) return [];
@@ -3546,16 +3389,9 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3546
3389
 
3547
3390
  const results = [];
3548
3391
  for (const el of inputList) {
3549
- const yvar = new Var("_mapY");
3392
+ const yvar = new Var('_mapY');
3550
3393
  const goal2 = new Triple(el, pred, yvar);
3551
- const sols = evalBuiltin(
3552
- goal2,
3553
- subst,
3554
- facts,
3555
- backRules,
3556
- depth + 1,
3557
- varGen,
3558
- );
3394
+ const sols = evalBuiltin(goal2, subst, facts, backRules, depth + 1, varGen);
3559
3395
  if (!sols.length) return [];
3560
3396
  const yval = applySubstTerm(yvar, sols[0]);
3561
3397
  if (yval instanceof Var) return [];
@@ -3567,7 +3403,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3567
3403
  }
3568
3404
 
3569
3405
  // list:firstRest
3570
- if (g.p instanceof Iri && g.p.value === LIST_NS + "firstRest") {
3406
+ if (g.p instanceof Iri && g.p.value === LIST_NS + 'firstRest') {
3571
3407
  if (g.s instanceof ListTerm) {
3572
3408
  if (!g.s.elems.length) return [];
3573
3409
  const first = g.s.elems[0];
@@ -3605,20 +3441,20 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3605
3441
  // -----------------------------------------------------------------
3606
3442
 
3607
3443
  // log:equalTo
3608
- if (g.p instanceof Iri && g.p.value === LOG_NS + "equalTo") {
3444
+ if (g.p instanceof Iri && g.p.value === LOG_NS + 'equalTo') {
3609
3445
  const s2 = unifyTerm(goal.s, goal.o, subst);
3610
3446
  return s2 !== null ? [s2] : [];
3611
3447
  }
3612
3448
 
3613
3449
  // log:notEqualTo
3614
- if (g.p instanceof Iri && g.p.value === LOG_NS + "notEqualTo") {
3450
+ if (g.p instanceof Iri && g.p.value === LOG_NS + 'notEqualTo') {
3615
3451
  const s2 = unifyTerm(goal.s, goal.o, subst);
3616
3452
  if (s2 !== null) return [];
3617
3453
  return [{ ...subst }];
3618
3454
  }
3619
3455
 
3620
3456
  // log:implies — expose internal forward rules as data
3621
- if (g.p instanceof Iri && g.p.value === LOG_NS + "implies") {
3457
+ if (g.p instanceof Iri && g.p.value === LOG_NS + 'implies') {
3622
3458
  const allFw = backRules.__allForwardRules || [];
3623
3459
  const results = [];
3624
3460
 
@@ -3629,9 +3465,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3629
3465
  const r = standardizeRule(r0, varGen);
3630
3466
 
3631
3467
  const premF = new FormulaTerm(r.premise);
3632
- const concTerm = r0.isFuse
3633
- ? new Literal("false")
3634
- : new FormulaTerm(r.conclusion);
3468
+ const concTerm = r0.isFuse ? new Literal('false') : new FormulaTerm(r.conclusion);
3635
3469
 
3636
3470
  // unify subject with the premise formula
3637
3471
  let s2 = unifyTerm(goal.s, premF, subst);
@@ -3648,7 +3482,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3648
3482
  }
3649
3483
 
3650
3484
  // log:impliedBy — expose internal backward rules as data
3651
- if (g.p instanceof Iri && g.p.value === LOG_NS + "impliedBy") {
3485
+ if (g.p instanceof Iri && g.p.value === LOG_NS + 'impliedBy') {
3652
3486
  const allBw = backRules.__allBackwardRules || backRules;
3653
3487
  const results = [];
3654
3488
 
@@ -3677,39 +3511,23 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3677
3511
  }
3678
3512
 
3679
3513
  // log:notIncludes
3680
- if (g.p instanceof Iri && g.p.value === LOG_NS + "notIncludes") {
3514
+ if (g.p instanceof Iri && g.p.value === LOG_NS + 'notIncludes') {
3681
3515
  if (!(g.o instanceof FormulaTerm)) return [];
3682
3516
  const body = g.o.triples;
3683
3517
  const visited2 = [];
3684
- const sols = proveGoals(
3685
- Array.from(body),
3686
- {},
3687
- facts,
3688
- backRules,
3689
- depth + 1,
3690
- visited2,
3691
- varGen,
3692
- );
3518
+ const sols = proveGoals(Array.from(body), {}, facts, backRules, depth + 1, visited2, varGen);
3693
3519
  if (!sols.length) return [{ ...subst }];
3694
3520
  return [];
3695
3521
  }
3696
3522
 
3697
3523
  // log:collectAllIn
3698
- if (g.p instanceof Iri && g.p.value === LOG_NS + "collectAllIn") {
3524
+ if (g.p instanceof Iri && g.p.value === LOG_NS + 'collectAllIn') {
3699
3525
  if (!(g.s instanceof ListTerm) || g.s.elems.length !== 3) return [];
3700
3526
  const [valueTempl, clauseTerm, listTerm] = g.s.elems;
3701
3527
  if (!(clauseTerm instanceof FormulaTerm)) return [];
3702
3528
  const body = clauseTerm.triples;
3703
3529
  const visited2 = [];
3704
- const sols = proveGoals(
3705
- Array.from(body),
3706
- {},
3707
- facts,
3708
- backRules,
3709
- depth + 1,
3710
- visited2,
3711
- varGen,
3712
- );
3530
+ const sols = proveGoals(Array.from(body), {}, facts, backRules, depth + 1, visited2, varGen);
3713
3531
 
3714
3532
  // Collect one value per *solution*, duplicates allowed
3715
3533
  const collected = [];
@@ -3724,7 +3542,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3724
3542
  }
3725
3543
 
3726
3544
  // log:forAllIn
3727
- if (g.p instanceof Iri && g.p.value === LOG_NS + "forAllIn") {
3545
+ if (g.p instanceof Iri && g.p.value === LOG_NS + 'forAllIn') {
3728
3546
  // Subject: list with two clauses (where-clause, then-clause)
3729
3547
  if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
3730
3548
  const [whereClause, thenClause] = g.s.elems;
@@ -3733,29 +3551,13 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3733
3551
 
3734
3552
  // 1. Find all substitutions that make the first clause true
3735
3553
  const visited1 = [];
3736
- const sols1 = proveGoals(
3737
- Array.from(whereClause.triples),
3738
- {},
3739
- facts,
3740
- backRules,
3741
- depth + 1,
3742
- visited1,
3743
- varGen,
3744
- );
3554
+ const sols1 = proveGoals(Array.from(whereClause.triples), {}, facts, backRules, depth + 1, visited1, varGen);
3745
3555
 
3746
3556
  // 2. For every such substitution, check that the second clause holds too.
3747
3557
  // If there are no matches for the first clause, this is vacuously true.
3748
3558
  for (const s1 of sols1) {
3749
3559
  const visited2 = [];
3750
- const sols2 = proveGoals(
3751
- Array.from(thenClause.triples),
3752
- s1,
3753
- facts,
3754
- backRules,
3755
- depth + 1,
3756
- visited2,
3757
- varGen,
3758
- );
3560
+ const sols2 = proveGoals(Array.from(thenClause.triples), s1, facts, backRules, depth + 1, visited2, varGen);
3759
3561
  // Found a counterexample: whereClause holds but thenClause does not
3760
3562
  if (!sols2.length) return [];
3761
3563
  }
@@ -3765,7 +3567,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3765
3567
  }
3766
3568
 
3767
3569
  // log:skolem
3768
- if (g.p instanceof Iri && g.p.value === LOG_NS + "skolem") {
3570
+ if (g.p instanceof Iri && g.p.value === LOG_NS + 'skolem') {
3769
3571
  // Subject must be ground; commonly a list, but we allow any ground term.
3770
3572
  if (!isGroundTerm(g.s)) return [];
3771
3573
 
@@ -3782,7 +3584,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3782
3584
  }
3783
3585
 
3784
3586
  // log:uri
3785
- if (g.p instanceof Iri && g.p.value === LOG_NS + "uri") {
3587
+ if (g.p instanceof Iri && g.p.value === LOG_NS + 'uri') {
3786
3588
  // Direction 1: subject is an IRI -> object is its string representation
3787
3589
  if (g.s instanceof Iri) {
3788
3590
  const uriStr = g.s.value; // raw IRI string, e.g. "https://www.w3.org"
@@ -3810,7 +3612,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3810
3612
  // -----------------------------------------------------------------
3811
3613
 
3812
3614
  // string:concatenation
3813
- if (g.p instanceof Iri && g.p.value === STRING_NS + "concatenation") {
3615
+ if (g.p instanceof Iri && g.p.value === STRING_NS + 'concatenation') {
3814
3616
  if (!(g.s instanceof ListTerm)) return [];
3815
3617
  const parts = [];
3816
3618
  for (const t of g.s.elems) {
@@ -3818,7 +3620,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3818
3620
  if (sStr === null) return [];
3819
3621
  parts.push(sStr);
3820
3622
  }
3821
- const lit = makeStringLiteral(parts.join(""));
3623
+ const lit = makeStringLiteral(parts.join(''));
3822
3624
 
3823
3625
  if (g.o instanceof Var) {
3824
3626
  const s2 = { ...subst };
@@ -3830,7 +3632,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3830
3632
  }
3831
3633
 
3832
3634
  // string:contains
3833
- if (g.p instanceof Iri && g.p.value === STRING_NS + "contains") {
3635
+ if (g.p instanceof Iri && g.p.value === STRING_NS + 'contains') {
3834
3636
  const sStr = termToJsString(g.s);
3835
3637
  const oStr = termToJsString(g.o);
3836
3638
  if (sStr === null || oStr === null) return [];
@@ -3838,17 +3640,15 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3838
3640
  }
3839
3641
 
3840
3642
  // string:containsIgnoringCase
3841
- if (g.p instanceof Iri && g.p.value === STRING_NS + "containsIgnoringCase") {
3643
+ if (g.p instanceof Iri && g.p.value === STRING_NS + 'containsIgnoringCase') {
3842
3644
  const sStr = termToJsString(g.s);
3843
3645
  const oStr = termToJsString(g.o);
3844
3646
  if (sStr === null || oStr === null) return [];
3845
- return sStr.toLowerCase().includes(oStr.toLowerCase())
3846
- ? [{ ...subst }]
3847
- : [];
3647
+ return sStr.toLowerCase().includes(oStr.toLowerCase()) ? [{ ...subst }] : [];
3848
3648
  }
3849
3649
 
3850
3650
  // string:endsWith
3851
- if (g.p instanceof Iri && g.p.value === STRING_NS + "endsWith") {
3651
+ if (g.p instanceof Iri && g.p.value === STRING_NS + 'endsWith') {
3852
3652
  const sStr = termToJsString(g.s);
3853
3653
  const oStr = termToJsString(g.o);
3854
3654
  if (sStr === null || oStr === null) return [];
@@ -3856,7 +3656,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3856
3656
  }
3857
3657
 
3858
3658
  // string:equalIgnoringCase
3859
- if (g.p instanceof Iri && g.p.value === STRING_NS + "equalIgnoringCase") {
3659
+ if (g.p instanceof Iri && g.p.value === STRING_NS + 'equalIgnoringCase') {
3860
3660
  const sStr = termToJsString(g.s);
3861
3661
  const oStr = termToJsString(g.o);
3862
3662
  if (sStr === null || oStr === null) return [];
@@ -3865,7 +3665,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3865
3665
 
3866
3666
  // string:format
3867
3667
  // (limited: only %s and %% are supported, anything else ⇒ builtin fails)
3868
- if (g.p instanceof Iri && g.p.value === STRING_NS + "format") {
3668
+ if (g.p instanceof Iri && g.p.value === STRING_NS + 'format') {
3869
3669
  if (!(g.s instanceof ListTerm) || g.s.elems.length < 1) return [];
3870
3670
  const fmtStr = termToJsString(g.s.elems[0]);
3871
3671
  if (fmtStr === null) return [];
@@ -3892,7 +3692,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3892
3692
 
3893
3693
  // string:jsonPointer
3894
3694
  // Schema: ( $jsonText $pointer ) string:jsonPointer $value
3895
- if (g.p instanceof Iri && g.p.value === STRING_NS + "jsonPointer") {
3695
+ if (g.p instanceof Iri && g.p.value === STRING_NS + 'jsonPointer') {
3896
3696
  if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
3897
3697
 
3898
3698
  const jsonText = termToJsonText(g.s.elems[0]); // <-- changed
@@ -3907,7 +3707,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3907
3707
  }
3908
3708
 
3909
3709
  // string:greaterThan
3910
- if (g.p instanceof Iri && g.p.value === STRING_NS + "greaterThan") {
3710
+ if (g.p instanceof Iri && g.p.value === STRING_NS + 'greaterThan') {
3911
3711
  const sStr = termToJsString(g.s);
3912
3712
  const oStr = termToJsString(g.o);
3913
3713
  if (sStr === null || oStr === null) return [];
@@ -3915,7 +3715,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3915
3715
  }
3916
3716
 
3917
3717
  // string:lessThan
3918
- if (g.p instanceof Iri && g.p.value === STRING_NS + "lessThan") {
3718
+ if (g.p instanceof Iri && g.p.value === STRING_NS + 'lessThan') {
3919
3719
  const sStr = termToJsString(g.s);
3920
3720
  const oStr = termToJsString(g.o);
3921
3721
  if (sStr === null || oStr === null) return [];
@@ -3923,7 +3723,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3923
3723
  }
3924
3724
 
3925
3725
  // string:matches
3926
- if (g.p instanceof Iri && g.p.value === STRING_NS + "matches") {
3726
+ if (g.p instanceof Iri && g.p.value === STRING_NS + 'matches') {
3927
3727
  const sStr = termToJsString(g.s);
3928
3728
  const pattern = termToJsString(g.o);
3929
3729
  if (sStr === null || pattern === null) return [];
@@ -3938,7 +3738,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3938
3738
  }
3939
3739
 
3940
3740
  // string:notEqualIgnoringCase
3941
- if (g.p instanceof Iri && g.p.value === STRING_NS + "notEqualIgnoringCase") {
3741
+ if (g.p instanceof Iri && g.p.value === STRING_NS + 'notEqualIgnoringCase') {
3942
3742
  const sStr = termToJsString(g.s);
3943
3743
  const oStr = termToJsString(g.o);
3944
3744
  if (sStr === null || oStr === null) return [];
@@ -3946,7 +3746,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3946
3746
  }
3947
3747
 
3948
3748
  // string:notGreaterThan (≤ in Unicode code order)
3949
- if (g.p instanceof Iri && g.p.value === STRING_NS + "notGreaterThan") {
3749
+ if (g.p instanceof Iri && g.p.value === STRING_NS + 'notGreaterThan') {
3950
3750
  const sStr = termToJsString(g.s);
3951
3751
  const oStr = termToJsString(g.o);
3952
3752
  if (sStr === null || oStr === null) return [];
@@ -3954,7 +3754,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3954
3754
  }
3955
3755
 
3956
3756
  // string:notLessThan (≥ in Unicode code order)
3957
- if (g.p instanceof Iri && g.p.value === STRING_NS + "notLessThan") {
3757
+ if (g.p instanceof Iri && g.p.value === STRING_NS + 'notLessThan') {
3958
3758
  const sStr = termToJsString(g.s);
3959
3759
  const oStr = termToJsString(g.o);
3960
3760
  if (sStr === null || oStr === null) return [];
@@ -3962,7 +3762,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3962
3762
  }
3963
3763
 
3964
3764
  // string:notMatches
3965
- if (g.p instanceof Iri && g.p.value === STRING_NS + "notMatches") {
3765
+ if (g.p instanceof Iri && g.p.value === STRING_NS + 'notMatches') {
3966
3766
  const sStr = termToJsString(g.s);
3967
3767
  const pattern = termToJsString(g.o);
3968
3768
  if (sStr === null || pattern === null) return [];
@@ -3976,7 +3776,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3976
3776
  }
3977
3777
 
3978
3778
  // string:replace
3979
- if (g.p instanceof Iri && g.p.value === STRING_NS + "replace") {
3779
+ if (g.p instanceof Iri && g.p.value === STRING_NS + 'replace') {
3980
3780
  if (!(g.s instanceof ListTerm) || g.s.elems.length !== 3) return [];
3981
3781
  const dataStr = termToJsString(g.s.elems[0]);
3982
3782
  const searchStr = termToJsString(g.s.elems[1]);
@@ -3986,7 +3786,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3986
3786
  let re;
3987
3787
  try {
3988
3788
  // Global replacement
3989
- re = new RegExp(searchStr, "g");
3789
+ re = new RegExp(searchStr, 'g');
3990
3790
  } catch (e) {
3991
3791
  return [];
3992
3792
  }
@@ -4004,7 +3804,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
4004
3804
  }
4005
3805
 
4006
3806
  // string:scrape
4007
- if (g.p instanceof Iri && g.p.value === STRING_NS + "scrape") {
3807
+ if (g.p instanceof Iri && g.p.value === STRING_NS + 'scrape') {
4008
3808
  if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
4009
3809
  const dataStr = termToJsString(g.s.elems[0]);
4010
3810
  const pattern = termToJsString(g.s.elems[1]);
@@ -4033,7 +3833,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
4033
3833
  }
4034
3834
 
4035
3835
  // string:startsWith
4036
- if (g.p instanceof Iri && g.p.value === STRING_NS + "startsWith") {
3836
+ if (g.p instanceof Iri && g.p.value === STRING_NS + 'startsWith') {
4037
3837
  const sStr = termToJsString(g.s);
4038
3838
  const oStr = termToJsString(g.o);
4039
3839
  if (sStr === null || oStr === null) return [];
@@ -4049,7 +3849,7 @@ function isBuiltinPred(p) {
4049
3849
  const v = p.value;
4050
3850
 
4051
3851
  // Treat RDF Collections as list-term builtins too.
4052
- if (v === RDF_NS + "first" || v === RDF_NS + "rest") {
3852
+ if (v === RDF_NS + 'first' || v === RDF_NS + 'rest') {
4053
3853
  return true;
4054
3854
  }
4055
3855
 
@@ -4092,14 +3892,7 @@ function standardizeRule(rule, gen) {
4092
3892
  }
4093
3893
  if (t instanceof FormulaTerm) {
4094
3894
  return new FormulaTerm(
4095
- t.triples.map(
4096
- (tr) =>
4097
- new Triple(
4098
- renameTerm(tr.s, vmap, genArr),
4099
- renameTerm(tr.p, vmap, genArr),
4100
- renameTerm(tr.o, vmap, genArr),
4101
- ),
4102
- ),
3895
+ t.triples.map((tr) => new Triple(renameTerm(tr.s, vmap, genArr), renameTerm(tr.p, vmap, genArr), renameTerm(tr.o, vmap, genArr))),
4103
3896
  );
4104
3897
  }
4105
3898
  return t;
@@ -4107,28 +3900,12 @@ function standardizeRule(rule, gen) {
4107
3900
 
4108
3901
  const vmap2 = {};
4109
3902
  const premise = rule.premise.map(
4110
- (tr) =>
4111
- new Triple(
4112
- renameTerm(tr.s, vmap2, gen),
4113
- renameTerm(tr.p, vmap2, gen),
4114
- renameTerm(tr.o, vmap2, gen),
4115
- ),
3903
+ (tr) => new Triple(renameTerm(tr.s, vmap2, gen), renameTerm(tr.p, vmap2, gen), renameTerm(tr.o, vmap2, gen)),
4116
3904
  );
4117
3905
  const conclusion = rule.conclusion.map(
4118
- (tr) =>
4119
- new Triple(
4120
- renameTerm(tr.s, vmap2, gen),
4121
- renameTerm(tr.p, vmap2, gen),
4122
- renameTerm(tr.o, vmap2, gen),
4123
- ),
4124
- );
4125
- return new Rule(
4126
- premise,
4127
- conclusion,
4128
- rule.isForward,
4129
- rule.isFuse,
4130
- rule.headBlankLabels,
3906
+ (tr) => new Triple(renameTerm(tr.s, vmap2, gen), renameTerm(tr.p, vmap2, gen), renameTerm(tr.o, vmap2, gen)),
4131
3907
  );
3908
+ return new Rule(premise, conclusion, rule.isForward, rule.isFuse, rule.headBlankLabels);
4132
3909
  }
4133
3910
 
4134
3911
  function listHasTriple(list, tr) {
@@ -4268,14 +4045,7 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
4268
4045
 
4269
4046
  // 1) Builtins
4270
4047
  if (isBuiltinPred(goal0.p)) {
4271
- const deltas = evalBuiltin(
4272
- goal0,
4273
- {},
4274
- facts,
4275
- backRules,
4276
- state.depth,
4277
- varGen,
4278
- );
4048
+ const deltas = evalBuiltin(goal0, {}, facts, backRules, state.depth, varGen);
4279
4049
  for (const delta of deltas) {
4280
4050
  const composed = composeSubst(state.subst, delta);
4281
4051
  if (composed === null) continue;
@@ -4283,12 +4053,7 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
4283
4053
  if (!restGoals.length) {
4284
4054
  results.push(gcCompactForGoals(composed, [], answerVars));
4285
4055
  } else {
4286
- const nextSubst = maybeCompactSubst(
4287
- composed,
4288
- restGoals,
4289
- answerVars,
4290
- state.depth + 1,
4291
- );
4056
+ const nextSubst = maybeCompactSubst(composed, restGoals, answerVars, state.depth + 1);
4292
4057
  stack.push({
4293
4058
  goals: restGoals,
4294
4059
  subst: nextSubst,
@@ -4317,12 +4082,7 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
4317
4082
  if (!restGoals.length) {
4318
4083
  results.push(gcCompactForGoals(composed, [], answerVars));
4319
4084
  } else {
4320
- const nextSubst = maybeCompactSubst(
4321
- composed,
4322
- restGoals,
4323
- answerVars,
4324
- state.depth + 1,
4325
- );
4085
+ const nextSubst = maybeCompactSubst(composed, restGoals, answerVars, state.depth + 1);
4326
4086
  stack.push({
4327
4087
  goals: restGoals,
4328
4088
  subst: nextSubst,
@@ -4343,12 +4103,7 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
4343
4103
  if (!restGoals.length) {
4344
4104
  results.push(gcCompactForGoals(composed, [], answerVars));
4345
4105
  } else {
4346
- const nextSubst = maybeCompactSubst(
4347
- composed,
4348
- restGoals,
4349
- answerVars,
4350
- state.depth + 1,
4351
- );
4106
+ const nextSubst = maybeCompactSubst(composed, restGoals, answerVars, state.depth + 1);
4352
4107
  stack.push({
4353
4108
  goals: restGoals,
4354
4109
  subst: nextSubst,
@@ -4362,16 +4117,13 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
4362
4117
  // 4) Backward rules (indexed by head predicate)
4363
4118
  if (goal0.p instanceof Iri) {
4364
4119
  ensureBackRuleIndexes(backRules);
4365
- const candRules = (
4366
- backRules.__byHeadPred.get(goal0.p.value) || []
4367
- ).concat(backRules.__wildHeadPred);
4120
+ const candRules = (backRules.__byHeadPred.get(goal0.p.value) || []).concat(backRules.__wildHeadPred);
4368
4121
 
4369
4122
  for (const r of candRules) {
4370
4123
  if (r.conclusion.length !== 1) continue;
4371
4124
 
4372
4125
  const rawHead = r.conclusion[0];
4373
- if (rawHead.p instanceof Iri && rawHead.p.value !== goal0.p.value)
4374
- continue;
4126
+ if (rawHead.p instanceof Iri && rawHead.p.value !== goal0.p.value) continue;
4375
4127
 
4376
4128
  const rStd = standardizeRule(r, varGen);
4377
4129
  const head = rStd.conclusion[0];
@@ -4383,12 +4135,7 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
4383
4135
  if (composed === null) continue;
4384
4136
 
4385
4137
  const newGoals = body.concat(restGoals);
4386
- const nextSubst = maybeCompactSubst(
4387
- composed,
4388
- newGoals,
4389
- answerVars,
4390
- state.depth + 1,
4391
- );
4138
+ const nextSubst = maybeCompactSubst(composed, newGoals, answerVars, state.depth + 1);
4392
4139
  stack.push({
4393
4140
  goals: newGoals,
4394
4141
  subst: nextSubst,
@@ -4427,53 +4174,31 @@ function forwardChain(facts, forwardRules, backRules) {
4427
4174
  const empty = {};
4428
4175
  const visited = [];
4429
4176
 
4430
- const sols = proveGoals(
4431
- r.premise.slice(),
4432
- empty,
4433
- facts,
4434
- backRules,
4435
- 0,
4436
- visited,
4437
- varGen,
4438
- );
4177
+ const sols = proveGoals(r.premise.slice(), empty, facts, backRules, 0, visited, varGen);
4439
4178
 
4440
4179
  // Inference fuse
4441
4180
  if (r.isFuse && sols.length) {
4442
- console.log(
4443
- "# Inference fuse triggered: a { ... } => false. rule fired.",
4444
- );
4181
+ console.log('# Inference fuse triggered: a { ... } => false. rule fired.');
4445
4182
  process.exit(2);
4446
4183
  }
4447
4184
 
4448
4185
  for (const s of sols) {
4449
- const instantiatedPremises = r.premise.map((b) =>
4450
- applySubstTriple(b, s),
4451
- );
4186
+ const instantiatedPremises = r.premise.map((b) => applySubstTriple(b, s));
4452
4187
 
4453
4188
  for (const cpat of r.conclusion) {
4454
4189
  const instantiated = applySubstTriple(cpat, s);
4455
4190
 
4456
4191
  const isFwRuleTriple =
4457
4192
  isLogImplies(instantiated.p) &&
4458
- ((instantiated.s instanceof FormulaTerm &&
4459
- instantiated.o instanceof FormulaTerm) ||
4460
- (instantiated.s instanceof Literal &&
4461
- instantiated.s.value === "true" &&
4462
- instantiated.o instanceof FormulaTerm) ||
4463
- (instantiated.s instanceof FormulaTerm &&
4464
- instantiated.o instanceof Literal &&
4465
- instantiated.o.value === "true"));
4193
+ ((instantiated.s instanceof FormulaTerm && instantiated.o instanceof FormulaTerm) ||
4194
+ (instantiated.s instanceof Literal && instantiated.s.value === 'true' && instantiated.o instanceof FormulaTerm) ||
4195
+ (instantiated.s instanceof FormulaTerm && instantiated.o instanceof Literal && instantiated.o.value === 'true'));
4466
4196
 
4467
4197
  const isBwRuleTriple =
4468
4198
  isLogImpliedBy(instantiated.p) &&
4469
- ((instantiated.s instanceof FormulaTerm &&
4470
- instantiated.o instanceof FormulaTerm) ||
4471
- (instantiated.s instanceof FormulaTerm &&
4472
- instantiated.o instanceof Literal &&
4473
- instantiated.o.value === "true") ||
4474
- (instantiated.s instanceof Literal &&
4475
- instantiated.s.value === "true" &&
4476
- instantiated.o instanceof FormulaTerm));
4199
+ ((instantiated.s instanceof FormulaTerm && instantiated.o instanceof FormulaTerm) ||
4200
+ (instantiated.s instanceof FormulaTerm && instantiated.o instanceof Literal && instantiated.o.value === 'true') ||
4201
+ (instantiated.s instanceof Literal && instantiated.s.value === 'true' && instantiated.o instanceof FormulaTerm));
4477
4202
 
4478
4203
  if (isFwRuleTriple || isBwRuleTriple) {
4479
4204
  if (!hasFactIndexed(facts, instantiated)) {
@@ -4491,16 +4216,14 @@ function forwardChain(facts, forwardRules, backRules) {
4491
4216
  const left =
4492
4217
  instantiated.s instanceof FormulaTerm
4493
4218
  ? instantiated.s.triples
4494
- : instantiated.s instanceof Literal &&
4495
- instantiated.s.value === "true"
4219
+ : instantiated.s instanceof Literal && instantiated.s.value === 'true'
4496
4220
  ? []
4497
4221
  : null;
4498
4222
 
4499
4223
  const right =
4500
4224
  instantiated.o instanceof FormulaTerm
4501
4225
  ? instantiated.o.triples
4502
- : instantiated.o instanceof Literal &&
4503
- instantiated.o.value === "true"
4226
+ : instantiated.o instanceof Literal && instantiated.o.value === 'true'
4504
4227
  ? []
4505
4228
  : null;
4506
4229
 
@@ -4510,13 +4233,7 @@ function forwardChain(facts, forwardRules, backRules) {
4510
4233
  const premise = reorderPremiseForConstraints(premise0);
4511
4234
 
4512
4235
  const headBlankLabels = collectBlankLabelsInTriples(conclusion);
4513
- const newRule = new Rule(
4514
- premise,
4515
- conclusion,
4516
- true,
4517
- false,
4518
- headBlankLabels,
4519
- );
4236
+ const newRule = new Rule(premise, conclusion, true, false, headBlankLabels);
4520
4237
 
4521
4238
  const already = forwardRules.some(
4522
4239
  (rr) =>
@@ -4530,13 +4247,7 @@ function forwardChain(facts, forwardRules, backRules) {
4530
4247
  const [premise, conclusion] = liftBlankRuleVars(right, left);
4531
4248
 
4532
4249
  const headBlankLabels = collectBlankLabelsInTriples(conclusion);
4533
- const newRule = new Rule(
4534
- premise,
4535
- conclusion,
4536
- false,
4537
- false,
4538
- headBlankLabels,
4539
- );
4250
+ const newRule = new Rule(premise, conclusion, false, false, headBlankLabels);
4540
4251
 
4541
4252
  const already = backRules.some(
4542
4253
  (rr) =>
@@ -4557,12 +4268,7 @@ function forwardChain(facts, forwardRules, backRules) {
4557
4268
 
4558
4269
  // Only skolemize blank nodes that occur explicitly in the rule head
4559
4270
  const skMap = {};
4560
- const inst = skolemizeTripleForHeadBlanks(
4561
- instantiated,
4562
- r.headBlankLabels,
4563
- skMap,
4564
- skCounter,
4565
- );
4271
+ const inst = skolemizeTripleForHeadBlanks(instantiated, r.headBlankLabels, skMap, skCounter);
4566
4272
 
4567
4273
  if (!isGroundTriple(inst)) continue;
4568
4274
  if (hasFactIndexed(facts, inst)) continue;
@@ -4570,9 +4276,7 @@ function forwardChain(facts, forwardRules, backRules) {
4570
4276
  factList.push(inst);
4571
4277
  pushFactIndexed(facts, inst);
4572
4278
 
4573
- derivedForward.push(
4574
- new DerivedFact(inst, r, instantiatedPremises.slice(), { ...s }),
4575
- );
4279
+ derivedForward.push(new DerivedFact(inst, r, instantiatedPremises.slice(), { ...s }));
4576
4280
  changed = true;
4577
4281
  }
4578
4282
  }
@@ -4593,19 +4297,19 @@ function termToN3(t, pref) {
4593
4297
  const i = t.value;
4594
4298
  const q = pref.shrinkIri(i);
4595
4299
  if (q !== null) return q;
4596
- if (i.startsWith("_:")) return i;
4300
+ if (i.startsWith('_:')) return i;
4597
4301
  return `<${i}>`;
4598
4302
  }
4599
4303
  if (t instanceof Literal) {
4600
4304
  const [lex, dt] = literalParts(t.value);
4601
4305
 
4602
4306
  // Pretty-print xsd:boolean as bare true/false
4603
- if (dt === XSD_NS + "boolean") {
4307
+ if (dt === XSD_NS + 'boolean') {
4604
4308
  const v = stripQuotes(lex);
4605
- if (v === "true" || v === "false") return v;
4309
+ if (v === 'true' || v === 'false') return v;
4606
4310
  // optional: normalize 1/0 too
4607
- if (v === "1") return "true";
4608
- if (v === "0") return "false";
4311
+ if (v === '1') return 'true';
4312
+ if (v === '0') return 'false';
4609
4313
  }
4610
4314
 
4611
4315
  if (!dt) return t.value; // keep numbers, booleans, lang-tagged strings, etc.
@@ -4617,20 +4321,20 @@ function termToN3(t, pref) {
4617
4321
  if (t instanceof Blank) return t.label;
4618
4322
  if (t instanceof ListTerm) {
4619
4323
  const inside = t.elems.map((e) => termToN3(e, pref));
4620
- return "(" + inside.join(" ") + ")";
4324
+ return '(' + inside.join(' ') + ')';
4621
4325
  }
4622
4326
  if (t instanceof OpenListTerm) {
4623
4327
  const inside = t.prefix.map((e) => termToN3(e, pref));
4624
- inside.push("?" + t.tailVar);
4625
- return "(" + inside.join(" ") + ")";
4328
+ inside.push('?' + t.tailVar);
4329
+ return '(' + inside.join(' ') + ')';
4626
4330
  }
4627
4331
  if (t instanceof FormulaTerm) {
4628
- let s = "{\n";
4332
+ let s = '{\n';
4629
4333
  for (const tr of t.triples) {
4630
4334
  let line = tripleToN3(tr, pref).trimEnd();
4631
- if (line) s += " " + line + "\n";
4335
+ if (line) s += ' ' + line + '\n';
4632
4336
  }
4633
- s += "}";
4337
+ s += '}';
4634
4338
  return s;
4635
4339
  }
4636
4340
  return JSON.stringify(t);
@@ -4651,74 +4355,62 @@ function tripleToN3(tr, prefixes) {
4651
4355
  }
4652
4356
 
4653
4357
  const s = termToN3(tr.s, prefixes);
4654
- const p = isRdfTypePred(tr.p)
4655
- ? "a"
4656
- : isOwlSameAsPred(tr.p)
4657
- ? "="
4658
- : termToN3(tr.p, prefixes);
4358
+ const p = isRdfTypePred(tr.p) ? 'a' : isOwlSameAsPred(tr.p) ? '=' : termToN3(tr.p, prefixes);
4659
4359
  const o = termToN3(tr.o, prefixes);
4660
4360
 
4661
4361
  return `${s} ${p} ${o} .`;
4662
4362
  }
4663
4363
 
4664
4364
  function printExplanation(df, prefixes) {
4665
- console.log(
4666
- "# ----------------------------------------------------------------------",
4667
- );
4668
- console.log("# Proof for derived triple:");
4365
+ console.log('# ----------------------------------------------------------------------');
4366
+ console.log('# Proof for derived triple:');
4669
4367
 
4670
4368
  // Fact line(s), indented 2 spaces after '# '
4671
4369
  for (const line of tripleToN3(df.fact, prefixes).split(/\r?\n/)) {
4672
- const stripped = line.replace(/\s+$/, "");
4370
+ const stripped = line.replace(/\s+$/, '');
4673
4371
  if (stripped) {
4674
- console.log("# " + stripped);
4372
+ console.log('# ' + stripped);
4675
4373
  }
4676
4374
  }
4677
4375
 
4678
4376
  if (!df.premises.length) {
4679
- console.log(
4680
- "# This triple is the head of a forward rule with an empty premise,",
4681
- );
4682
- console.log(
4683
- "# so it holds unconditionally whenever the program is loaded.",
4684
- );
4377
+ console.log('# This triple is the head of a forward rule with an empty premise,');
4378
+ console.log('# so it holds unconditionally whenever the program is loaded.');
4685
4379
  } else {
4686
- console.log(
4687
- "# It holds because the following instance of the rule body is provable:",
4688
- );
4380
+ console.log('# It holds because the following instance of the rule body is provable:');
4689
4381
 
4690
4382
  // Premises, also indented 2 spaces after '# '
4691
4383
  for (const prem of df.premises) {
4692
4384
  for (const line of tripleToN3(prem, prefixes).split(/\r?\n/)) {
4693
- const stripped = line.replace(/\s+$/, "");
4385
+ const stripped = line.replace(/\s+$/, '');
4694
4386
  if (stripped) {
4695
- console.log("# " + stripped);
4387
+ console.log('# ' + stripped);
4696
4388
  }
4697
4389
  }
4698
4390
  }
4699
4391
 
4700
- console.log("# via the schematic forward rule:");
4392
+ console.log('# via the schematic forward rule:');
4701
4393
 
4702
4394
  // Rule pretty-printed
4703
- console.log("# {");
4395
+ console.log('# {');
4704
4396
  for (const tr of df.rule.premise) {
4705
4397
  for (const line of tripleToN3(tr, prefixes).split(/\r?\n/)) {
4706
- const stripped = line.replace(/\s+$/, "");
4398
+ const stripped = line.replace(/\s+$/, '');
4707
4399
  if (stripped) {
4708
- console.log("# " + stripped);
4400
+ console.log('# ' + stripped);
4709
4401
  }
4710
4402
  }
4711
4403
  }
4712
- console.log("# } => {");
4404
+ console.log('# } => {');
4713
4405
  for (const tr of df.rule.conclusion) {
4714
4406
  for (const line of tripleToN3(tr, prefixes).split(/\r?\n/)) {
4715
- const stripped = line.replace(/\s+$/, "");
4407
+ const stripped = line.replace(/\s+$/, '');
4716
4408
  if (stripped) {
4717
- console.log("# " + stripped);
4409
+ console.log('# ' + stripped);
4718
4410
  }
4719
4411
  }
4720
4412
  }
4721
- console.log("# } .");
4413
+ console.log('# } .');
4722
4414
  }
4723
4415
 
4724
4416
  // Substitution block
@@ -4728,7 +4420,7 @@ function printExplanation(df, prefixes) {
4728
4420
  .sort();
4729
4421
 
4730
4422
  if (visibleNames.length) {
4731
- console.log("# with substitution (on rule variables):");
4423
+ console.log('# with substitution (on rule variables):');
4732
4424
  for (const v of visibleNames) {
4733
4425
  const fullTerm = applySubstTerm(new Var(v), df.subst);
4734
4426
  const rendered = termToN3(fullTerm, prefixes);
@@ -4736,37 +4428,33 @@ function printExplanation(df, prefixes) {
4736
4428
 
4737
4429
  if (lines.length === 1) {
4738
4430
  // single-line term
4739
- const stripped = lines[0].replace(/\s+$/, "");
4431
+ const stripped = lines[0].replace(/\s+$/, '');
4740
4432
  if (stripped) {
4741
- console.log("# ?" + v + " = " + stripped);
4433
+ console.log('# ?' + v + ' = ' + stripped);
4742
4434
  }
4743
4435
  } else {
4744
4436
  // multi-line term (e.g. a formula)
4745
4437
  const first = lines[0].trimEnd(); // usually "{"
4746
4438
  if (first) {
4747
- console.log("# ?" + v + " = " + first);
4439
+ console.log('# ?' + v + ' = ' + first);
4748
4440
  }
4749
4441
  for (let i = 1; i < lines.length; i++) {
4750
4442
  const stripped = lines[i].trim();
4751
4443
  if (!stripped) continue;
4752
4444
  if (i === lines.length - 1) {
4753
4445
  // closing brace
4754
- console.log("# " + stripped);
4446
+ console.log('# ' + stripped);
4755
4447
  } else {
4756
4448
  // inner triple lines
4757
- console.log("# " + stripped);
4449
+ console.log('# ' + stripped);
4758
4450
  }
4759
4451
  }
4760
4452
  }
4761
4453
  }
4762
4454
  }
4763
4455
 
4764
- console.log(
4765
- "# Therefore the derived triple above is entailed by the rules and facts.",
4766
- );
4767
- console.log(
4768
- "# ----------------------------------------------------------------------\n",
4769
- );
4456
+ console.log('# Therefore the derived triple above is entailed by the rules and facts.');
4457
+ console.log('# ----------------------------------------------------------------------\n');
4770
4458
  }
4771
4459
 
4772
4460
  // ============================================================================
@@ -4776,13 +4464,13 @@ function printExplanation(df, prefixes) {
4776
4464
  // Turn RDF Collections described with rdf:first/rdf:rest (+ rdf:nil) into ListTerm terms.
4777
4465
  // This mutates triples/rules in-place so list:* builtins work on RDF-serialized lists too.
4778
4466
  function materializeRdfLists(triples, forwardRules, backwardRules) {
4779
- const RDF_FIRST = RDF_NS + "first";
4780
- const RDF_REST = RDF_NS + "rest";
4781
- const RDF_NIL = RDF_NS + "nil";
4467
+ const RDF_FIRST = RDF_NS + 'first';
4468
+ const RDF_REST = RDF_NS + 'rest';
4469
+ const RDF_NIL = RDF_NS + 'nil';
4782
4470
 
4783
4471
  function nodeKey(t) {
4784
- if (t instanceof Blank) return "B:" + t.label;
4785
- if (t instanceof Iri) return "I:" + t.value;
4472
+ if (t instanceof Blank) return 'B:' + t.label;
4473
+ if (t instanceof Iri) return 'I:' + t.value;
4786
4474
  return null;
4787
4475
  }
4788
4476
 
@@ -4807,7 +4495,7 @@ function materializeRdfLists(triples, forwardRules, backwardRules) {
4807
4495
  visiting.add(k);
4808
4496
 
4809
4497
  // rdf:nil => ()
4810
- if (k === "I:" + RDF_NIL) {
4498
+ if (k === 'I:' + RDF_NIL) {
4811
4499
  const empty = new ListTerm([]);
4812
4500
  cache.set(k, empty);
4813
4501
  visiting.delete(k);
@@ -4904,7 +4592,7 @@ function materializeRdfLists(triples, forwardRules, backwardRules) {
4904
4592
 
4905
4593
  function localIsoDateTimeString(d) {
4906
4594
  function pad(n, width = 2) {
4907
- return String(n).padStart(width, "0");
4595
+ return String(n).padStart(width, '0');
4908
4596
  }
4909
4597
  const year = d.getFullYear();
4910
4598
  const month = d.getMonth() + 1;
@@ -4914,27 +4602,27 @@ function localIsoDateTimeString(d) {
4914
4602
  const sec = d.getSeconds();
4915
4603
  const ms = d.getMilliseconds();
4916
4604
  const offsetMin = -d.getTimezoneOffset(); // minutes east of UTC
4917
- const sign = offsetMin >= 0 ? "+" : "-";
4605
+ const sign = offsetMin >= 0 ? '+' : '-';
4918
4606
  const abs = Math.abs(offsetMin);
4919
4607
  const oh = Math.floor(abs / 60);
4920
4608
  const om = abs % 60;
4921
- const msPart = ms ? "." + String(ms).padStart(3, "0") : "";
4609
+ const msPart = ms ? '.' + String(ms).padStart(3, '0') : '';
4922
4610
  return (
4923
4611
  pad(year, 4) +
4924
- "-" +
4612
+ '-' +
4925
4613
  pad(month) +
4926
- "-" +
4614
+ '-' +
4927
4615
  pad(day) +
4928
- "T" +
4616
+ 'T' +
4929
4617
  pad(hour) +
4930
- ":" +
4618
+ ':' +
4931
4619
  pad(min) +
4932
- ":" +
4620
+ ':' +
4933
4621
  pad(sec) +
4934
4622
  msPart +
4935
4623
  sign +
4936
4624
  pad(oh) +
4937
- ":" +
4625
+ ':' +
4938
4626
  pad(om)
4939
4627
  );
4940
4628
  }
@@ -4952,33 +4640,31 @@ function main() {
4952
4640
  // --------------------------------------------------------------------------
4953
4641
 
4954
4642
  // --version / -v: print version and exit
4955
- if (argv.includes("--version") || argv.includes("-v")) {
4643
+ if (argv.includes('--version') || argv.includes('-v')) {
4956
4644
  console.log(`eyeling v${version}`);
4957
4645
  process.exit(0);
4958
4646
  }
4959
4647
 
4960
4648
  // --no-proof-comments / -n: disable proof explanations
4961
- if (argv.includes("--no-proof-comments") || argv.includes("-n")) {
4649
+ if (argv.includes('--no-proof-comments') || argv.includes('-n')) {
4962
4650
  proofCommentsEnabled = false;
4963
4651
  }
4964
4652
 
4965
4653
  // --------------------------------------------------------------------------
4966
4654
  // Positional args (the N3 file)
4967
4655
  // --------------------------------------------------------------------------
4968
- const positional = argv.filter((a) => !a.startsWith("-"));
4656
+ const positional = argv.filter((a) => !a.startsWith('-'));
4969
4657
 
4970
4658
  if (positional.length !== 1) {
4971
- console.error(
4972
- "Usage: eyeling.js [--version|-v] [--no-proof-comments|-n] <file.n3>",
4973
- );
4659
+ console.error('Usage: eyeling.js [--version|-v] [--no-proof-comments|-n] <file.n3>');
4974
4660
  process.exit(1);
4975
4661
  }
4976
4662
 
4977
4663
  const path = positional[0];
4978
4664
  let text;
4979
4665
  try {
4980
- const fs = require("fs");
4981
- text = fs.readFileSync(path, { encoding: "utf8" });
4666
+ const fs = require('fs');
4667
+ text = fs.readFileSync(path, { encoding: 'utf8' });
4982
4668
  } catch (e) {
4983
4669
  console.error(`Error reading file ${JSON.stringify(path)}: ${e.message}`);
4984
4670
  process.exit(1);
@@ -4999,7 +4685,7 @@ function main() {
4999
4685
  const usedPrefixes = prefixes.prefixesUsedForOutput(derivedTriples);
5000
4686
 
5001
4687
  for (const [pfx, base] of usedPrefixes) {
5002
- if (pfx === "") console.log(`@prefix : <${base}> .`);
4688
+ if (pfx === '') console.log(`@prefix : <${base}> .`);
5003
4689
  else console.log(`@prefix ${pfx}: <${base}> .`);
5004
4690
  }
5005
4691
  if (derived.length && usedPrefixes.length) console.log();