eyeling 1.6.6 → 1.6.8

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 +80 -5
  2. package/package.json +1 -1
package/eyeling.js CHANGED
@@ -2404,6 +2404,20 @@ const XSD_INTEGER_DERIVED_DTS = new Set([
2404
2404
  XSD_NS + 'positiveInteger',
2405
2405
  ]);
2406
2406
 
2407
+ function parseXsdFloatSpecialLex(s) {
2408
+ if (s === 'INF' || s === '+INF') return Infinity;
2409
+ if (s === '-INF') return -Infinity;
2410
+ if (s === 'NaN') return NaN;
2411
+ return null;
2412
+ }
2413
+
2414
+ function formatXsdFloatSpecialLex(n) {
2415
+ if (n === Infinity) return 'INF';
2416
+ if (n === -Infinity) return '-INF';
2417
+ if (Number.isNaN(n)) return 'NaN';
2418
+ return null;
2419
+ }
2420
+
2407
2421
  function isQuotedLexical(lex) {
2408
2422
  // Note: the lexer stores long strings with literal delimiters: """..."""
2409
2423
  return (
@@ -2440,7 +2454,7 @@ function looksLikeUntypedNumericTokenLex(lex) {
2440
2454
 
2441
2455
  function parseNum(t) {
2442
2456
  // Parse as JS Number, but ONLY for xsd numeric datatypes or untyped numeric tokens.
2443
- // Rejects values such as "1"^^<...non-numeric...> or "1" (a string literal).
2457
+ // For xsd:float/xsd:double, accept INF/-INF/NaN (and +INF).
2444
2458
  if (!(t instanceof Literal)) return null;
2445
2459
 
2446
2460
  const [lex, dt] = literalParts(t.value);
@@ -2449,6 +2463,17 @@ function parseNum(t) {
2449
2463
  if (dt !== null) {
2450
2464
  if (!isXsdNumericDatatype(dt)) return null;
2451
2465
  const val = stripQuotes(lex);
2466
+
2467
+ // float/double: allow INF/-INF/NaN and allow +/-Infinity results
2468
+ if (dt === XSD_FLOAT_DT || dt === XSD_DOUBLE_DT) {
2469
+ const sp = parseXsdFloatSpecialLex(val);
2470
+ if (sp !== null) return sp;
2471
+ const n = Number(val);
2472
+ if (Number.isNaN(n)) return null;
2473
+ return n; // may be finite, +/-Infinity, or NaN (if val was "NaN" handled above)
2474
+ }
2475
+
2476
+ // decimal/integer-derived: keep strict finite parsing
2452
2477
  const n = Number(val);
2453
2478
  if (!Number.isFinite(n)) return null;
2454
2479
  return n;
@@ -2705,7 +2730,15 @@ function listAppendSplit(parts, resElems, subst) {
2705
2730
 
2706
2731
  function numEqualTerm(t, n, eps = 1e-9) {
2707
2732
  const v = parseNum(t);
2708
- return v !== null && Math.abs(v - n) < eps;
2733
+ if (v === null) return false;
2734
+
2735
+ // NaN is not equal to anything (including itself) for our numeric-equality use.
2736
+ if (Number.isNaN(v) || Number.isNaN(n)) return false;
2737
+
2738
+ // Infinity handling
2739
+ if (!Number.isFinite(v) || !Number.isFinite(n)) return v === n;
2740
+
2741
+ return Math.abs(v - n) < eps;
2709
2742
  }
2710
2743
 
2711
2744
  function numericDatatypeFromLex(lex) {
@@ -2746,8 +2779,18 @@ function parseNumericLiteralInfo(t) {
2746
2779
  }
2747
2780
  }
2748
2781
 
2782
+ // float/double special lexicals
2783
+ if (dt2 === XSD_FLOAT_DT || dt2 === XSD_DOUBLE_DT) {
2784
+ const sp = parseXsdFloatSpecialLex(lexStr);
2785
+ if (sp !== null) return { dt: dt2, kind: 'number', value: sp, lexStr };
2786
+ }
2787
+
2749
2788
  const num = Number(lexStr);
2750
2789
  if (Number.isNaN(num)) return null;
2790
+
2791
+ // allow +/-Infinity for float/double
2792
+ if ((dt2 === XSD_DECIMAL_DT) && !Number.isFinite(num)) return null;
2793
+
2751
2794
  return { dt: dt2, kind: 'number', value: num, lexStr };
2752
2795
  }
2753
2796
 
@@ -2800,6 +2843,14 @@ function makeNumericOutputLiteral(val, dt) {
2800
2843
  // If a non-integer sneaks in, promote to decimal.
2801
2844
  return new Literal(`"${formatNum(val)}"^^<${XSD_DECIMAL_DT}>`);
2802
2845
  }
2846
+
2847
+ if (dt === XSD_FLOAT_DT || dt === XSD_DOUBLE_DT) {
2848
+ const sp = formatXsdFloatSpecialLex(val);
2849
+ const lex = sp !== null ? sp : formatNum(val);
2850
+ return new Literal(`"${lex}"^^<${dt}>`);
2851
+ }
2852
+
2853
+ // decimal
2803
2854
  const lex = typeof val === 'bigint' ? val.toString() : formatNum(val);
2804
2855
  return new Literal(`"${lex}"^^<${dt}>`);
2805
2856
  }
@@ -3260,6 +3311,17 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3260
3311
  s2[g.o.name] = lit;
3261
3312
  return [s2];
3262
3313
  }
3314
+ if (g.o instanceof Blank) return [{ ...subst }];
3315
+
3316
+ const oi = parseIntLiteral(g.o);
3317
+ if (oi !== null && oi === q) return [{ ...subst }];
3318
+
3319
+ // Only do numeric compare when safe enough to convert
3320
+ const qNum = Number(q);
3321
+ if (Number.isFinite(qNum) && Math.abs(qNum) <= Number.MAX_SAFE_INTEGER) {
3322
+ if (numEqualTerm(g.o, qNum)) return [{ ...subst }];
3323
+ }
3324
+
3263
3325
  const s2 = unifyTerm(g.o, lit, subst);
3264
3326
  return s2 !== null ? [s2] : [];
3265
3327
  }
@@ -3278,6 +3340,10 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3278
3340
  s2[g.o.name] = lit;
3279
3341
  return [s2];
3280
3342
  }
3343
+ if (g.o instanceof Blank) return [{ ...subst }];
3344
+
3345
+ if (numEqualTerm(g.o, q)) return [{ ...subst }];
3346
+
3281
3347
  const s2 = unifyTerm(g.o, lit, subst);
3282
3348
  return s2 !== null ? [s2] : [];
3283
3349
  }
@@ -3458,18 +3524,27 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3458
3524
  // math:rounded
3459
3525
  // Round to nearest integer.
3460
3526
  // If there are two such numbers, then the one closest to positive infinity is returned.
3461
- // Schema: $s math:rounded $o
3527
+ // Schema: $s+ math:rounded $o-
3528
+ // Note: spec says $o is xsd:integer, but we also accept any numeric $o that equals the rounded value.
3462
3529
  if (g.p instanceof Iri && g.p.value === MATH_NS + 'rounded') {
3463
3530
  const a = parseNum(g.s);
3464
3531
  if (a === null) return [];
3465
- const rVal = Math.round(a);
3466
- const lit = new Literal(formatNum(rVal));
3532
+ if (Number.isNaN(a)) return [];
3533
+
3534
+ const rVal = Math.round(a); // ties go toward +∞ in JS (e.g., -1.5 -> -1)
3535
+ const lit = new Literal(String(rVal)); // integer token
3467
3536
 
3468
3537
  if (g.o instanceof Var) {
3469
3538
  const s2 = { ...subst };
3470
3539
  s2[g.o.name] = lit;
3471
3540
  return [s2];
3472
3541
  }
3542
+ if (g.o instanceof Blank) return [{ ...subst }];
3543
+
3544
+ // Accept typed numeric literals too (e.g., "3"^^xsd:float) if numerically equal.
3545
+ if (numEqualTerm(g.o, rVal)) return [{ ...subst }];
3546
+
3547
+ // Fallback to strict unification
3473
3548
  const s2 = unifyTerm(g.o, lit, subst);
3474
3549
  return s2 !== null ? [s2] : [];
3475
3550
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.6.6",
3
+ "version": "1.6.8",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [