eyeling 1.6.7 → 1.6.9

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 +119 -27
  2. package/package.json +1 -1
package/eyeling.js CHANGED
@@ -1285,20 +1285,6 @@ function termsEqual(a, b) {
1285
1285
  const bn = bi.kind === 'bigint' ? Number(bi.value) : bi.value;
1286
1286
  return !Number.isNaN(an) && !Number.isNaN(bn) && an === bn;
1287
1287
  }
1288
-
1289
- // integer <-> decimal: allow exact equality (but NOT with double)
1290
- const intDt = XSD_NS + 'integer';
1291
- const decDt = XSD_NS + 'decimal';
1292
- if ((ai.dt === intDt && bi.dt === decDt) || (ai.dt === decDt && bi.dt === intDt)) {
1293
- const intInfo = ai.dt === intDt ? ai : bi;
1294
- const decInfo = ai.dt === decDt ? ai : bi;
1295
-
1296
- const dec = parseXsdDecimalToBigIntScale(decInfo.lexStr);
1297
- if (dec) {
1298
- const scaledInt = intInfo.value * pow10n(dec.scale);
1299
- return scaledInt === dec.num;
1300
- }
1301
- }
1302
1288
  }
1303
1289
 
1304
1290
  return false;
@@ -2014,14 +2000,97 @@ function unifyTerm(a, b, subst) {
2014
2000
  if (!Number.isNaN(an) && !Number.isNaN(bn) && an === bn) return { ...subst };
2015
2001
  }
2016
2002
  }
2003
+ }
2004
+ }
2005
+
2006
+ // Open list vs concrete list
2007
+ if (a instanceof OpenListTerm && b instanceof ListTerm) {
2008
+ return unifyOpenWithList(a.prefix, a.tailVar, b.elems, subst);
2009
+ }
2010
+ if (a instanceof ListTerm && b instanceof OpenListTerm) {
2011
+ return unifyOpenWithList(b.prefix, b.tailVar, a.elems, subst);
2012
+ }
2013
+
2014
+ // Open list vs open list (same tail var)
2015
+ if (a instanceof OpenListTerm && b instanceof OpenListTerm) {
2016
+ if (a.tailVar !== b.tailVar || a.prefix.length !== b.prefix.length) return null;
2017
+ let s2 = { ...subst };
2018
+ for (let i = 0; i < a.prefix.length; i++) {
2019
+ s2 = unifyTerm(a.prefix[i], b.prefix[i], s2);
2020
+ if (s2 === null) return null;
2021
+ }
2022
+ return s2;
2023
+ }
2024
+
2025
+ // List terms
2026
+ if (a instanceof ListTerm && b instanceof ListTerm) {
2027
+ if (a.elems.length !== b.elems.length) return null;
2028
+ let s2 = { ...subst };
2029
+ for (let i = 0; i < a.elems.length; i++) {
2030
+ s2 = unifyTerm(a.elems[i], b.elems[i], s2);
2031
+ if (s2 === null) return null;
2032
+ }
2033
+ return s2;
2034
+ }
2035
+
2036
+ // Formulas:
2037
+ // 1) If they are alpha-equivalent, succeed without leaking internal bindings.
2038
+ // 2) Otherwise fall back to full unification (may bind vars).
2039
+ if (a instanceof FormulaTerm && b instanceof FormulaTerm) {
2040
+ if (alphaEqFormulaTriples(a.triples, b.triples)) return { ...subst };
2041
+ return unifyFormulaTriples(a.triples, b.triples, subst);
2042
+ }
2043
+ return null;
2044
+ }
2045
+
2046
+ function unifyTermListAppend(a, b, subst) {
2047
+ a = applySubstTerm(a, subst);
2048
+ b = applySubstTerm(b, subst);
2049
+
2050
+ // Variable binding (same as unifyTerm)
2051
+ if (a instanceof Var) {
2052
+ const v = a.name;
2053
+ const t = b;
2054
+ if (t instanceof Var && t.name === v) return { ...subst };
2055
+ if (containsVarTerm(t, v)) return null;
2056
+ const s2 = { ...subst };
2057
+ s2[v] = t;
2058
+ return s2;
2059
+ }
2060
+ if (b instanceof Var) return unifyTermListAppend(b, a, subst);
2061
+
2062
+ // Exact matches
2063
+ if (a instanceof Iri && b instanceof Iri && a.value === b.value) return { ...subst };
2064
+ if (a instanceof Literal && b instanceof Literal && a.value === b.value) return { ...subst };
2065
+ if (a instanceof Blank && b instanceof Blank && a.label === b.label) return { ...subst };
2066
+
2067
+ // Plain string vs xsd:string equivalence
2068
+ if (a instanceof Literal && b instanceof Literal) {
2069
+ if (literalsEquivalentAsXsdString(a.value, b.value)) return { ...subst };
2070
+ }
2071
+
2072
+ // Numeric match: same-dt OR integer<->decimal exact equality (for list:append only)
2073
+ if (a instanceof Literal && b instanceof Literal) {
2074
+ const ai = parseNumericLiteralInfo(a);
2075
+ const bi = parseNumericLiteralInfo(b);
2076
+ if (ai && bi) {
2077
+ // same datatype
2078
+ if (ai.dt === bi.dt) {
2079
+ if (ai.kind === 'bigint' && bi.kind === 'bigint') {
2080
+ if (ai.value === bi.value) return { ...subst };
2081
+ } else {
2082
+ const an = ai.kind === 'bigint' ? Number(ai.value) : ai.value;
2083
+ const bn = bi.kind === 'bigint' ? Number(bi.value) : bi.value;
2084
+ if (!Number.isNaN(an) && !Number.isNaN(bn) && an === bn) return { ...subst };
2085
+ }
2086
+ }
2017
2087
 
2018
- // integer <-> decimal: allow exact equality (but NOT with double)
2088
+ // integer <-> decimal exact equality
2019
2089
  const intDt = XSD_NS + 'integer';
2020
2090
  const decDt = XSD_NS + 'decimal';
2021
2091
  if ((ai.dt === intDt && bi.dt === decDt) || (ai.dt === decDt && bi.dt === intDt)) {
2022
2092
  const intInfo = ai.dt === intDt ? ai : bi;
2023
2093
  const decInfo = ai.dt === decDt ? ai : bi;
2024
-
2025
2094
  const dec = parseXsdDecimalToBigIntScale(decInfo.lexStr);
2026
2095
  if (dec) {
2027
2096
  const scaledInt = intInfo.value * pow10n(dec.scale);
@@ -2039,12 +2108,12 @@ function unifyTerm(a, b, subst) {
2039
2108
  return unifyOpenWithList(b.prefix, b.tailVar, a.elems, subst);
2040
2109
  }
2041
2110
 
2042
- // Open list vs open list (same tail var)
2111
+ // Open list vs open list
2043
2112
  if (a instanceof OpenListTerm && b instanceof OpenListTerm) {
2044
2113
  if (a.tailVar !== b.tailVar || a.prefix.length !== b.prefix.length) return null;
2045
2114
  let s2 = { ...subst };
2046
2115
  for (let i = 0; i < a.prefix.length; i++) {
2047
- s2 = unifyTerm(a.prefix[i], b.prefix[i], s2);
2116
+ s2 = unifyTermListAppend(a.prefix[i], b.prefix[i], s2);
2048
2117
  if (s2 === null) return null;
2049
2118
  }
2050
2119
  return s2;
@@ -2055,19 +2124,18 @@ function unifyTerm(a, b, subst) {
2055
2124
  if (a.elems.length !== b.elems.length) return null;
2056
2125
  let s2 = { ...subst };
2057
2126
  for (let i = 0; i < a.elems.length; i++) {
2058
- s2 = unifyTerm(a.elems[i], b.elems[i], s2);
2127
+ s2 = unifyTermListAppend(a.elems[i], b.elems[i], s2);
2059
2128
  if (s2 === null) return null;
2060
2129
  }
2061
2130
  return s2;
2062
2131
  }
2063
2132
 
2064
- // Formulas:
2065
- // 1) If they are alpha-equivalent, succeed without leaking internal bindings.
2066
- // 2) Otherwise fall back to full unification (may bind vars).
2133
+ // Formulas
2067
2134
  if (a instanceof FormulaTerm && b instanceof FormulaTerm) {
2068
2135
  if (alphaEqFormulaTriples(a.triples, b.triples)) return { ...subst };
2069
2136
  return unifyFormulaTriples(a.triples, b.triples, subst);
2070
2137
  }
2138
+
2071
2139
  return null;
2072
2140
  }
2073
2141
 
@@ -2720,7 +2788,7 @@ function listAppendSplit(parts, resElems, subst) {
2720
2788
  const n = resElems.length;
2721
2789
  for (let k = 0; k <= n; k++) {
2722
2790
  const left = new ListTerm(resElems.slice(0, k));
2723
- let s1 = unifyTerm(parts[0], left, subst);
2791
+ let s1 = unifyTermListAppend(parts[0], left, subst);
2724
2792
  if (s1 === null) continue;
2725
2793
  const restElems = resElems.slice(k);
2726
2794
  out.push(...listAppendSplit(parts.slice(1), restElems, s1));
@@ -2789,7 +2857,7 @@ function parseNumericLiteralInfo(t) {
2789
2857
  if (Number.isNaN(num)) return null;
2790
2858
 
2791
2859
  // allow +/-Infinity for float/double
2792
- if ((dt2 === XSD_DECIMAL_DT) && !Number.isFinite(num)) return null;
2860
+ if (dt2 === XSD_DECIMAL_DT && !Number.isFinite(num)) return null;
2793
2861
 
2794
2862
  return { dt: dt2, kind: 'number', value: num, lexStr };
2795
2863
  }
@@ -3311,6 +3379,17 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3311
3379
  s2[g.o.name] = lit;
3312
3380
  return [s2];
3313
3381
  }
3382
+ if (g.o instanceof Blank) return [{ ...subst }];
3383
+
3384
+ const oi = parseIntLiteral(g.o);
3385
+ if (oi !== null && oi === q) return [{ ...subst }];
3386
+
3387
+ // Only do numeric compare when safe enough to convert
3388
+ const qNum = Number(q);
3389
+ if (Number.isFinite(qNum) && Math.abs(qNum) <= Number.MAX_SAFE_INTEGER) {
3390
+ if (numEqualTerm(g.o, qNum)) return [{ ...subst }];
3391
+ }
3392
+
3314
3393
  const s2 = unifyTerm(g.o, lit, subst);
3315
3394
  return s2 !== null ? [s2] : [];
3316
3395
  }
@@ -3329,6 +3408,10 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3329
3408
  s2[g.o.name] = lit;
3330
3409
  return [s2];
3331
3410
  }
3411
+ if (g.o instanceof Blank) return [{ ...subst }];
3412
+
3413
+ if (numEqualTerm(g.o, q)) return [{ ...subst }];
3414
+
3332
3415
  const s2 = unifyTerm(g.o, lit, subst);
3333
3416
  return s2 !== null ? [s2] : [];
3334
3417
  }
@@ -3509,18 +3592,27 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3509
3592
  // math:rounded
3510
3593
  // Round to nearest integer.
3511
3594
  // If there are two such numbers, then the one closest to positive infinity is returned.
3512
- // Schema: $s math:rounded $o
3595
+ // Schema: $s+ math:rounded $o-
3596
+ // Note: spec says $o is xsd:integer, but we also accept any numeric $o that equals the rounded value.
3513
3597
  if (g.p instanceof Iri && g.p.value === MATH_NS + 'rounded') {
3514
3598
  const a = parseNum(g.s);
3515
3599
  if (a === null) return [];
3516
- const rVal = Math.round(a);
3517
- const lit = new Literal(formatNum(rVal));
3600
+ if (Number.isNaN(a)) return [];
3601
+
3602
+ const rVal = Math.round(a); // ties go toward +∞ in JS (e.g., -1.5 -> -1)
3603
+ const lit = new Literal(String(rVal)); // integer token
3518
3604
 
3519
3605
  if (g.o instanceof Var) {
3520
3606
  const s2 = { ...subst };
3521
3607
  s2[g.o.name] = lit;
3522
3608
  return [s2];
3523
3609
  }
3610
+ if (g.o instanceof Blank) return [{ ...subst }];
3611
+
3612
+ // Accept typed numeric literals too (e.g., "3"^^xsd:float) if numerically equal.
3613
+ if (numEqualTerm(g.o, rVal)) return [{ ...subst }];
3614
+
3615
+ // Fallback to strict unification
3524
3616
  const s2 = unifyTerm(g.o, lit, subst);
3525
3617
  return s2 !== null ? [s2] : [];
3526
3618
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.6.7",
3
+ "version": "1.6.9",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [