eyeling 1.6.19 → 1.6.21

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 +191 -54
  2. package/package.json +1 -1
package/eyeling.js CHANGED
@@ -62,9 +62,9 @@ function termToJsonText(t) {
62
62
  function makeRdfJsonLiteral(jsonText) {
63
63
  // Prefer a readable long literal when safe; fall back to short if needed.
64
64
  if (!jsonText.includes('"""')) {
65
- return new Literal('"""' + jsonText + '"""^^<' + RDF_JSON_DT + '>');
65
+ return internLiteral('"""' + jsonText + '"""^^<' + RDF_JSON_DT + '>');
66
66
  }
67
- return new Literal(JSON.stringify(jsonText) + '^^<' + RDF_JSON_DT + '>');
67
+ return internLiteral(JSON.stringify(jsonText) + '^^<' + RDF_JSON_DT + '>');
68
68
  }
69
69
 
70
70
  // For a single reasoning run, this maps a canonical representation
@@ -212,6 +212,37 @@ class Var extends Term {
212
212
  }
213
213
  }
214
214
 
215
+ // ===========================================================================
216
+ // Term interning
217
+ // ===========================================================================
218
+
219
+ // Intern IRIs and literals by their raw lexical string.
220
+ // This reduces allocations when the same terms repeat and can improve performance.
221
+ //
222
+ // NOTE: Terms are treated as immutable. Do NOT mutate .value on interned objects.
223
+ const __iriIntern = new Map();
224
+ const __literalIntern = new Map();
225
+
226
+ /** @param {string} value */
227
+ function internIri(value) {
228
+ let t = __iriIntern.get(value);
229
+ if (!t) {
230
+ t = new Iri(value);
231
+ __iriIntern.set(value, t);
232
+ }
233
+ return t;
234
+ }
235
+
236
+ /** @param {string} value */
237
+ function internLiteral(value) {
238
+ let t = __literalIntern.get(value);
239
+ if (!t) {
240
+ t = new Literal(value);
241
+ __literalIntern.set(value, t);
242
+ }
243
+ return t;
244
+ }
245
+
215
246
  class Blank extends Term {
216
247
  constructor(label) {
217
248
  super();
@@ -528,6 +559,67 @@ function lex(inputText) {
528
559
  continue;
529
560
  }
530
561
 
562
+ // String literal: short '...' or long '''...'''
563
+ if (c === "'") {
564
+ // Long string literal ''' ... '''
565
+ if (peek(1) === "'" && peek(2) === "'") {
566
+ i += 3; // consume opening '''
567
+ const sChars = [];
568
+ let closed = false;
569
+ while (i < n) {
570
+ // closing delimiter?
571
+ if (peek() === "'" && peek(1) === "'" && peek(2) === "'") {
572
+ i += 3; // consume closing '''
573
+ closed = true;
574
+ break;
575
+ }
576
+ let cc = chars[i];
577
+ i++;
578
+ if (cc === '\\') {
579
+ // Preserve escapes verbatim (same behavior as short strings)
580
+ if (i < n) {
581
+ const esc = chars[i];
582
+ i++;
583
+ sChars.push('\\');
584
+ sChars.push(esc);
585
+ }
586
+ continue;
587
+ }
588
+ sChars.push(cc);
589
+ }
590
+ if (!closed) throw new Error("Unterminated long string literal '''...'''");
591
+ const raw = "'''" + sChars.join('') + "'''";
592
+ const decoded = decodeN3StringEscapes(stripQuotes(raw));
593
+ const s = JSON.stringify(decoded); // canonical short quoted form
594
+ tokens.push(new Token('Literal', s));
595
+ continue;
596
+ }
597
+
598
+ // Short string literal ' ... '
599
+ i++; // consume opening '
600
+ const sChars = [];
601
+ while (i < n) {
602
+ let cc = chars[i];
603
+ i++;
604
+ if (cc === '\\') {
605
+ if (i < n) {
606
+ const esc = chars[i];
607
+ i++;
608
+ sChars.push('\\');
609
+ sChars.push(esc);
610
+ }
611
+ continue;
612
+ }
613
+ if (cc === "'") break;
614
+ sChars.push(cc);
615
+ }
616
+ const raw = "'" + sChars.join('') + "'";
617
+ const decoded = decodeN3StringEscapes(stripQuotes(raw));
618
+ const s = JSON.stringify(decoded); // canonical short quoted form
619
+ tokens.push(new Token('Literal', s));
620
+ continue;
621
+ }
622
+
531
623
  // Variable ?name
532
624
  if (c === '?') {
533
625
  i++;
@@ -976,23 +1068,23 @@ class Parser {
976
1068
  const val = tok.value;
977
1069
 
978
1070
  if (typ === 'Equals') {
979
- return new Iri(OWL_NS + 'sameAs');
1071
+ return internIri(OWL_NS + 'sameAs');
980
1072
  }
981
1073
 
982
1074
  if (typ === 'IriRef') {
983
1075
  const base = this.prefixes.map[''] || '';
984
- return new Iri(resolveIriRef(val || '', base));
1076
+ return internIri(resolveIriRef(val || '', base));
985
1077
  }
986
1078
  if (typ === 'Ident') {
987
1079
  const name = val || '';
988
1080
  if (name === 'a') {
989
- return new Iri(RDF_NS + 'type');
1081
+ return internIri(RDF_NS + 'type');
990
1082
  } else if (name.startsWith('_:')) {
991
1083
  return new Blank(name);
992
1084
  } else if (name.includes(':')) {
993
- return new Iri(this.prefixes.expandQName(name));
1085
+ return internIri(this.prefixes.expandQName(name));
994
1086
  } else {
995
- return new Iri(name);
1087
+ return internIri(name);
996
1088
  }
997
1089
  }
998
1090
 
@@ -1030,7 +1122,7 @@ class Parser {
1030
1122
  }
1031
1123
  s = `${s}^^<${dtIri}>`;
1032
1124
  }
1033
- return new Literal(s);
1125
+ return internLiteral(s);
1034
1126
  }
1035
1127
 
1036
1128
  if (typ === 'Var') return new Var(val || '');
@@ -1069,7 +1161,7 @@ class Parser {
1069
1161
  let invert = false;
1070
1162
  if (this.peek().typ === 'Ident' && (this.peek().value || '') === 'a') {
1071
1163
  this.next();
1072
- pred = new Iri(RDF_NS + 'type');
1164
+ pred = internIri(RDF_NS + 'type');
1073
1165
  } else if (this.peek().typ === 'OpPredInvert') {
1074
1166
  this.next(); // consume "<-"
1075
1167
  pred = this.parseTerm();
@@ -1113,7 +1205,7 @@ class Parser {
1113
1205
  if (this.peek().typ === 'OpImplies') {
1114
1206
  this.next();
1115
1207
  const right = this.parseTerm();
1116
- const pred = new Iri(LOG_NS + 'implies');
1208
+ const pred = internIri(LOG_NS + 'implies');
1117
1209
  triples.push(new Triple(left, pred, right));
1118
1210
  if (this.peek().typ === 'Dot') this.next();
1119
1211
  else if (this.peek().typ === 'RBrace') {
@@ -1124,7 +1216,7 @@ class Parser {
1124
1216
  } else if (this.peek().typ === 'OpImpliedBy') {
1125
1217
  this.next();
1126
1218
  const right = this.parseTerm();
1127
- const pred = new Iri(LOG_NS + 'impliedBy');
1219
+ const pred = internIri(LOG_NS + 'impliedBy');
1128
1220
  triples.push(new Triple(left, pred, right));
1129
1221
  if (this.peek().typ === 'Dot') this.next();
1130
1222
  else if (this.peek().typ === 'RBrace') {
@@ -1172,7 +1264,7 @@ class Parser {
1172
1264
 
1173
1265
  if (this.peek().typ === 'Ident' && (this.peek().value || '') === 'a') {
1174
1266
  this.next();
1175
- verb = new Iri(RDF_NS + 'type');
1267
+ verb = internIri(RDF_NS + 'type');
1176
1268
  } else if (this.peek().typ === 'OpPredInvert') {
1177
1269
  this.next(); // "<-"
1178
1270
  verb = this.parseTerm();
@@ -1669,13 +1761,18 @@ function tripleFastKey(tr) {
1669
1761
  }
1670
1762
 
1671
1763
  function ensureFactIndexes(facts) {
1672
- if (facts.__byPred && facts.__byPO && facts.__keySet) return;
1764
+ if (facts.__byPred && facts.__byPS && facts.__byPO && facts.__keySet) return;
1673
1765
 
1674
1766
  Object.defineProperty(facts, '__byPred', {
1675
1767
  value: new Map(),
1676
1768
  enumerable: false,
1677
1769
  writable: true,
1678
1770
  });
1771
+ Object.defineProperty(facts, '__byPS', {
1772
+ value: new Map(),
1773
+ enumerable: false,
1774
+ writable: true,
1775
+ });
1679
1776
  Object.defineProperty(facts, '__byPO', {
1680
1777
  value: new Map(),
1681
1778
  enumerable: false,
@@ -1701,6 +1798,21 @@ function indexFact(facts, tr) {
1701
1798
  }
1702
1799
  pb.push(tr);
1703
1800
 
1801
+ const sk = termFastKey(tr.s);
1802
+ if (sk !== null) {
1803
+ let ps = facts.__byPS.get(pk);
1804
+ if (!ps) {
1805
+ ps = new Map();
1806
+ facts.__byPS.set(pk, ps);
1807
+ }
1808
+ let psb = ps.get(sk);
1809
+ if (!psb) {
1810
+ psb = [];
1811
+ ps.set(sk, psb);
1812
+ }
1813
+ psb.push(tr);
1814
+ }
1815
+
1704
1816
  const ok = termFastKey(tr.o);
1705
1817
  if (ok !== null) {
1706
1818
  let po = facts.__byPO.get(pk);
@@ -1727,15 +1839,27 @@ function candidateFacts(facts, goal) {
1727
1839
  if (goal.p instanceof Iri) {
1728
1840
  const pk = goal.p.value;
1729
1841
 
1842
+ const sk = termFastKey(goal.s);
1730
1843
  const ok = termFastKey(goal.o);
1844
+
1845
+ /** @type {Triple[] | null} */
1846
+ let byPS = null;
1847
+ if (sk !== null) {
1848
+ const ps = facts.__byPS.get(pk);
1849
+ if (ps) byPS = ps.get(sk) || null;
1850
+ }
1851
+
1852
+ /** @type {Triple[] | null} */
1853
+ let byPO = null;
1731
1854
  if (ok !== null) {
1732
1855
  const po = facts.__byPO.get(pk);
1733
- if (po) {
1734
- const pob = po.get(ok);
1735
- if (pob) return pob;
1736
- }
1856
+ if (po) byPO = po.get(ok) || null;
1737
1857
  }
1738
1858
 
1859
+ if (byPS && byPO) return byPS.length <= byPO.length ? byPS : byPO;
1860
+ if (byPS) return byPS;
1861
+ if (byPO) return byPO;
1862
+
1739
1863
  return facts.__byPred.get(pk) || [];
1740
1864
  }
1741
1865
 
@@ -2308,11 +2432,16 @@ function normalizeLiteralForFastKey(lit) {
2308
2432
  }
2309
2433
 
2310
2434
  function stripQuotes(lex) {
2311
- if (lex.length >= 6 && lex.startsWith('"""') && lex.endsWith('"""')) {
2312
- return lex.slice(3, -3);
2435
+ if (typeof lex !== 'string') return lex;
2436
+ // Handle both short ('...' / "...") and long ('''...''' / """...""") forms.
2437
+ if (lex.length >= 6) {
2438
+ if (lex.startsWith('"""') && lex.endsWith('"""')) return lex.slice(3, -3);
2439
+ if (lex.startsWith("'''") && lex.endsWith("'''")) return lex.slice(3, -3);
2313
2440
  }
2314
- if (lex.length >= 2 && lex[0] === '"' && lex[lex.length - 1] === '"') {
2315
- return lex.slice(1, -1);
2441
+ if (lex.length >= 2) {
2442
+ const a = lex[0];
2443
+ const b = lex[lex.length - 1];
2444
+ if ((a === '"' && b === '"') || (a === "'" && b === "'")) return lex.slice(1, -1);
2316
2445
  }
2317
2446
  return lex;
2318
2447
  }
@@ -2332,7 +2461,7 @@ function termToJsString(t) {
2332
2461
  function makeStringLiteral(str) {
2333
2462
  // JSON.stringify gives us a valid N3/Turtle-style quoted string
2334
2463
  // (with proper escaping for quotes, backslashes, newlines, …).
2335
- return new Literal(JSON.stringify(str));
2464
+ return internLiteral(JSON.stringify(str));
2336
2465
  }
2337
2466
 
2338
2467
  function termToJsStringDecoded(t) {
@@ -2381,8 +2510,8 @@ function jsonPointerUnescape(seg) {
2381
2510
  function jsonToTerm(v) {
2382
2511
  if (v === null) return makeStringLiteral('null');
2383
2512
  if (typeof v === 'string') return makeStringLiteral(v);
2384
- if (typeof v === 'number') return new Literal(String(v));
2385
- if (typeof v === 'boolean') return new Literal(v ? 'true' : 'false');
2513
+ if (typeof v === 'number') return internLiteral(String(v));
2514
+ if (typeof v === 'boolean') return internLiteral(v ? 'true' : 'false');
2386
2515
  if (Array.isArray(v)) return new ListTerm(v.map(jsonToTerm));
2387
2516
 
2388
2517
  if (v && typeof v === 'object') {
@@ -2565,10 +2694,18 @@ function formatXsdFloatSpecialLex(n) {
2565
2694
  }
2566
2695
 
2567
2696
  function isQuotedLexical(lex) {
2568
- // Note: the lexer stores long strings with literal delimiters: """..."""
2569
- return (
2570
- (lex.length >= 2 && lex[0] === '"' && lex[lex.length - 1] === '"') || (lex.length >= 6 && lex.startsWith('"""') && lex.endsWith('"""'))
2571
- );
2697
+ // Accept both Turtle/N3 quoting styles:
2698
+ // short: "..." or '...'
2699
+ // long: """...""" or '''...'''
2700
+ if (typeof lex !== 'string') return false;
2701
+ const n = lex.length;
2702
+ if (n >= 6 && ((lex.startsWith('"""') && lex.endsWith('"""')) || (lex.startsWith("'''") && lex.endsWith("'''")))) return true;
2703
+ if (n >= 2) {
2704
+ const a = lex[0];
2705
+ const b = lex[n - 1];
2706
+ return (a === '"' && b === '"') || (a === "'" && b === "'");
2707
+ }
2708
+ return false;
2572
2709
  }
2573
2710
 
2574
2711
  function isXsdNumericDatatype(dt) {
@@ -2855,7 +2992,7 @@ function formatDurationLiteralFromSeconds(secs) {
2855
2992
  const neg = secs < 0;
2856
2993
  const days = Math.round(Math.abs(secs) / 86400.0);
2857
2994
  const literalLex = neg ? `"-P${days}D"` : `"P${days}D"`;
2858
- return new Literal(`${literalLex}^^<${XSD_NS}duration>`);
2995
+ return internLiteral(`${literalLex}^^<${XSD_NS}duration>`);
2859
2996
  }
2860
2997
  function numEqualTerm(t, n, eps = 1e-9) {
2861
2998
  const v = parseNum(t);
@@ -2967,21 +3104,21 @@ function commonNumericDatatype(terms, outTerm) {
2967
3104
 
2968
3105
  function makeNumericOutputLiteral(val, dt) {
2969
3106
  if (dt === XSD_INTEGER_DT) {
2970
- if (typeof val === 'bigint') return new Literal(val.toString());
2971
- if (Number.isInteger(val)) return new Literal(String(val));
3107
+ if (typeof val === 'bigint') return internLiteral(val.toString());
3108
+ if (Number.isInteger(val)) return internLiteral(String(val));
2972
3109
  // If a non-integer sneaks in, promote to decimal.
2973
- return new Literal(`"${formatNum(val)}"^^<${XSD_DECIMAL_DT}>`);
3110
+ return internLiteral(`"${formatNum(val)}"^^<${XSD_DECIMAL_DT}>`);
2974
3111
  }
2975
3112
 
2976
3113
  if (dt === XSD_FLOAT_DT || dt === XSD_DOUBLE_DT) {
2977
3114
  const sp = formatXsdFloatSpecialLex(val);
2978
3115
  const lex = sp !== null ? sp : formatNum(val);
2979
- return new Literal(`"${lex}"^^<${dt}>`);
3116
+ return internLiteral(`"${lex}"^^<${dt}>`);
2980
3117
  }
2981
3118
 
2982
3119
  // decimal
2983
3120
  const lex = typeof val === 'bigint' ? val.toString() : formatNum(val);
2984
- return new Literal(`"${lex}"^^<${dt}>`);
3121
+ return internLiteral(`"${lex}"^^<${dt}>`);
2985
3122
  }
2986
3123
 
2987
3124
  function evalUnaryMathRel(g, subst, forwardFn, inverseFn /* may be null */) {
@@ -3229,7 +3366,7 @@ function hashLiteralTerm(t, algo) {
3229
3366
  const input = stripQuotes(lex);
3230
3367
  try {
3231
3368
  const digest = nodeCrypto.createHash(algo).update(input, 'utf8').digest('hex');
3232
- return new Literal(JSON.stringify(digest));
3369
+ return internLiteral(JSON.stringify(digest));
3233
3370
  } catch (e) {
3234
3371
  return null;
3235
3372
  }
@@ -3429,7 +3566,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3429
3566
  if (secs !== null) {
3430
3567
  const outSecs = aDt.getTime() / 1000.0 - secs;
3431
3568
  const lex = utcIsoDateTimeStringFromEpochSeconds(outSecs);
3432
- const lit = new Literal(`"${lex}"^^<${XSD_NS}dateTime>`);
3569
+ const lit = internLiteral(`"${lex}"^^<${XSD_NS}dateTime>`);
3433
3570
  if (g.o instanceof Var) {
3434
3571
  const s2 = { ...subst };
3435
3572
  s2[g.o.name] = lit;
@@ -3445,7 +3582,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3445
3582
  const bi = parseIntLiteral(b0);
3446
3583
  if (ai !== null && bi !== null) {
3447
3584
  const ci = ai - bi;
3448
- const lit = new Literal(ci.toString());
3585
+ const lit = internLiteral(ci.toString());
3449
3586
  if (g.o instanceof Var) {
3450
3587
  const s2 = { ...subst };
3451
3588
  s2[g.o.name] = lit;
@@ -3480,7 +3617,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3480
3617
  }
3481
3618
 
3482
3619
  // Fallback (if you *don’t* have those helpers yet):
3483
- const lit = new Literal(formatNum(c));
3620
+ const lit = internLiteral(formatNum(c));
3484
3621
  if (g.o instanceof Var) {
3485
3622
  const s2 = { ...subst };
3486
3623
  s2[g.o.name] = lit;
@@ -3531,7 +3668,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3531
3668
  if (ai !== null && bi !== null) {
3532
3669
  if (bi === 0n) return [];
3533
3670
  const q = ai / bi; // BigInt division truncates toward zero
3534
- const lit = new Literal(q.toString());
3671
+ const lit = internLiteral(q.toString());
3535
3672
  if (g.o instanceof Var) {
3536
3673
  const s2 = { ...subst };
3537
3674
  s2[g.o.name] = lit;
@@ -3560,7 +3697,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3560
3697
  if (!Number.isInteger(a) || !Number.isInteger(b)) return [];
3561
3698
 
3562
3699
  const q = Math.trunc(a / b);
3563
- const lit = new Literal(String(q));
3700
+ const lit = internLiteral(String(q));
3564
3701
  if (g.o instanceof Var) {
3565
3702
  const s2 = { ...subst };
3566
3703
  s2[g.o.name] = lit;
@@ -3758,7 +3895,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3758
3895
  if (Number.isNaN(a)) return [];
3759
3896
 
3760
3897
  const rVal = Math.round(a); // ties go toward +∞ in JS (e.g., -1.5 -> -1)
3761
- const lit = new Literal(String(rVal)); // integer token
3898
+ const lit = internLiteral(String(rVal)); // integer token
3762
3899
 
3763
3900
  if (g.o instanceof Var) {
3764
3901
  const s2 = { ...subst };
@@ -3786,7 +3923,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3786
3923
 
3787
3924
  if (g.o instanceof Var) {
3788
3925
  const s2 = { ...subst };
3789
- s2[g.o.name] = new Literal(`"${now}"^^<${XSD_NS}dateTime>`);
3926
+ s2[g.o.name] = internLiteral(`"${now}"^^<${XSD_NS}dateTime>`);
3790
3927
  return [s2];
3791
3928
  }
3792
3929
  if (g.o instanceof Literal) {
@@ -3848,7 +3985,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3848
3985
  const outs = [];
3849
3986
 
3850
3987
  for (let i = 0; i < xs.length; i++) {
3851
- const idxLit = new Literal(String(i)); // 0-based
3988
+ const idxLit = internLiteral(String(i)); // 0-based
3852
3989
  const val = xs[i];
3853
3990
 
3854
3991
  // Fast path: object is exactly a 2-element list (idx, value)
@@ -3904,7 +4041,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3904
4041
  const outs = [];
3905
4042
 
3906
4043
  for (let i = 0; i < xs.length; i++) {
3907
- const idxLit = new Literal(String(i)); // index starts at 0
4044
+ const idxLit = internLiteral(String(i)); // index starts at 0
3908
4045
 
3909
4046
  // --- index side: strict if ground, otherwise unify/bind
3910
4047
  let s1 = null;
@@ -3980,7 +4117,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3980
4117
  // list:length (strict: do not accept integer<->decimal matches for a ground object)
3981
4118
  if (pv === LIST_NS + 'length') {
3982
4119
  if (!(g.s instanceof ListTerm)) return [];
3983
- const nTerm = new Literal(String(g.s.elems.length));
4120
+ const nTerm = internLiteral(String(g.s.elems.length));
3984
4121
 
3985
4122
  const o2 = applySubstTerm(g.o, subst);
3986
4123
  if (isGroundTerm(o2)) {
@@ -4092,7 +4229,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
4092
4229
  if (!(inputTerm instanceof ListTerm)) return [];
4093
4230
  const inputList = inputTerm.elems;
4094
4231
  if (!(predTerm instanceof Iri)) return [];
4095
- const pred = new Iri(predTerm.value);
4232
+ const pred = internIri(predTerm.value);
4096
4233
  if (!isBuiltinPred(pred)) return [];
4097
4234
  if (!inputList.every((e) => isGroundTerm(e))) return [];
4098
4235
 
@@ -4185,8 +4322,8 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
4185
4322
  }
4186
4323
 
4187
4324
  if (oDt !== null) {
4188
- const strLit = isQuotedLexical(oLex) ? new Literal(oLex) : makeStringLiteral(String(oLex));
4189
- const subjList = new ListTerm([strLit, new Iri(oDt)]);
4325
+ const strLit = isQuotedLexical(oLex) ? internLiteral(oLex) : makeStringLiteral(String(oLex));
4326
+ const subjList = new ListTerm([strLit, internIri(oDt)]);
4190
4327
  const s2 = unifyTerm(goal.s, subjList, subst);
4191
4328
  if (s2 !== null) results.push(s2);
4192
4329
  }
@@ -4205,7 +4342,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
4205
4342
  if (okString) {
4206
4343
  const dtIri = b.value;
4207
4344
  // For xsd:string, prefer the plain string literal form.
4208
- const outLit = dtIri === XSD_NS + 'string' ? new Literal(sLex) : new Literal(`${sLex}^^<${dtIri}>`);
4345
+ const outLit = dtIri === XSD_NS + 'string' ? internLiteral(sLex) : internLiteral(`${sLex}^^<${dtIri}>`);
4209
4346
  const s2 = unifyTerm(goal.o, outLit, subst);
4210
4347
  if (s2 !== null) results.push(s2);
4211
4348
  }
@@ -4241,7 +4378,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
4241
4378
  const tag = extractLangTag(g.o.value);
4242
4379
  if (tag !== null) {
4243
4380
  const [oLex] = literalParts(g.o.value); // strips @lang into lexical part
4244
- const strLit = isQuotedLexical(oLex) ? new Literal(oLex) : makeStringLiteral(String(oLex));
4381
+ const strLit = isQuotedLexical(oLex) ? internLiteral(oLex) : makeStringLiteral(String(oLex));
4245
4382
  const langLit = makeStringLiteral(tag);
4246
4383
  const subjList = new ListTerm([strLit, langLit]);
4247
4384
  const s2 = unifyTerm(goal.s, subjList, subst);
@@ -4261,7 +4398,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
4261
4398
  if (okString && okLang) {
4262
4399
  const tag = stripQuotes(langLex);
4263
4400
  if (LANG_RE.test(tag)) {
4264
- const outLit = new Literal(`${sLex}@${tag}`);
4401
+ const outLit = internLiteral(`${sLex}@${tag}`);
4265
4402
  const s2 = unifyTerm(goal.o, outLit, subst);
4266
4403
  if (s2 !== null) results.push(s2);
4267
4404
  }
@@ -4283,7 +4420,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
4283
4420
  const r = standardizeRule(r0, varGen);
4284
4421
 
4285
4422
  const premF = new FormulaTerm(r.premise);
4286
- const concTerm = r0.isFuse ? new Literal('false') : new FormulaTerm(r.conclusion);
4423
+ const concTerm = r0.isFuse ? internLiteral('false') : new FormulaTerm(r.conclusion);
4287
4424
 
4288
4425
  // unify subject with the premise formula
4289
4426
  let s2 = unifyTerm(goal.s, premF, subst);
@@ -4420,7 +4557,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
4420
4557
  let iri = skolemCache.get(key);
4421
4558
  if (!iri) {
4422
4559
  const id = deterministicSkolemIdFromKey(key);
4423
- iri = new Iri(SKOLEM_NS + id);
4560
+ iri = internIri(SKOLEM_NS + id);
4424
4561
  skolemCache.set(key, iri);
4425
4562
  }
4426
4563
 
@@ -4442,7 +4579,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
4442
4579
  if (g.o instanceof Literal) {
4443
4580
  const uriStr = termToJsString(g.o); // JS string from the literal
4444
4581
  if (uriStr === null) return [];
4445
- const iri = new Iri(uriStr);
4582
+ const iri = internIri(uriStr);
4446
4583
  const s2 = unifyTerm(goal.s, iri, subst);
4447
4584
  return s2 !== null ? [s2] : [];
4448
4585
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.6.19",
3
+ "version": "1.6.21",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [