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.
- package/eyeling.js +80 -5
- 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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
3466
|
-
|
|
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
|
}
|