eyeling 1.6.5 → 1.6.7
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/examples/output/cobalt-kepler-kitchen.n3 +3544 -3543
- package/examples/output/complex.n3 +25 -24
- package/examples/output/control-system.n3 +21 -20
- package/examples/output/cranberry-calculus.n3 +509 -508
- package/examples/output/drone-corridor-planner.n3 +154 -153
- package/examples/output/ev-roundtrip-planner.n3 +81 -80
- package/examples/output/gps.n3 +15 -14
- package/examples/output/jade-eigen-loom.n3 +2033 -2032
- package/examples/output/light-eaters.n3 +51 -50
- package/examples/output/lldm.n3 +244 -243
- package/examples/output/math-builtins-tests.n3 +40 -40
- package/examples/output/oslo-steps-library-scholarly.n3 +197 -196
- package/examples/output/oslo-steps-workflow-composition.n3 +29 -28
- package/examples/output/pi.n3 +5 -4
- package/examples/output/ruby-runge-workshop.n3 +106 -105
- package/examples/output/saffron-slopeworks.n3 +455 -454
- package/examples/output/spectral-week.n3 +81 -80
- package/examples/output/topaz-markov-mill.n3 +1618 -1617
- package/examples/output/ultramarine-simpson-forge.n3 +1213 -1212
- package/eyeling.js +369 -139
- 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,13 +2730,21 @@ 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) {
|
|
2712
|
-
if (/[eE]/.test(lex)) return
|
|
2713
|
-
if (lex.includes('.')) return
|
|
2714
|
-
return
|
|
2745
|
+
if (/[eE]/.test(lex)) return XSD_DOUBLE_DT;
|
|
2746
|
+
if (lex.includes('.')) return XSD_DECIMAL_DT;
|
|
2747
|
+
return XSD_INTEGER_DT;
|
|
2715
2748
|
}
|
|
2716
2749
|
|
|
2717
2750
|
function parseNumericLiteralInfo(t) {
|
|
@@ -2724,21 +2757,21 @@ function parseNumericLiteralInfo(t) {
|
|
|
2724
2757
|
let lexStr;
|
|
2725
2758
|
|
|
2726
2759
|
if (dt2 !== null) {
|
|
2727
|
-
|
|
2760
|
+
// Accept all xsd numeric datatypes; normalize integer-derived to xsd:integer.
|
|
2761
|
+
if (!isXsdNumericDatatype(dt2)) return null;
|
|
2762
|
+
if (isXsdIntegerDatatype(dt2)) dt2 = XSD_INTEGER_DT;
|
|
2728
2763
|
lexStr = stripQuotes(lex);
|
|
2729
2764
|
} else {
|
|
2765
|
+
// Untyped numeric token (N3/Turtle numeric literal)
|
|
2730
2766
|
if (typeof v !== 'string') return null;
|
|
2731
2767
|
if (v.startsWith('"')) return null; // exclude quoted strings
|
|
2732
2768
|
if (!/^[+-]?(?:\d+\.\d*|\.\d+|\d+)(?:[eE][+-]?\d+)?$/.test(v)) return null;
|
|
2733
2769
|
|
|
2734
|
-
|
|
2735
|
-
else if (v.includes('.')) dt2 = XSD_NS + 'decimal';
|
|
2736
|
-
else dt2 = XSD_NS + 'integer';
|
|
2737
|
-
|
|
2770
|
+
dt2 = numericDatatypeFromLex(v);
|
|
2738
2771
|
lexStr = v;
|
|
2739
2772
|
}
|
|
2740
2773
|
|
|
2741
|
-
if (dt2 ===
|
|
2774
|
+
if (dt2 === XSD_INTEGER_DT) {
|
|
2742
2775
|
try {
|
|
2743
2776
|
return { dt: dt2, kind: 'bigint', value: BigInt(lexStr), lexStr };
|
|
2744
2777
|
} catch {
|
|
@@ -2746,11 +2779,82 @@ 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
|
|
|
2797
|
+
function numericRank(dt) {
|
|
2798
|
+
if (dt === XSD_INTEGER_DT) return 0;
|
|
2799
|
+
if (dt === XSD_DECIMAL_DT) return 1;
|
|
2800
|
+
if (dt === XSD_FLOAT_DT) return 2;
|
|
2801
|
+
if (dt === XSD_DOUBLE_DT) return 3;
|
|
2802
|
+
return -1;
|
|
2803
|
+
}
|
|
2804
|
+
|
|
2805
|
+
function numericDatatypeOfTerm(t) {
|
|
2806
|
+
if (!(t instanceof Literal)) return null;
|
|
2807
|
+
const [lex, dt] = literalParts(t.value);
|
|
2808
|
+
|
|
2809
|
+
if (dt !== null) {
|
|
2810
|
+
if (!isXsdNumericDatatype(dt)) return null;
|
|
2811
|
+
if (isXsdIntegerDatatype(dt)) return XSD_INTEGER_DT;
|
|
2812
|
+
if (dt === XSD_DECIMAL_DT || dt === XSD_FLOAT_DT || dt === XSD_DOUBLE_DT) return dt;
|
|
2813
|
+
return null;
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
// Untyped numeric token
|
|
2817
|
+
if (!looksLikeUntypedNumericTokenLex(lex)) return null;
|
|
2818
|
+
return numericDatatypeFromLex(lex);
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2821
|
+
function commonNumericDatatype(terms, outTerm) {
|
|
2822
|
+
let r = 0;
|
|
2823
|
+
const all = Array.isArray(terms) ? terms.slice() : [];
|
|
2824
|
+
if (outTerm) all.push(outTerm);
|
|
2825
|
+
|
|
2826
|
+
for (const t of all) {
|
|
2827
|
+
const dt = numericDatatypeOfTerm(t);
|
|
2828
|
+
if (!dt) continue;
|
|
2829
|
+
const rr = numericRank(dt);
|
|
2830
|
+
if (rr > r) r = rr;
|
|
2831
|
+
}
|
|
2832
|
+
|
|
2833
|
+
if (r === 3) return XSD_DOUBLE_DT;
|
|
2834
|
+
if (r === 2) return XSD_FLOAT_DT;
|
|
2835
|
+
if (r === 1) return XSD_DECIMAL_DT;
|
|
2836
|
+
return XSD_INTEGER_DT;
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2839
|
+
function makeNumericOutputLiteral(val, dt) {
|
|
2840
|
+
if (dt === XSD_INTEGER_DT) {
|
|
2841
|
+
if (typeof val === 'bigint') return new Literal(val.toString());
|
|
2842
|
+
if (Number.isInteger(val)) return new Literal(String(val));
|
|
2843
|
+
// If a non-integer sneaks in, promote to decimal.
|
|
2844
|
+
return new Literal(`"${formatNum(val)}"^^<${XSD_DECIMAL_DT}>`);
|
|
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
|
|
2854
|
+
const lex = typeof val === 'bigint' ? val.toString() : formatNum(val);
|
|
2855
|
+
return new Literal(`"${lex}"^^<${dt}>`);
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2754
2858
|
function evalUnaryMathRel(g, subst, forwardFn, inverseFn /* may be null */) {
|
|
2755
2859
|
const sIsUnbound = g.s instanceof Var || g.s instanceof Blank;
|
|
2756
2860
|
const oIsUnbound = g.o instanceof Var || g.o instanceof Blank;
|
|
@@ -2762,9 +2866,13 @@ function evalUnaryMathRel(g, subst, forwardFn, inverseFn /* may be null */) {
|
|
|
2762
2866
|
if (a !== null) {
|
|
2763
2867
|
const outVal = forwardFn(a);
|
|
2764
2868
|
if (!Number.isFinite(outVal)) return [];
|
|
2869
|
+
|
|
2870
|
+
let outDt = commonNumericDatatype([g.s], g.o);
|
|
2871
|
+
if (outDt === XSD_INTEGER_DT && !Number.isInteger(outVal)) outDt = XSD_DECIMAL_DT;
|
|
2872
|
+
|
|
2765
2873
|
if (g.o instanceof Var) {
|
|
2766
2874
|
const s2 = { ...subst };
|
|
2767
|
-
s2[g.o.name] =
|
|
2875
|
+
s2[g.o.name] = makeNumericOutputLiteral(outVal, outDt);
|
|
2768
2876
|
return [s2];
|
|
2769
2877
|
}
|
|
2770
2878
|
if (g.o instanceof Blank) return [{ ...subst }];
|
|
@@ -2776,9 +2884,13 @@ function evalUnaryMathRel(g, subst, forwardFn, inverseFn /* may be null */) {
|
|
|
2776
2884
|
if (b !== null && typeof inverseFn === 'function') {
|
|
2777
2885
|
const inVal = inverseFn(b);
|
|
2778
2886
|
if (!Number.isFinite(inVal)) return [];
|
|
2887
|
+
|
|
2888
|
+
let inDt = commonNumericDatatype([g.o], g.s);
|
|
2889
|
+
if (inDt === XSD_INTEGER_DT && !Number.isInteger(inVal)) inDt = XSD_DECIMAL_DT;
|
|
2890
|
+
|
|
2779
2891
|
if (g.s instanceof Var) {
|
|
2780
2892
|
const s2 = { ...subst };
|
|
2781
|
-
s2[g.s.name] =
|
|
2893
|
+
s2[g.s.name] = makeNumericOutputLiteral(inVal, inDt);
|
|
2782
2894
|
return [s2];
|
|
2783
2895
|
}
|
|
2784
2896
|
if (g.s instanceof Blank) return [{ ...subst }];
|
|
@@ -2959,153 +3071,225 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2959
3071
|
}
|
|
2960
3072
|
|
|
2961
3073
|
// math:sum
|
|
3074
|
+
// Schema: ( $s.i+ )+ math:sum $o-
|
|
2962
3075
|
if (g.p instanceof Iri && g.p.value === MATH_NS + 'sum') {
|
|
2963
|
-
if (g.s instanceof ListTerm
|
|
2964
|
-
|
|
2965
|
-
|
|
3076
|
+
if (!(g.s instanceof ListTerm) || g.s.elems.length < 2) return [];
|
|
3077
|
+
const xs = g.s.elems;
|
|
3078
|
+
|
|
3079
|
+
const dtOut0 = commonNumericDatatype(xs, g.o);
|
|
3080
|
+
|
|
3081
|
+
// Exact integer mode
|
|
3082
|
+
if (dtOut0 === XSD_INTEGER_DT) {
|
|
3083
|
+
let total = 0n;
|
|
2966
3084
|
for (const t of xs) {
|
|
2967
|
-
const v =
|
|
3085
|
+
const v = parseIntLiteral(t);
|
|
2968
3086
|
if (v === null) return [];
|
|
2969
|
-
|
|
2970
|
-
}
|
|
2971
|
-
|
|
2972
|
-
let lit;
|
|
2973
|
-
const allBig = values.every((v) => typeof v === 'bigint');
|
|
2974
|
-
if (allBig) {
|
|
2975
|
-
let total = 0n;
|
|
2976
|
-
for (const v of values) total += v;
|
|
2977
|
-
lit = new Literal(total.toString());
|
|
2978
|
-
} else {
|
|
2979
|
-
let total = 0.0;
|
|
2980
|
-
for (const v of values) {
|
|
2981
|
-
total += typeof v === 'bigint' ? Number(v) : v;
|
|
2982
|
-
}
|
|
2983
|
-
lit = new Literal(formatNum(total));
|
|
3087
|
+
total += v;
|
|
2984
3088
|
}
|
|
2985
3089
|
|
|
2986
3090
|
if (g.o instanceof Var) {
|
|
2987
3091
|
const s2 = { ...subst };
|
|
2988
|
-
s2[g.o.name] =
|
|
3092
|
+
s2[g.o.name] = makeNumericOutputLiteral(total, XSD_INTEGER_DT);
|
|
2989
3093
|
return [s2];
|
|
2990
3094
|
}
|
|
2991
|
-
|
|
2992
|
-
|
|
3095
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3096
|
+
|
|
3097
|
+
const oi = parseIntLiteral(g.o);
|
|
3098
|
+
if (oi !== null && oi === total) return [{ ...subst }];
|
|
3099
|
+
|
|
3100
|
+
// Fallback numeric compare
|
|
3101
|
+
if (numEqualTerm(g.o, Number(total))) return [{ ...subst }];
|
|
3102
|
+
return [];
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3105
|
+
// Numeric mode (decimal/float/double)
|
|
3106
|
+
let total = 0.0;
|
|
3107
|
+
for (const t of xs) {
|
|
3108
|
+
const v = parseNum(t);
|
|
3109
|
+
if (v === null) return [];
|
|
3110
|
+
total += v;
|
|
3111
|
+
}
|
|
3112
|
+
|
|
3113
|
+
let dtOut = dtOut0;
|
|
3114
|
+
if (dtOut === XSD_INTEGER_DT && !Number.isInteger(total)) dtOut = XSD_DECIMAL_DT;
|
|
3115
|
+
const lit = makeNumericOutputLiteral(total, dtOut);
|
|
3116
|
+
|
|
3117
|
+
if (g.o instanceof Var) {
|
|
3118
|
+
const s2 = { ...subst };
|
|
3119
|
+
s2[g.o.name] = lit;
|
|
3120
|
+
return [s2];
|
|
2993
3121
|
}
|
|
3122
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3123
|
+
if (numEqualTerm(g.o, total)) return [{ ...subst }];
|
|
2994
3124
|
return [];
|
|
2995
3125
|
}
|
|
2996
3126
|
|
|
2997
3127
|
// math:product
|
|
3128
|
+
// Schema: ( $s.i+ )+ math:product $o-
|
|
2998
3129
|
if (g.p instanceof Iri && g.p.value === MATH_NS + 'product') {
|
|
2999
|
-
if (g.s instanceof ListTerm
|
|
3000
|
-
|
|
3001
|
-
|
|
3130
|
+
if (!(g.s instanceof ListTerm) || g.s.elems.length < 2) return [];
|
|
3131
|
+
const xs = g.s.elems;
|
|
3132
|
+
|
|
3133
|
+
const dtOut0 = commonNumericDatatype(xs, g.o);
|
|
3134
|
+
|
|
3135
|
+
// Exact integer mode
|
|
3136
|
+
if (dtOut0 === XSD_INTEGER_DT) {
|
|
3137
|
+
let prod = 1n;
|
|
3002
3138
|
for (const t of xs) {
|
|
3003
|
-
const v =
|
|
3139
|
+
const v = parseIntLiteral(t);
|
|
3004
3140
|
if (v === null) return [];
|
|
3005
|
-
|
|
3006
|
-
}
|
|
3007
|
-
|
|
3008
|
-
let lit;
|
|
3009
|
-
const allBig = values.every((v) => typeof v === 'bigint');
|
|
3010
|
-
if (allBig) {
|
|
3011
|
-
let prod = 1n;
|
|
3012
|
-
for (const v of values) prod *= v;
|
|
3013
|
-
lit = new Literal(prod.toString());
|
|
3014
|
-
} else {
|
|
3015
|
-
let prod = 1.0;
|
|
3016
|
-
for (const v of values) {
|
|
3017
|
-
prod *= typeof v === 'bigint' ? Number(v) : v;
|
|
3018
|
-
}
|
|
3019
|
-
lit = new Literal(formatNum(prod));
|
|
3141
|
+
prod *= v;
|
|
3020
3142
|
}
|
|
3021
3143
|
|
|
3022
3144
|
if (g.o instanceof Var) {
|
|
3023
3145
|
const s2 = { ...subst };
|
|
3024
|
-
s2[g.o.name] =
|
|
3146
|
+
s2[g.o.name] = makeNumericOutputLiteral(prod, XSD_INTEGER_DT);
|
|
3025
3147
|
return [s2];
|
|
3026
3148
|
}
|
|
3027
|
-
|
|
3028
|
-
|
|
3149
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3150
|
+
|
|
3151
|
+
const oi = parseIntLiteral(g.o);
|
|
3152
|
+
if (oi !== null && oi === prod) return [{ ...subst }];
|
|
3153
|
+
if (numEqualTerm(g.o, Number(prod))) return [{ ...subst }];
|
|
3154
|
+
return [];
|
|
3029
3155
|
}
|
|
3156
|
+
|
|
3157
|
+
// Numeric mode (decimal/float/double)
|
|
3158
|
+
let prod = 1.0;
|
|
3159
|
+
for (const t of xs) {
|
|
3160
|
+
const v = parseNum(t);
|
|
3161
|
+
if (v === null) return [];
|
|
3162
|
+
prod *= v;
|
|
3163
|
+
}
|
|
3164
|
+
|
|
3165
|
+
let dtOut = dtOut0;
|
|
3166
|
+
if (dtOut === XSD_INTEGER_DT && !Number.isInteger(prod)) dtOut = XSD_DECIMAL_DT;
|
|
3167
|
+
const lit = makeNumericOutputLiteral(prod, dtOut);
|
|
3168
|
+
|
|
3169
|
+
if (g.o instanceof Var) {
|
|
3170
|
+
const s2 = { ...subst };
|
|
3171
|
+
s2[g.o.name] = lit;
|
|
3172
|
+
return [s2];
|
|
3173
|
+
}
|
|
3174
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3175
|
+
if (numEqualTerm(g.o, prod)) return [{ ...subst }];
|
|
3176
|
+
return [];
|
|
3030
3177
|
}
|
|
3031
3178
|
|
|
3032
3179
|
// math:difference
|
|
3180
|
+
// Schema: ( $s.1+ $s.2+ )+ math:difference $o-
|
|
3033
3181
|
if (g.p instanceof Iri && g.p.value === MATH_NS + 'difference') {
|
|
3034
|
-
if (g.s instanceof ListTerm
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3182
|
+
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3183
|
+
const [a0, b0] = g.s.elems;
|
|
3184
|
+
|
|
3185
|
+
// 1) Date/datetime difference -> duration (needed for examples/age.n3)
|
|
3186
|
+
const aDt = parseDatetimeLike(a0);
|
|
3187
|
+
const bDt = parseDatetimeLike(b0);
|
|
3188
|
+
if (aDt !== null && bDt !== null) {
|
|
3189
|
+
const diffSecs = (aDt.getTime() - bDt.getTime()) / 1000.0;
|
|
3190
|
+
const durTerm = formatDurationLiteralFromSeconds(diffSecs);
|
|
3191
|
+
if (g.o instanceof Var) {
|
|
3192
|
+
const s2 = { ...subst };
|
|
3193
|
+
s2[g.o.name] = durTerm;
|
|
3194
|
+
return [s2];
|
|
3195
|
+
}
|
|
3196
|
+
const s2 = unifyTerm(g.o, durTerm, subst);
|
|
3197
|
+
return s2 !== null ? [s2] : [];
|
|
3198
|
+
}
|
|
3199
|
+
|
|
3200
|
+
// 2) Date/datetime minus duration/seconds -> dateTime (keeps older functionality)
|
|
3201
|
+
if (aDt !== null) {
|
|
3202
|
+
const secs = parseNumOrDuration(b0);
|
|
3203
|
+
if (secs !== null) {
|
|
3204
|
+
const outSecs = aDt.getTime() / 1000.0 - secs;
|
|
3205
|
+
const lex = utcIsoDateTimeStringFromEpochSeconds(outSecs);
|
|
3206
|
+
const lit = new Literal(`"${lex}"^^<${XSD_NS}dateTime>`);
|
|
3043
3207
|
if (g.o instanceof Var) {
|
|
3044
3208
|
const s2 = { ...subst };
|
|
3045
3209
|
s2[g.o.name] = lit;
|
|
3046
3210
|
return [s2];
|
|
3047
|
-
} else {
|
|
3048
|
-
const s2 = unifyTerm(g.o, lit, subst);
|
|
3049
|
-
return s2 !== null ? [s2] : [];
|
|
3050
3211
|
}
|
|
3212
|
+
const s2 = unifyTerm(g.o, lit, subst);
|
|
3213
|
+
return s2 !== null ? [s2] : [];
|
|
3051
3214
|
}
|
|
3215
|
+
}
|
|
3052
3216
|
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
if (g.o instanceof Literal && g.o.value === formatNum(c)) {
|
|
3064
|
-
return [{ ...subst }];
|
|
3065
|
-
}
|
|
3217
|
+
// 3) Exact integer difference (BigInt)
|
|
3218
|
+
const ai = parseIntLiteral(a0);
|
|
3219
|
+
const bi = parseIntLiteral(b0);
|
|
3220
|
+
if (ai !== null && bi !== null) {
|
|
3221
|
+
const ci = ai - bi;
|
|
3222
|
+
const lit = new Literal(ci.toString());
|
|
3223
|
+
if (g.o instanceof Var) {
|
|
3224
|
+
const s2 = { ...subst };
|
|
3225
|
+
s2[g.o.name] = lit;
|
|
3226
|
+
return [s2];
|
|
3066
3227
|
}
|
|
3228
|
+
const s2 = unifyTerm(g.o, lit, subst);
|
|
3229
|
+
return s2 !== null ? [s2] : [];
|
|
3230
|
+
}
|
|
3067
3231
|
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3232
|
+
// 4) Numeric difference (your “typed output + numeric compare” version)
|
|
3233
|
+
const a = parseNum(a0);
|
|
3234
|
+
const b = parseNum(b0);
|
|
3235
|
+
if (a === null || b === null) return [];
|
|
3236
|
+
|
|
3237
|
+
const c = a - b;
|
|
3238
|
+
if (!Number.isFinite(c)) return [];
|
|
3239
|
+
|
|
3240
|
+
// If you added commonNumericDatatype/makeNumericOutputLiteral, keep using them:
|
|
3241
|
+
if (typeof commonNumericDatatype === 'function' && typeof makeNumericOutputLiteral === 'function') {
|
|
3242
|
+
let dtOut = commonNumericDatatype([a0, b0], g.o);
|
|
3243
|
+
if (dtOut === XSD_INTEGER_DT && !Number.isInteger(c)) dtOut = XSD_DECIMAL_DT;
|
|
3244
|
+
const lit = makeNumericOutputLiteral(c, dtOut);
|
|
3245
|
+
|
|
3246
|
+
if (g.o instanceof Var) {
|
|
3247
|
+
const s2 = { ...subst };
|
|
3248
|
+
s2[g.o.name] = lit;
|
|
3249
|
+
return [s2];
|
|
3081
3250
|
}
|
|
3251
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3252
|
+
if (numEqualTerm(g.o, c)) return [{ ...subst }];
|
|
3082
3253
|
return [];
|
|
3083
3254
|
}
|
|
3255
|
+
|
|
3256
|
+
// Fallback (if you *don’t* have those helpers yet):
|
|
3257
|
+
const lit = new Literal(formatNum(c));
|
|
3258
|
+
if (g.o instanceof Var) {
|
|
3259
|
+
const s2 = { ...subst };
|
|
3260
|
+
s2[g.o.name] = lit;
|
|
3261
|
+
return [s2];
|
|
3262
|
+
}
|
|
3263
|
+
const s2 = unifyTerm(g.o, lit, subst);
|
|
3264
|
+
return s2 !== null ? [s2] : [];
|
|
3084
3265
|
}
|
|
3085
3266
|
|
|
3086
3267
|
// math:quotient
|
|
3268
|
+
// Schema: ( $s.1+ $s.2+ )+ math:quotient $o-
|
|
3087
3269
|
if (g.p instanceof Iri && g.p.value === MATH_NS + 'quotient') {
|
|
3088
|
-
if (g.s instanceof ListTerm
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
return [];
|
|
3270
|
+
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3271
|
+
const [a0, b0] = g.s.elems;
|
|
3272
|
+
|
|
3273
|
+
const a = parseNum(a0);
|
|
3274
|
+
const b = parseNum(b0);
|
|
3275
|
+
if (a === null || b === null) return [];
|
|
3276
|
+
if (!Number.isFinite(a) || !Number.isFinite(b) || b === 0) return [];
|
|
3277
|
+
|
|
3278
|
+
const c = a / b;
|
|
3279
|
+
if (!Number.isFinite(c)) return [];
|
|
3280
|
+
|
|
3281
|
+
let dtOut = commonNumericDatatype([a0, b0], g.o);
|
|
3282
|
+
if (dtOut === XSD_INTEGER_DT && !Number.isInteger(c)) dtOut = XSD_DECIMAL_DT;
|
|
3283
|
+
const lit = makeNumericOutputLiteral(c, dtOut);
|
|
3284
|
+
|
|
3285
|
+
if (g.o instanceof Var) {
|
|
3286
|
+
const s2 = { ...subst };
|
|
3287
|
+
s2[g.o.name] = lit;
|
|
3288
|
+
return [s2];
|
|
3108
3289
|
}
|
|
3290
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3291
|
+
if (numEqualTerm(g.o, c)) return [{ ...subst }];
|
|
3292
|
+
return [];
|
|
3109
3293
|
}
|
|
3110
3294
|
|
|
3111
3295
|
// math:integerQuotient
|
|
@@ -3152,30 +3336,43 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3152
3336
|
// math:exponentiation
|
|
3153
3337
|
if (g.p instanceof Iri && g.p.value === MATH_NS + 'exponentiation') {
|
|
3154
3338
|
if (g.s instanceof ListTerm && g.s.elems.length === 2) {
|
|
3155
|
-
const
|
|
3156
|
-
const
|
|
3157
|
-
|
|
3339
|
+
const baseTerm = g.s.elems[0];
|
|
3340
|
+
const expTerm = g.s.elems[1];
|
|
3341
|
+
|
|
3342
|
+
const a = parseNum(baseTerm);
|
|
3158
3343
|
let b = null;
|
|
3159
|
-
if (a !== null
|
|
3344
|
+
if (a !== null) b = parseNum(expTerm);
|
|
3160
3345
|
|
|
3346
|
+
// Forward mode: base and exponent are numeric
|
|
3161
3347
|
if (a !== null && b !== null) {
|
|
3162
3348
|
const cVal = a ** b;
|
|
3349
|
+
if (!Number.isFinite(cVal)) return [];
|
|
3350
|
+
|
|
3351
|
+
let dtOut = commonNumericDatatype([baseTerm, expTerm], g.o);
|
|
3352
|
+
if (dtOut === XSD_INTEGER_DT && !Number.isInteger(cVal)) dtOut = XSD_DECIMAL_DT;
|
|
3353
|
+
const lit = makeNumericOutputLiteral(cVal, dtOut);
|
|
3354
|
+
|
|
3163
3355
|
if (g.o instanceof Var) {
|
|
3164
3356
|
const s2 = { ...subst };
|
|
3165
|
-
s2[g.o.name] =
|
|
3357
|
+
s2[g.o.name] = lit;
|
|
3166
3358
|
return [s2];
|
|
3167
3359
|
}
|
|
3168
|
-
if (
|
|
3169
|
-
|
|
3170
|
-
}
|
|
3360
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3361
|
+
if (numEqualTerm(g.o, cVal)) return [{ ...subst }];
|
|
3171
3362
|
}
|
|
3172
3363
|
|
|
3173
|
-
//
|
|
3174
|
-
|
|
3364
|
+
// Inverse mode: solve exponent
|
|
3365
|
+
const c = parseNum(g.o);
|
|
3366
|
+
if (a !== null && expTerm instanceof Var && c !== null) {
|
|
3175
3367
|
if (a > 0.0 && a !== 1.0 && c > 0.0) {
|
|
3176
3368
|
const bVal = Math.log(c) / Math.log(a);
|
|
3369
|
+
if (!Number.isFinite(bVal)) return [];
|
|
3370
|
+
|
|
3371
|
+
let dtB = commonNumericDatatype([baseTerm, g.o], expTerm);
|
|
3372
|
+
if (dtB === XSD_INTEGER_DT && !Number.isInteger(bVal)) dtB = XSD_DECIMAL_DT;
|
|
3373
|
+
|
|
3177
3374
|
const s2 = { ...subst };
|
|
3178
|
-
s2[
|
|
3375
|
+
s2[expTerm.name] = makeNumericOutputLiteral(bVal, dtB);
|
|
3179
3376
|
return [s2];
|
|
3180
3377
|
}
|
|
3181
3378
|
}
|
|
@@ -3186,15 +3383,21 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3186
3383
|
// math:absoluteValue
|
|
3187
3384
|
if (g.p instanceof Iri && g.p.value === MATH_NS + 'absoluteValue') {
|
|
3188
3385
|
const a = parseNum(g.s);
|
|
3189
|
-
if (a
|
|
3386
|
+
if (a === null) return [];
|
|
3387
|
+
|
|
3388
|
+
const outVal = Math.abs(a);
|
|
3389
|
+
if (!Number.isFinite(outVal)) return [];
|
|
3390
|
+
|
|
3391
|
+
let dtOut = commonNumericDatatype([g.s], g.o);
|
|
3392
|
+
if (dtOut === XSD_INTEGER_DT && !Number.isInteger(outVal)) dtOut = XSD_DECIMAL_DT;
|
|
3393
|
+
|
|
3394
|
+
if (g.o instanceof Var) {
|
|
3190
3395
|
const s2 = { ...subst };
|
|
3191
|
-
s2[g.o.name] =
|
|
3396
|
+
s2[g.o.name] = makeNumericOutputLiteral(outVal, dtOut);
|
|
3192
3397
|
return [s2];
|
|
3193
3398
|
}
|
|
3194
|
-
|
|
3195
|
-
if (
|
|
3196
|
-
if (Math.abs(Math.abs(a) - b) < 1e-9) return [{ ...subst }];
|
|
3197
|
-
}
|
|
3399
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3400
|
+
if (numEqualTerm(g.o, outVal)) return [{ ...subst }];
|
|
3198
3401
|
return [];
|
|
3199
3402
|
}
|
|
3200
3403
|
|
|
@@ -3260,20 +3463,47 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3260
3463
|
// Schema: ( $a $b ) math:remainder $r
|
|
3261
3464
|
if (g.p instanceof Iri && g.p.value === MATH_NS + 'remainder') {
|
|
3262
3465
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3263
|
-
const
|
|
3264
|
-
|
|
3265
|
-
|
|
3466
|
+
const [a0, b0] = g.s.elems;
|
|
3467
|
+
|
|
3468
|
+
// Prefer exact integer arithmetic (BigInt)
|
|
3469
|
+
const ai = parseIntLiteral(a0);
|
|
3470
|
+
const bi = parseIntLiteral(b0);
|
|
3471
|
+
if (ai !== null && bi !== null) {
|
|
3472
|
+
if (bi === 0n) return [];
|
|
3473
|
+
const r = ai % bi;
|
|
3474
|
+
const lit = makeNumericOutputLiteral(r, XSD_INTEGER_DT);
|
|
3475
|
+
|
|
3476
|
+
if (g.o instanceof Var) {
|
|
3477
|
+
const s2 = { ...subst };
|
|
3478
|
+
s2[g.o.name] = lit;
|
|
3479
|
+
return [s2];
|
|
3480
|
+
}
|
|
3481
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3482
|
+
|
|
3483
|
+
const oi = parseIntLiteral(g.o);
|
|
3484
|
+
if (oi !== null && oi === r) return [{ ...subst }];
|
|
3485
|
+
if (numEqualTerm(g.o, Number(r))) return [{ ...subst }];
|
|
3486
|
+
return [];
|
|
3487
|
+
}
|
|
3488
|
+
|
|
3489
|
+
// Fallback: allow Number literals that still represent integers
|
|
3490
|
+
const a = parseNum(a0);
|
|
3491
|
+
const b = parseNum(b0);
|
|
3492
|
+
if (a === null || b === null) return [];
|
|
3493
|
+
if (!Number.isFinite(a) || !Number.isFinite(b) || b === 0) return [];
|
|
3494
|
+
if (!Number.isInteger(a) || !Number.isInteger(b)) return [];
|
|
3495
|
+
|
|
3266
3496
|
const rVal = a % b;
|
|
3267
|
-
|
|
3268
|
-
const lit = new Literal(formatNum(rVal));
|
|
3497
|
+
const lit = makeNumericOutputLiteral(rVal, XSD_INTEGER_DT);
|
|
3269
3498
|
|
|
3270
3499
|
if (g.o instanceof Var) {
|
|
3271
3500
|
const s2 = { ...subst };
|
|
3272
3501
|
s2[g.o.name] = lit;
|
|
3273
3502
|
return [s2];
|
|
3274
3503
|
}
|
|
3275
|
-
|
|
3276
|
-
|
|
3504
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3505
|
+
if (numEqualTerm(g.o, rVal)) return [{ ...subst }];
|
|
3506
|
+
return [];
|
|
3277
3507
|
}
|
|
3278
3508
|
|
|
3279
3509
|
// math:rounded
|