eyeling 1.6.15 → 1.6.17
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 +325 -509
- package/package.json +1 -1
package/eyeling.js
CHANGED
|
@@ -1993,6 +1993,10 @@ function applySubstTriple(tr, s) {
|
|
|
1993
1993
|
return new Triple(applySubstTerm(tr.s, s), applySubstTerm(tr.p, s), applySubstTerm(tr.o, s));
|
|
1994
1994
|
}
|
|
1995
1995
|
|
|
1996
|
+
function iriValue(t) {
|
|
1997
|
+
return t instanceof Iri ? t.value : null;
|
|
1998
|
+
}
|
|
1999
|
+
|
|
1996
2000
|
function unifyOpenWithList(prefix, tailv, ys, subst) {
|
|
1997
2001
|
if (ys.length < prefix.length) return null;
|
|
1998
2002
|
let s2 = { ...subst };
|
|
@@ -2041,6 +2045,16 @@ function unifyFormulaTriples(xs, ys, subst) {
|
|
|
2041
2045
|
}
|
|
2042
2046
|
|
|
2043
2047
|
function unifyTerm(a, b, subst) {
|
|
2048
|
+
return unifyTermWithOptions(a, b, subst, { boolValueEq: true, intDecimalEq: false });
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
function unifyTermListAppend(a, b, subst) {
|
|
2052
|
+
// Keep list:append behavior: allow integer<->decimal exact equality,
|
|
2053
|
+
// but do NOT add boolean-value equivalence (preserves current semantics).
|
|
2054
|
+
return unifyTermWithOptions(a, b, subst, { boolValueEq: false, intDecimalEq: true });
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
function unifyTermWithOptions(a, b, subst, opts) {
|
|
2044
2058
|
a = applySubstTerm(a, subst);
|
|
2045
2059
|
b = applySubstTerm(b, subst);
|
|
2046
2060
|
|
|
@@ -2054,9 +2068,8 @@ function unifyTerm(a, b, subst) {
|
|
|
2054
2068
|
s2[v] = t;
|
|
2055
2069
|
return s2;
|
|
2056
2070
|
}
|
|
2057
|
-
|
|
2058
2071
|
if (b instanceof Var) {
|
|
2059
|
-
return
|
|
2072
|
+
return unifyTermWithOptions(b, a, subst, opts);
|
|
2060
2073
|
}
|
|
2061
2074
|
|
|
2062
2075
|
// Exact matches
|
|
@@ -2064,25 +2077,25 @@ function unifyTerm(a, b, subst) {
|
|
|
2064
2077
|
if (a instanceof Literal && b instanceof Literal && a.value === b.value) return { ...subst };
|
|
2065
2078
|
if (a instanceof Blank && b instanceof Blank && a.label === b.label) return { ...subst };
|
|
2066
2079
|
|
|
2067
|
-
//
|
|
2080
|
+
// Plain string vs xsd:string equivalence
|
|
2068
2081
|
if (a instanceof Literal && b instanceof Literal) {
|
|
2069
2082
|
if (literalsEquivalentAsXsdString(a.value, b.value)) return { ...subst };
|
|
2070
2083
|
}
|
|
2071
2084
|
|
|
2072
|
-
// Boolean-value
|
|
2073
|
-
if (a instanceof Literal && b instanceof Literal) {
|
|
2085
|
+
// Boolean-value equivalence (ONLY for normal unifyTerm)
|
|
2086
|
+
if (opts.boolValueEq && a instanceof Literal && b instanceof Literal) {
|
|
2074
2087
|
const ai = parseBooleanLiteralInfo(a);
|
|
2075
2088
|
const bi = parseBooleanLiteralInfo(b);
|
|
2076
2089
|
if (ai && bi && ai.value === bi.value) return { ...subst };
|
|
2077
2090
|
}
|
|
2078
2091
|
|
|
2079
|
-
// Numeric-value match
|
|
2092
|
+
// Numeric-value match:
|
|
2093
|
+
// - always allow equality when datatype matches (existing behavior)
|
|
2094
|
+
// - optionally allow integer<->decimal exact equality (list:append only)
|
|
2080
2095
|
if (a instanceof Literal && b instanceof Literal) {
|
|
2081
2096
|
const ai = parseNumericLiteralInfo(a);
|
|
2082
2097
|
const bi = parseNumericLiteralInfo(b);
|
|
2083
|
-
|
|
2084
2098
|
if (ai && bi) {
|
|
2085
|
-
// same datatype: keep existing behavior
|
|
2086
2099
|
if (ai.dt === bi.dt) {
|
|
2087
2100
|
if (ai.kind === 'bigint' && bi.kind === 'bigint') {
|
|
2088
2101
|
if (ai.value === bi.value) return { ...subst };
|
|
@@ -2092,101 +2105,18 @@ function unifyTerm(a, b, subst) {
|
|
|
2092
2105
|
if (!Number.isNaN(an) && !Number.isNaN(bn) && an === bn) return { ...subst };
|
|
2093
2106
|
}
|
|
2094
2107
|
}
|
|
2095
|
-
}
|
|
2096
|
-
}
|
|
2097
|
-
|
|
2098
|
-
// Open list vs concrete list
|
|
2099
|
-
if (a instanceof OpenListTerm && b instanceof ListTerm) {
|
|
2100
|
-
return unifyOpenWithList(a.prefix, a.tailVar, b.elems, subst);
|
|
2101
|
-
}
|
|
2102
|
-
if (a instanceof ListTerm && b instanceof OpenListTerm) {
|
|
2103
|
-
return unifyOpenWithList(b.prefix, b.tailVar, a.elems, subst);
|
|
2104
|
-
}
|
|
2105
2108
|
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
// List terms
|
|
2118
|
-
if (a instanceof ListTerm && b instanceof ListTerm) {
|
|
2119
|
-
if (a.elems.length !== b.elems.length) return null;
|
|
2120
|
-
let s2 = { ...subst };
|
|
2121
|
-
for (let i = 0; i < a.elems.length; i++) {
|
|
2122
|
-
s2 = unifyTerm(a.elems[i], b.elems[i], s2);
|
|
2123
|
-
if (s2 === null) return null;
|
|
2124
|
-
}
|
|
2125
|
-
return s2;
|
|
2126
|
-
}
|
|
2127
|
-
|
|
2128
|
-
// Formulas:
|
|
2129
|
-
// 1) If they are alpha-equivalent, succeed without leaking internal bindings.
|
|
2130
|
-
// 2) Otherwise fall back to full unification (may bind vars).
|
|
2131
|
-
if (a instanceof FormulaTerm && b instanceof FormulaTerm) {
|
|
2132
|
-
if (alphaEqFormulaTriples(a.triples, b.triples)) return { ...subst };
|
|
2133
|
-
return unifyFormulaTriples(a.triples, b.triples, subst);
|
|
2134
|
-
}
|
|
2135
|
-
return null;
|
|
2136
|
-
}
|
|
2137
|
-
|
|
2138
|
-
function unifyTermListAppend(a, b, subst) {
|
|
2139
|
-
a = applySubstTerm(a, subst);
|
|
2140
|
-
b = applySubstTerm(b, subst);
|
|
2141
|
-
|
|
2142
|
-
// Variable binding (same as unifyTerm)
|
|
2143
|
-
if (a instanceof Var) {
|
|
2144
|
-
const v = a.name;
|
|
2145
|
-
const t = b;
|
|
2146
|
-
if (t instanceof Var && t.name === v) return { ...subst };
|
|
2147
|
-
if (containsVarTerm(t, v)) return null;
|
|
2148
|
-
const s2 = { ...subst };
|
|
2149
|
-
s2[v] = t;
|
|
2150
|
-
return s2;
|
|
2151
|
-
}
|
|
2152
|
-
if (b instanceof Var) return unifyTermListAppend(b, a, subst);
|
|
2153
|
-
|
|
2154
|
-
// Exact matches
|
|
2155
|
-
if (a instanceof Iri && b instanceof Iri && a.value === b.value) return { ...subst };
|
|
2156
|
-
if (a instanceof Literal && b instanceof Literal && a.value === b.value) return { ...subst };
|
|
2157
|
-
if (a instanceof Blank && b instanceof Blank && a.label === b.label) return { ...subst };
|
|
2158
|
-
|
|
2159
|
-
// Plain string vs xsd:string equivalence
|
|
2160
|
-
if (a instanceof Literal && b instanceof Literal) {
|
|
2161
|
-
if (literalsEquivalentAsXsdString(a.value, b.value)) return { ...subst };
|
|
2162
|
-
}
|
|
2163
|
-
|
|
2164
|
-
// Numeric match: same-dt OR integer<->decimal exact equality (for list:append only)
|
|
2165
|
-
if (a instanceof Literal && b instanceof Literal) {
|
|
2166
|
-
const ai = parseNumericLiteralInfo(a);
|
|
2167
|
-
const bi = parseNumericLiteralInfo(b);
|
|
2168
|
-
if (ai && bi) {
|
|
2169
|
-
// same datatype
|
|
2170
|
-
if (ai.dt === bi.dt) {
|
|
2171
|
-
if (ai.kind === 'bigint' && bi.kind === 'bigint') {
|
|
2172
|
-
if (ai.value === bi.value) return { ...subst };
|
|
2173
|
-
} else {
|
|
2174
|
-
const an = ai.kind === 'bigint' ? Number(ai.value) : ai.value;
|
|
2175
|
-
const bn = bi.kind === 'bigint' ? Number(bi.value) : bi.value;
|
|
2176
|
-
if (!Number.isNaN(an) && !Number.isNaN(bn) && an === bn) return { ...subst };
|
|
2177
|
-
}
|
|
2178
|
-
}
|
|
2179
|
-
|
|
2180
|
-
// integer <-> decimal exact equality
|
|
2181
|
-
const intDt = XSD_NS + 'integer';
|
|
2182
|
-
const decDt = XSD_NS + 'decimal';
|
|
2183
|
-
if ((ai.dt === intDt && bi.dt === decDt) || (ai.dt === decDt && bi.dt === intDt)) {
|
|
2184
|
-
const intInfo = ai.dt === intDt ? ai : bi;
|
|
2185
|
-
const decInfo = ai.dt === decDt ? ai : bi;
|
|
2186
|
-
const dec = parseXsdDecimalToBigIntScale(decInfo.lexStr);
|
|
2187
|
-
if (dec) {
|
|
2188
|
-
const scaledInt = intInfo.value * pow10n(dec.scale);
|
|
2189
|
-
if (scaledInt === dec.num) return { ...subst };
|
|
2109
|
+
if (opts.intDecimalEq) {
|
|
2110
|
+
const intDt = XSD_NS + 'integer';
|
|
2111
|
+
const decDt = XSD_NS + 'decimal';
|
|
2112
|
+
if ((ai.dt === intDt && bi.dt === decDt) || (ai.dt === decDt && bi.dt === intDt)) {
|
|
2113
|
+
const intInfo = ai.dt === intDt ? ai : bi; // bigint
|
|
2114
|
+
const decInfo = ai.dt === decDt ? ai : bi; // number + lexStr
|
|
2115
|
+
const dec = parseXsdDecimalToBigIntScale(decInfo.lexStr);
|
|
2116
|
+
if (dec) {
|
|
2117
|
+
const scaledInt = intInfo.value * pow10n(dec.scale);
|
|
2118
|
+
if (scaledInt === dec.num) return { ...subst };
|
|
2119
|
+
}
|
|
2190
2120
|
}
|
|
2191
2121
|
}
|
|
2192
2122
|
}
|
|
@@ -2205,7 +2135,7 @@ function unifyTermListAppend(a, b, subst) {
|
|
|
2205
2135
|
if (a.tailVar !== b.tailVar || a.prefix.length !== b.prefix.length) return null;
|
|
2206
2136
|
let s2 = { ...subst };
|
|
2207
2137
|
for (let i = 0; i < a.prefix.length; i++) {
|
|
2208
|
-
s2 =
|
|
2138
|
+
s2 = unifyTermWithOptions(a.prefix[i], b.prefix[i], s2, opts);
|
|
2209
2139
|
if (s2 === null) return null;
|
|
2210
2140
|
}
|
|
2211
2141
|
return s2;
|
|
@@ -2216,7 +2146,7 @@ function unifyTermListAppend(a, b, subst) {
|
|
|
2216
2146
|
if (a.elems.length !== b.elems.length) return null;
|
|
2217
2147
|
let s2 = { ...subst };
|
|
2218
2148
|
for (let i = 0; i < a.elems.length; i++) {
|
|
2219
|
-
s2 =
|
|
2149
|
+
s2 = unifyTermWithOptions(a.elems[i], b.elems[i], s2, opts);
|
|
2220
2150
|
if (s2 === null) return null;
|
|
2221
2151
|
}
|
|
2222
2152
|
return s2;
|
|
@@ -2598,6 +2528,10 @@ function parseXsdFloatSpecialLex(s) {
|
|
|
2598
2528
|
return null;
|
|
2599
2529
|
}
|
|
2600
2530
|
|
|
2531
|
+
// ============================================================================
|
|
2532
|
+
// Math builtin helpers
|
|
2533
|
+
// ============================================================================
|
|
2534
|
+
|
|
2601
2535
|
function formatXsdFloatSpecialLex(n) {
|
|
2602
2536
|
if (n === Infinity) return 'INF';
|
|
2603
2537
|
if (n === -Infinity) return '-INF';
|
|
@@ -2752,6 +2686,10 @@ function pow10n(k) {
|
|
|
2752
2686
|
return 10n ** BigInt(k);
|
|
2753
2687
|
}
|
|
2754
2688
|
|
|
2689
|
+
// ============================================================================
|
|
2690
|
+
// Time & duration builtin helpers
|
|
2691
|
+
// ============================================================================
|
|
2692
|
+
|
|
2755
2693
|
function parseXsdDateTerm(t) {
|
|
2756
2694
|
if (!(t instanceof Literal)) return null;
|
|
2757
2695
|
const [lex, dt] = literalParts(t.value);
|
|
@@ -2833,7 +2771,7 @@ function parseNumericForCompareTerm(t) {
|
|
|
2833
2771
|
}
|
|
2834
2772
|
|
|
2835
2773
|
function cmpNumericInfo(aInfo, bInfo, op) {
|
|
2836
|
-
// op is one of ">", "<", ">=", "<="
|
|
2774
|
+
// op is one of ">", "<", ">=", "<=", "==", "!="
|
|
2837
2775
|
if (!aInfo || !bInfo) return false;
|
|
2838
2776
|
|
|
2839
2777
|
if (aInfo.kind === 'bigint' && bInfo.kind === 'bigint') {
|
|
@@ -2858,6 +2796,19 @@ function cmpNumericInfo(aInfo, bInfo, op) {
|
|
|
2858
2796
|
return false;
|
|
2859
2797
|
}
|
|
2860
2798
|
|
|
2799
|
+
function evalNumericComparisonBuiltin(g, subst, op) {
|
|
2800
|
+
const aInfo = parseNumericForCompareTerm(g.s);
|
|
2801
|
+
const bInfo = parseNumericForCompareTerm(g.o);
|
|
2802
|
+
if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, op)) return [{ ...subst }];
|
|
2803
|
+
|
|
2804
|
+
if (g.s instanceof ListTerm && g.s.elems.length === 2) {
|
|
2805
|
+
const a2 = parseNumericForCompareTerm(g.s.elems[0]);
|
|
2806
|
+
const b2 = parseNumericForCompareTerm(g.s.elems[1]);
|
|
2807
|
+
if (a2 && b2 && cmpNumericInfo(a2, b2, op)) return [{ ...subst }];
|
|
2808
|
+
}
|
|
2809
|
+
return [];
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2861
2812
|
function parseNumOrDuration(t) {
|
|
2862
2813
|
const n = parseNum(t);
|
|
2863
2814
|
if (n !== null) return n;
|
|
@@ -2886,35 +2837,10 @@ function parseNumOrDuration(t) {
|
|
|
2886
2837
|
|
|
2887
2838
|
function formatDurationLiteralFromSeconds(secs) {
|
|
2888
2839
|
const neg = secs < 0;
|
|
2889
|
-
const
|
|
2890
|
-
const days = Math.round(absSecs / 86400.0);
|
|
2891
|
-
const lex = neg ? `" -P${days}D"` : `"P${days}D"`;
|
|
2892
|
-
const cleanLex = neg ? `" -P${days}D"` : `"P${days}D"`; // minor detail; we just follow shape
|
|
2893
|
-
const lex2 = neg ? `" -P${days}D"` : `"P${days}D"`;
|
|
2894
|
-
const actualLex = neg ? `" -P${days}D"` : `"P${days}D"`;
|
|
2895
|
-
// keep simpler, no spaces:
|
|
2896
|
-
const finalLex = neg ? `" -P${days}D"` : `"P${days}D"`;
|
|
2840
|
+
const days = Math.round(Math.abs(secs) / 86400.0);
|
|
2897
2841
|
const literalLex = neg ? `"-P${days}D"` : `"P${days}D"`;
|
|
2898
2842
|
return new Literal(`${literalLex}^^<${XSD_NS}duration>`);
|
|
2899
2843
|
}
|
|
2900
|
-
|
|
2901
|
-
function listAppendSplit(parts, resElems, subst) {
|
|
2902
|
-
if (!parts.length) {
|
|
2903
|
-
if (!resElems.length) return [{ ...subst }];
|
|
2904
|
-
return [];
|
|
2905
|
-
}
|
|
2906
|
-
const out = [];
|
|
2907
|
-
const n = resElems.length;
|
|
2908
|
-
for (let k = 0; k <= n; k++) {
|
|
2909
|
-
const left = new ListTerm(resElems.slice(0, k));
|
|
2910
|
-
let s1 = unifyTermListAppend(parts[0], left, subst);
|
|
2911
|
-
if (s1 === null) continue;
|
|
2912
|
-
const restElems = resElems.slice(k);
|
|
2913
|
-
out.push(...listAppendSplit(parts.slice(1), restElems, s1));
|
|
2914
|
-
}
|
|
2915
|
-
return out;
|
|
2916
|
-
}
|
|
2917
|
-
|
|
2918
2844
|
function numEqualTerm(t, n, eps = 1e-9) {
|
|
2919
2845
|
const v = parseNum(t);
|
|
2920
2846
|
if (v === null) return false;
|
|
@@ -3092,174 +3018,138 @@ function evalUnaryMathRel(g, subst, forwardFn, inverseFn /* may be null */) {
|
|
|
3092
3018
|
}
|
|
3093
3019
|
|
|
3094
3020
|
// ============================================================================
|
|
3095
|
-
//
|
|
3021
|
+
// List builtin helpers
|
|
3096
3022
|
// ============================================================================
|
|
3097
3023
|
|
|
3098
|
-
function
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
// Accept only literals, interpret lexical form as UTF-8 string
|
|
3103
|
-
if (!(t instanceof Literal)) return null;
|
|
3104
|
-
const [lex, _dt] = literalParts(t.value);
|
|
3105
|
-
const input = stripQuotes(lex);
|
|
3106
|
-
try {
|
|
3107
|
-
const digest = nodeCrypto.createHash(algo).update(input, 'utf8').digest('hex');
|
|
3108
|
-
// plain string literal with the hex digest
|
|
3109
|
-
return new Literal(JSON.stringify(digest));
|
|
3110
|
-
} catch (e) {
|
|
3111
|
-
return null;
|
|
3112
|
-
}
|
|
3024
|
+
function listAppendSplit(parts, resElems, subst) {
|
|
3025
|
+
if (!parts.length) {
|
|
3026
|
+
if (!resElems.length) return [{ ...subst }];
|
|
3027
|
+
return [];
|
|
3113
3028
|
}
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
const lit = hashLiteral(g.s, 'sha1');
|
|
3123
|
-
if (!lit) return [];
|
|
3124
|
-
if (g.o instanceof Var) {
|
|
3125
|
-
const s2 = { ...subst };
|
|
3126
|
-
s2[g.o.name] = lit;
|
|
3127
|
-
return [s2];
|
|
3128
|
-
}
|
|
3129
|
-
const s2 = unifyTerm(g.o, lit, subst);
|
|
3130
|
-
return s2 !== null ? [s2] : [];
|
|
3029
|
+
const out = [];
|
|
3030
|
+
const n = resElems.length;
|
|
3031
|
+
for (let k = 0; k <= n; k++) {
|
|
3032
|
+
const left = new ListTerm(resElems.slice(0, k));
|
|
3033
|
+
let s1 = unifyTermListAppend(parts[0], left, subst);
|
|
3034
|
+
if (s1 === null) continue;
|
|
3035
|
+
const restElems = resElems.slice(k);
|
|
3036
|
+
out.push(...listAppendSplit(parts.slice(1), restElems, s1));
|
|
3131
3037
|
}
|
|
3038
|
+
return out;
|
|
3039
|
+
}
|
|
3132
3040
|
|
|
3133
|
-
|
|
3134
|
-
if (
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
return [s2];
|
|
3141
|
-
}
|
|
3142
|
-
const s2 = unifyTerm(g.o, lit, subst);
|
|
3143
|
-
return s2 !== null ? [s2] : [];
|
|
3144
|
-
}
|
|
3041
|
+
function evalListFirstLikeBuiltin(sTerm, oTerm, subst) {
|
|
3042
|
+
if (!(sTerm instanceof ListTerm)) return [];
|
|
3043
|
+
if (!sTerm.elems.length) return [];
|
|
3044
|
+
const first = sTerm.elems[0];
|
|
3045
|
+
const s2 = unifyTerm(oTerm, first, subst);
|
|
3046
|
+
return s2 !== null ? [s2] : [];
|
|
3047
|
+
}
|
|
3145
3048
|
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
if (!
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
s2[g.o.name] = lit;
|
|
3153
|
-
return [s2];
|
|
3154
|
-
}
|
|
3155
|
-
const s2 = unifyTerm(g.o, lit, subst);
|
|
3049
|
+
function evalListRestLikeBuiltin(sTerm, oTerm, subst) {
|
|
3050
|
+
// Closed list: (a b c) -> (b c)
|
|
3051
|
+
if (sTerm instanceof ListTerm) {
|
|
3052
|
+
if (!sTerm.elems.length) return [];
|
|
3053
|
+
const rest = new ListTerm(sTerm.elems.slice(1));
|
|
3054
|
+
const s2 = unifyTerm(oTerm, rest, subst);
|
|
3156
3055
|
return s2 !== null ? [s2] : [];
|
|
3157
3056
|
}
|
|
3158
3057
|
|
|
3159
|
-
//
|
|
3160
|
-
if (
|
|
3161
|
-
|
|
3162
|
-
if (
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
s2[g.o.name] = lit;
|
|
3166
|
-
return [s2];
|
|
3058
|
+
// Open list: (a b ... ?T) -> (b ... ?T)
|
|
3059
|
+
if (sTerm instanceof OpenListTerm) {
|
|
3060
|
+
if (!sTerm.prefix.length) return [];
|
|
3061
|
+
if (sTerm.prefix.length === 1) {
|
|
3062
|
+
const s2 = unifyTerm(oTerm, new Var(sTerm.tailVar), subst);
|
|
3063
|
+
return s2 !== null ? [s2] : [];
|
|
3167
3064
|
}
|
|
3168
|
-
const
|
|
3065
|
+
const rest = new OpenListTerm(sTerm.prefix.slice(1), sTerm.tailVar);
|
|
3066
|
+
const s2 = unifyTerm(oTerm, rest, subst);
|
|
3169
3067
|
return s2 !== null ? [s2] : [];
|
|
3170
3068
|
}
|
|
3171
3069
|
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
// -----------------------------------------------------------------
|
|
3175
|
-
|
|
3176
|
-
// math:greaterThan
|
|
3177
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS + 'greaterThan') {
|
|
3178
|
-
const aInfo = parseNumericForCompareTerm(g.s);
|
|
3179
|
-
const bInfo = parseNumericForCompareTerm(g.o);
|
|
3180
|
-
if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, '>')) return [{ ...subst }];
|
|
3181
|
-
|
|
3182
|
-
if (g.s instanceof ListTerm && g.s.elems.length === 2) {
|
|
3183
|
-
const a2 = parseNumericForCompareTerm(g.s.elems[0]);
|
|
3184
|
-
const b2 = parseNumericForCompareTerm(g.s.elems[1]);
|
|
3185
|
-
if (a2 && b2 && cmpNumericInfo(a2, b2, '>')) return [{ ...subst }];
|
|
3186
|
-
}
|
|
3187
|
-
return [];
|
|
3188
|
-
}
|
|
3070
|
+
return [];
|
|
3071
|
+
}
|
|
3189
3072
|
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
const bInfo = parseNumericForCompareTerm(g.o);
|
|
3194
|
-
if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, '<')) return [{ ...subst }];
|
|
3073
|
+
// ============================================================================
|
|
3074
|
+
// Crypto builtin helpers
|
|
3075
|
+
// ============================================================================
|
|
3195
3076
|
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3077
|
+
function hashLiteralTerm(t, algo) {
|
|
3078
|
+
if (!(t instanceof Literal)) return null;
|
|
3079
|
+
const [lex] = literalParts(t.value);
|
|
3080
|
+
const input = stripQuotes(lex);
|
|
3081
|
+
try {
|
|
3082
|
+
const digest = nodeCrypto.createHash(algo).update(input, 'utf8').digest('hex');
|
|
3083
|
+
return new Literal(JSON.stringify(digest));
|
|
3084
|
+
} catch (e) {
|
|
3085
|
+
return null;
|
|
3202
3086
|
}
|
|
3087
|
+
}
|
|
3203
3088
|
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
const a2 = parseNumericForCompareTerm(g.s.elems[0]);
|
|
3212
|
-
const b2 = parseNumericForCompareTerm(g.s.elems[1]);
|
|
3213
|
-
if (a2 && b2 && cmpNumericInfo(a2, b2, '>=')) return [{ ...subst }];
|
|
3214
|
-
}
|
|
3215
|
-
return [];
|
|
3089
|
+
function evalCryptoHashBuiltin(g, subst, algo) {
|
|
3090
|
+
const lit = hashLiteralTerm(g.s, algo);
|
|
3091
|
+
if (!lit) return [];
|
|
3092
|
+
if (g.o instanceof Var) {
|
|
3093
|
+
const s2 = { ...subst };
|
|
3094
|
+
s2[g.o.name] = lit;
|
|
3095
|
+
return [s2];
|
|
3216
3096
|
}
|
|
3097
|
+
const s2 = unifyTerm(g.o, lit, subst);
|
|
3098
|
+
return s2 !== null ? [s2] : [];
|
|
3099
|
+
}
|
|
3217
3100
|
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, '<=')) return [{ ...subst }];
|
|
3101
|
+
// ============================================================================
|
|
3102
|
+
// Builtin evaluation
|
|
3103
|
+
// ============================================================================
|
|
3104
|
+
// Backward proof & builtins mutual recursion — declarations first
|
|
3223
3105
|
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
}
|
|
3229
|
-
return [];
|
|
3230
|
-
}
|
|
3106
|
+
function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
3107
|
+
const g = applySubstTriple(goal, subst);
|
|
3108
|
+
const pv = iriValue(g.p);
|
|
3109
|
+
if (pv === null) return null;
|
|
3231
3110
|
|
|
3232
|
-
//
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
const bInfo = parseNumericForCompareTerm(g.o);
|
|
3236
|
-
if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, '==')) return [{ ...subst }];
|
|
3111
|
+
// -----------------------------------------------------------------
|
|
3112
|
+
// 4.1 crypto: builtins
|
|
3113
|
+
// -----------------------------------------------------------------
|
|
3237
3114
|
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3115
|
+
// crypto:sha, crypto:md5, crypto:sha256, crypto:sha512
|
|
3116
|
+
// Digest builtins. crypto:sha uses SHA-1 per the N3/crypto convention.
|
|
3117
|
+
const cryptoAlgo =
|
|
3118
|
+
pv === CRYPTO_NS + 'sha'
|
|
3119
|
+
? 'sha1'
|
|
3120
|
+
: pv === CRYPTO_NS + 'md5'
|
|
3121
|
+
? 'md5'
|
|
3122
|
+
: pv === CRYPTO_NS + 'sha256'
|
|
3123
|
+
? 'sha256'
|
|
3124
|
+
: pv === CRYPTO_NS + 'sha512'
|
|
3125
|
+
? 'sha512'
|
|
3126
|
+
: null;
|
|
3127
|
+
if (cryptoAlgo) return evalCryptoHashBuiltin(g, subst, cryptoAlgo);
|
|
3245
3128
|
|
|
3246
|
-
//
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
const bInfo = parseNumericForCompareTerm(g.o);
|
|
3250
|
-
if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, '!=')) return [{ ...subst }];
|
|
3129
|
+
// -----------------------------------------------------------------
|
|
3130
|
+
// 4.2 math: builtins
|
|
3131
|
+
// -----------------------------------------------------------------
|
|
3251
3132
|
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3133
|
+
// math:greaterThan / lessThan / notLessThan / notGreaterThan / equalTo / notEqualTo
|
|
3134
|
+
const mathCmpOp =
|
|
3135
|
+
pv === MATH_NS + 'greaterThan'
|
|
3136
|
+
? '>'
|
|
3137
|
+
: pv === MATH_NS + 'lessThan'
|
|
3138
|
+
? '<'
|
|
3139
|
+
: pv === MATH_NS + 'notLessThan'
|
|
3140
|
+
? '>='
|
|
3141
|
+
: pv === MATH_NS + 'notGreaterThan'
|
|
3142
|
+
? '<='
|
|
3143
|
+
: pv === MATH_NS + 'equalTo'
|
|
3144
|
+
? '=='
|
|
3145
|
+
: pv === MATH_NS + 'notEqualTo'
|
|
3146
|
+
? '!='
|
|
3147
|
+
: null;
|
|
3148
|
+
if (mathCmpOp) return evalNumericComparisonBuiltin(g, subst, mathCmpOp);
|
|
3259
3149
|
|
|
3260
3150
|
// math:sum
|
|
3261
3151
|
// Schema: ( $s.i+ )+ math:sum $o-
|
|
3262
|
-
if (
|
|
3152
|
+
if (pv === MATH_NS + 'sum') {
|
|
3263
3153
|
if (!(g.s instanceof ListTerm) || g.s.elems.length < 2) return [];
|
|
3264
3154
|
const xs = g.s.elems;
|
|
3265
3155
|
|
|
@@ -3313,7 +3203,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3313
3203
|
|
|
3314
3204
|
// math:product
|
|
3315
3205
|
// Schema: ( $s.i+ )+ math:product $o-
|
|
3316
|
-
if (
|
|
3206
|
+
if (pv === MATH_NS + 'product') {
|
|
3317
3207
|
if (!(g.s instanceof ListTerm) || g.s.elems.length < 2) return [];
|
|
3318
3208
|
const xs = g.s.elems;
|
|
3319
3209
|
|
|
@@ -3365,7 +3255,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3365
3255
|
|
|
3366
3256
|
// math:difference
|
|
3367
3257
|
// Schema: ( $s.1+ $s.2+ )+ math:difference $o-
|
|
3368
|
-
if (
|
|
3258
|
+
if (pv === MATH_NS + 'difference') {
|
|
3369
3259
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3370
3260
|
const [a0, b0] = g.s.elems;
|
|
3371
3261
|
|
|
@@ -3453,7 +3343,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3453
3343
|
|
|
3454
3344
|
// math:quotient
|
|
3455
3345
|
// Schema: ( $s.1+ $s.2+ )+ math:quotient $o-
|
|
3456
|
-
if (
|
|
3346
|
+
if (pv === MATH_NS + 'quotient') {
|
|
3457
3347
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3458
3348
|
const [a0, b0] = g.s.elems;
|
|
3459
3349
|
|
|
@@ -3482,7 +3372,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3482
3372
|
// math:integerQuotient
|
|
3483
3373
|
// Schema: ( $a $b ) math:integerQuotient $q
|
|
3484
3374
|
// Cwm: divide first integer by second integer, ignoring remainder. :contentReference[oaicite:1]{index=1}
|
|
3485
|
-
if (
|
|
3375
|
+
if (pv === MATH_NS + 'integerQuotient') {
|
|
3486
3376
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3487
3377
|
const [a0, b0] = g.s.elems;
|
|
3488
3378
|
|
|
@@ -3536,7 +3426,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3536
3426
|
}
|
|
3537
3427
|
|
|
3538
3428
|
// math:exponentiation
|
|
3539
|
-
if (
|
|
3429
|
+
if (pv === MATH_NS + 'exponentiation') {
|
|
3540
3430
|
if (g.s instanceof ListTerm && g.s.elems.length === 2) {
|
|
3541
3431
|
const baseTerm = g.s.elems[0];
|
|
3542
3432
|
const expTerm = g.s.elems[1];
|
|
@@ -3583,7 +3473,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3583
3473
|
}
|
|
3584
3474
|
|
|
3585
3475
|
// math:absoluteValue
|
|
3586
|
-
if (
|
|
3476
|
+
if (pv === MATH_NS + 'absoluteValue') {
|
|
3587
3477
|
const a = parseNum(g.s);
|
|
3588
3478
|
if (a === null) return [];
|
|
3589
3479
|
|
|
@@ -3604,58 +3494,58 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3604
3494
|
}
|
|
3605
3495
|
|
|
3606
3496
|
// math:acos
|
|
3607
|
-
if (
|
|
3497
|
+
if (pv === MATH_NS + 'acos') {
|
|
3608
3498
|
return evalUnaryMathRel(g, subst, Math.acos, Math.cos);
|
|
3609
3499
|
}
|
|
3610
3500
|
|
|
3611
3501
|
// math:asin
|
|
3612
|
-
if (
|
|
3502
|
+
if (pv === MATH_NS + 'asin') {
|
|
3613
3503
|
return evalUnaryMathRel(g, subst, Math.asin, Math.sin);
|
|
3614
3504
|
}
|
|
3615
3505
|
|
|
3616
3506
|
// math:atan
|
|
3617
|
-
if (
|
|
3507
|
+
if (pv === MATH_NS + 'atan') {
|
|
3618
3508
|
return evalUnaryMathRel(g, subst, Math.atan, Math.tan);
|
|
3619
3509
|
}
|
|
3620
3510
|
|
|
3621
3511
|
// math:sin (inverse uses principal asin)
|
|
3622
|
-
if (
|
|
3512
|
+
if (pv === MATH_NS + 'sin') {
|
|
3623
3513
|
return evalUnaryMathRel(g, subst, Math.sin, Math.asin);
|
|
3624
3514
|
}
|
|
3625
3515
|
|
|
3626
3516
|
// math:cos (inverse uses principal acos)
|
|
3627
|
-
if (
|
|
3517
|
+
if (pv === MATH_NS + 'cos') {
|
|
3628
3518
|
return evalUnaryMathRel(g, subst, Math.cos, Math.acos);
|
|
3629
3519
|
}
|
|
3630
3520
|
|
|
3631
3521
|
// math:tan (inverse uses principal atan)
|
|
3632
|
-
if (
|
|
3522
|
+
if (pv === MATH_NS + 'tan') {
|
|
3633
3523
|
return evalUnaryMathRel(g, subst, Math.tan, Math.atan);
|
|
3634
3524
|
}
|
|
3635
3525
|
|
|
3636
3526
|
// math:sinh / cosh / tanh (guard for JS availability)
|
|
3637
|
-
if (
|
|
3527
|
+
if (pv === MATH_NS + 'sinh') {
|
|
3638
3528
|
if (typeof Math.sinh !== 'function' || typeof Math.asinh !== 'function') return [];
|
|
3639
3529
|
return evalUnaryMathRel(g, subst, Math.sinh, Math.asinh);
|
|
3640
3530
|
}
|
|
3641
|
-
if (
|
|
3531
|
+
if (pv === MATH_NS + 'cosh') {
|
|
3642
3532
|
if (typeof Math.cosh !== 'function' || typeof Math.acosh !== 'function') return [];
|
|
3643
3533
|
return evalUnaryMathRel(g, subst, Math.cosh, Math.acosh);
|
|
3644
3534
|
}
|
|
3645
|
-
if (
|
|
3535
|
+
if (pv === MATH_NS + 'tanh') {
|
|
3646
3536
|
if (typeof Math.tanh !== 'function' || typeof Math.atanh !== 'function') return [];
|
|
3647
3537
|
return evalUnaryMathRel(g, subst, Math.tanh, Math.atanh);
|
|
3648
3538
|
}
|
|
3649
3539
|
|
|
3650
3540
|
// math:degrees (inverse is radians)
|
|
3651
|
-
if (
|
|
3541
|
+
if (pv === MATH_NS + 'degrees') {
|
|
3652
3542
|
const toDeg = (rad) => (rad * 180.0) / Math.PI;
|
|
3653
3543
|
const toRad = (deg) => (deg * Math.PI) / 180.0;
|
|
3654
3544
|
return evalUnaryMathRel(g, subst, toDeg, toRad);
|
|
3655
3545
|
}
|
|
3656
3546
|
|
|
3657
3547
|
// math:negation (inverse is itself)
|
|
3658
|
-
if (
|
|
3548
|
+
if (pv === MATH_NS + 'negation') {
|
|
3659
3549
|
const neg = (x) => -x;
|
|
3660
3550
|
return evalUnaryMathRel(g, subst, neg, neg);
|
|
3661
3551
|
}
|
|
@@ -3663,7 +3553,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3663
3553
|
// math:remainder
|
|
3664
3554
|
// Subject is a list (dividend divisor); object is the remainder.
|
|
3665
3555
|
// Schema: ( $a $b ) math:remainder $r
|
|
3666
|
-
if (
|
|
3556
|
+
if (pv === MATH_NS + 'remainder') {
|
|
3667
3557
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3668
3558
|
const [a0, b0] = g.s.elems;
|
|
3669
3559
|
|
|
@@ -3713,7 +3603,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3713
3603
|
// If there are two such numbers, then the one closest to positive infinity is returned.
|
|
3714
3604
|
// Schema: $s+ math:rounded $o-
|
|
3715
3605
|
// Note: spec says $o is xsd:integer, but we also accept any numeric $o that equals the rounded value.
|
|
3716
|
-
if (
|
|
3606
|
+
if (pv === MATH_NS + 'rounded') {
|
|
3717
3607
|
const a = parseNum(g.s);
|
|
3718
3608
|
if (a === null) return [];
|
|
3719
3609
|
if (Number.isNaN(a)) return [];
|
|
@@ -3742,7 +3632,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3742
3632
|
|
|
3743
3633
|
// time:localTime
|
|
3744
3634
|
// "" time:localTime ?D. binds ?D to “now” as xsd:dateTime.
|
|
3745
|
-
if (
|
|
3635
|
+
if (pv === TIME_NS + 'localTime') {
|
|
3746
3636
|
const now = getNowLex();
|
|
3747
3637
|
|
|
3748
3638
|
if (g.o instanceof Var) {
|
|
@@ -3764,7 +3654,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3764
3654
|
// list:append
|
|
3765
3655
|
// true if and only if $o is the concatenation of all lists $s.i.
|
|
3766
3656
|
// Schema: ( $s.i?[*] )+ list:append $o?
|
|
3767
|
-
if (
|
|
3657
|
+
if (pv === LIST_NS + 'append') {
|
|
3768
3658
|
if (!(g.s instanceof ListTerm)) return [];
|
|
3769
3659
|
const parts = g.s.elems;
|
|
3770
3660
|
if (g.o instanceof ListTerm) {
|
|
@@ -3785,86 +3675,25 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3785
3675
|
return [];
|
|
3786
3676
|
}
|
|
3787
3677
|
|
|
3788
|
-
// list:first
|
|
3678
|
+
// list:first and rdf:first
|
|
3789
3679
|
// true iff $s is a list and $o is the first member of that list.
|
|
3790
3680
|
// Schema: $s+ list:first $o-
|
|
3791
|
-
if (
|
|
3792
|
-
|
|
3793
|
-
if (!g.s.elems.length) return [];
|
|
3794
|
-
const first = g.s.elems[0];
|
|
3795
|
-
const s2 = unifyTerm(g.o, first, subst);
|
|
3796
|
-
return s2 !== null ? [s2] : [];
|
|
3681
|
+
if (pv === LIST_NS + 'first' || pv === RDF_NS + 'first') {
|
|
3682
|
+
return evalListFirstLikeBuiltin(g.s, g.o, subst);
|
|
3797
3683
|
}
|
|
3798
3684
|
|
|
3799
|
-
// list:rest
|
|
3685
|
+
// list:rest and rdf:rest
|
|
3800
3686
|
// true iff $s is a (non-empty) list and $o is the rest (tail) of that list.
|
|
3801
3687
|
// Schema: $s+ list:rest $o-
|
|
3802
|
-
if (
|
|
3803
|
-
|
|
3804
|
-
if (g.s instanceof ListTerm) {
|
|
3805
|
-
if (!g.s.elems.length) return [];
|
|
3806
|
-
const rest = new ListTerm(g.s.elems.slice(1));
|
|
3807
|
-
const s2 = unifyTerm(g.o, rest, subst);
|
|
3808
|
-
return s2 !== null ? [s2] : [];
|
|
3809
|
-
}
|
|
3810
|
-
|
|
3811
|
-
// Open list: (a b ... ?T) -> (b ... ?T)
|
|
3812
|
-
if (g.s instanceof OpenListTerm) {
|
|
3813
|
-
if (!g.s.prefix.length) return []; // can't compute rest without a known head
|
|
3814
|
-
|
|
3815
|
-
if (g.s.prefix.length === 1) {
|
|
3816
|
-
// (a ... ?T) rest is exactly ?T
|
|
3817
|
-
const s2 = unifyTerm(g.o, new Var(g.s.tailVar), subst);
|
|
3818
|
-
return s2 !== null ? [s2] : [];
|
|
3819
|
-
}
|
|
3820
|
-
|
|
3821
|
-
const rest = new OpenListTerm(g.s.prefix.slice(1), g.s.tailVar);
|
|
3822
|
-
const s2 = unifyTerm(g.o, rest, subst);
|
|
3823
|
-
return s2 !== null ? [s2] : [];
|
|
3824
|
-
}
|
|
3825
|
-
|
|
3826
|
-
return [];
|
|
3827
|
-
}
|
|
3828
|
-
|
|
3829
|
-
// rdf:first (alias of list:first)
|
|
3830
|
-
// Schema: $s+ rdf:first $o-
|
|
3831
|
-
if (g.p instanceof Iri && g.p.value === RDF_NS + 'first') {
|
|
3832
|
-
if (!(g.s instanceof ListTerm)) return [];
|
|
3833
|
-
if (!g.s.elems.length) return [];
|
|
3834
|
-
const first = g.s.elems[0];
|
|
3835
|
-
const s2 = unifyTerm(g.o, first, subst);
|
|
3836
|
-
return s2 !== null ? [s2] : [];
|
|
3837
|
-
}
|
|
3838
|
-
|
|
3839
|
-
// rdf:rest (alias of list:rest)
|
|
3840
|
-
// Schema: $s+ rdf:rest $o-
|
|
3841
|
-
if (g.p instanceof Iri && g.p.value === RDF_NS + 'rest') {
|
|
3842
|
-
// Closed list: (a b c) -> (b c)
|
|
3843
|
-
if (g.s instanceof ListTerm) {
|
|
3844
|
-
if (!g.s.elems.length) return [];
|
|
3845
|
-
const rest = new ListTerm(g.s.elems.slice(1));
|
|
3846
|
-
const s2 = unifyTerm(g.o, rest, subst);
|
|
3847
|
-
return s2 !== null ? [s2] : [];
|
|
3848
|
-
}
|
|
3849
|
-
// Open list: (a b ... ?T) -> (b ... ?T)
|
|
3850
|
-
if (g.s instanceof OpenListTerm) {
|
|
3851
|
-
if (!g.s.prefix.length) return [];
|
|
3852
|
-
if (g.s.prefix.length === 1) {
|
|
3853
|
-
const s2 = unifyTerm(g.o, new Var(g.s.tailVar), subst);
|
|
3854
|
-
return s2 !== null ? [s2] : [];
|
|
3855
|
-
}
|
|
3856
|
-
const rest = new OpenListTerm(g.s.prefix.slice(1), g.s.tailVar);
|
|
3857
|
-
const s2 = unifyTerm(g.o, rest, subst);
|
|
3858
|
-
return s2 !== null ? [s2] : [];
|
|
3859
|
-
}
|
|
3860
|
-
return [];
|
|
3688
|
+
if (pv === LIST_NS + 'rest' || pv === RDF_NS + 'rest') {
|
|
3689
|
+
return evalListRestLikeBuiltin(g.s, g.o, subst);
|
|
3861
3690
|
}
|
|
3862
3691
|
|
|
3863
3692
|
// list:iterate
|
|
3864
3693
|
// Multi-solution builtin:
|
|
3865
3694
|
// For a list subject $s, generate solutions by unifying $o with (index value).
|
|
3866
3695
|
// This allows $o to be a variable (e.g., ?Y) or a pattern (e.g., (?i "Dewey")).
|
|
3867
|
-
if (
|
|
3696
|
+
if (pv === LIST_NS + 'iterate') {
|
|
3868
3697
|
if (!(g.s instanceof ListTerm)) return [];
|
|
3869
3698
|
const xs = g.s.elems;
|
|
3870
3699
|
const outs = [];
|
|
@@ -3905,7 +3734,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3905
3734
|
// list:last
|
|
3906
3735
|
// true iff $s is a list and $o is the last member of that list.
|
|
3907
3736
|
// Schema: $s+ list:last $o-
|
|
3908
|
-
if (
|
|
3737
|
+
if (pv === LIST_NS + 'last') {
|
|
3909
3738
|
if (!(g.s instanceof ListTerm)) return [];
|
|
3910
3739
|
const xs = g.s.elems;
|
|
3911
3740
|
if (!xs.length) return [];
|
|
@@ -3917,7 +3746,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3917
3746
|
// list:memberAt
|
|
3918
3747
|
// true iff $s.1 is a list, $s.2 is a valid index, and $o is the member at that index.
|
|
3919
3748
|
// Schema: ( $s.1+ $s.2?[*] )+ list:memberAt $o?[*]
|
|
3920
|
-
if (
|
|
3749
|
+
if (pv === LIST_NS + 'memberAt') {
|
|
3921
3750
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3922
3751
|
const [listTerm, indexTerm] = g.s.elems;
|
|
3923
3752
|
if (!(listTerm instanceof ListTerm)) return [];
|
|
@@ -3956,7 +3785,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3956
3785
|
// list:remove
|
|
3957
3786
|
// true iff $s.1 is a list and $o is that list with all occurrences of $s.2 removed.
|
|
3958
3787
|
// Schema: ( $s.1+ $s.2+ )+ list:remove $o-
|
|
3959
|
-
if (
|
|
3788
|
+
if (pv === LIST_NS + 'remove') {
|
|
3960
3789
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3961
3790
|
const [listTerm, itemTerm] = g.s.elems;
|
|
3962
3791
|
if (!(listTerm instanceof ListTerm)) return [];
|
|
@@ -3978,7 +3807,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3978
3807
|
}
|
|
3979
3808
|
|
|
3980
3809
|
// list:member
|
|
3981
|
-
if (
|
|
3810
|
+
if (pv === LIST_NS + 'member') {
|
|
3982
3811
|
if (!(g.s instanceof ListTerm)) return [];
|
|
3983
3812
|
const outs = [];
|
|
3984
3813
|
for (const x of g.s.elems) {
|
|
@@ -3989,7 +3818,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3989
3818
|
}
|
|
3990
3819
|
|
|
3991
3820
|
// list:in
|
|
3992
|
-
if (
|
|
3821
|
+
if (pv === LIST_NS + 'in') {
|
|
3993
3822
|
if (!(g.o instanceof ListTerm)) return [];
|
|
3994
3823
|
const outs = [];
|
|
3995
3824
|
for (const x of g.o.elems) {
|
|
@@ -4000,7 +3829,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4000
3829
|
}
|
|
4001
3830
|
|
|
4002
3831
|
// list:length (strict: do not accept integer<->decimal matches for a ground object)
|
|
4003
|
-
if (
|
|
3832
|
+
if (pv === LIST_NS + 'length') {
|
|
4004
3833
|
if (!(g.s instanceof ListTerm)) return [];
|
|
4005
3834
|
const nTerm = new Literal(String(g.s.elems.length));
|
|
4006
3835
|
|
|
@@ -4014,7 +3843,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4014
3843
|
}
|
|
4015
3844
|
|
|
4016
3845
|
// list:notMember
|
|
4017
|
-
if (
|
|
3846
|
+
if (pv === LIST_NS + 'notMember') {
|
|
4018
3847
|
if (!(g.s instanceof ListTerm)) return [];
|
|
4019
3848
|
for (const el of g.s.elems) {
|
|
4020
3849
|
if (unifyTerm(g.o, el, subst) !== null) return [];
|
|
@@ -4023,7 +3852,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4023
3852
|
}
|
|
4024
3853
|
|
|
4025
3854
|
// list:reverse
|
|
4026
|
-
if (
|
|
3855
|
+
if (pv === LIST_NS + 'reverse') {
|
|
4027
3856
|
if (g.s instanceof ListTerm) {
|
|
4028
3857
|
const rev = [...g.s.elems].reverse();
|
|
4029
3858
|
const rterm = new ListTerm(rev);
|
|
@@ -4040,7 +3869,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4040
3869
|
}
|
|
4041
3870
|
|
|
4042
3871
|
// list:sort
|
|
4043
|
-
if (
|
|
3872
|
+
if (pv === LIST_NS + 'sort') {
|
|
4044
3873
|
function cmpTermForSort(a, b) {
|
|
4045
3874
|
if (a instanceof Literal && b instanceof Literal) {
|
|
4046
3875
|
const [lexA] = literalParts(a.value);
|
|
@@ -4108,7 +3937,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4108
3937
|
}
|
|
4109
3938
|
|
|
4110
3939
|
// list:map
|
|
4111
|
-
if (
|
|
3940
|
+
if (pv === LIST_NS + 'map') {
|
|
4112
3941
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
4113
3942
|
const [inputTerm, predTerm] = g.s.elems;
|
|
4114
3943
|
if (!(inputTerm instanceof ListTerm)) return [];
|
|
@@ -4134,7 +3963,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4134
3963
|
}
|
|
4135
3964
|
|
|
4136
3965
|
// list:firstRest
|
|
4137
|
-
if (
|
|
3966
|
+
if (pv === LIST_NS + 'firstRest') {
|
|
4138
3967
|
if (g.s instanceof ListTerm) {
|
|
4139
3968
|
if (!g.s.elems.length) return [];
|
|
4140
3969
|
const first = g.s.elems[0];
|
|
@@ -4172,13 +4001,13 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4172
4001
|
// -----------------------------------------------------------------
|
|
4173
4002
|
|
|
4174
4003
|
// log:equalTo
|
|
4175
|
-
if (
|
|
4004
|
+
if (pv === LOG_NS + 'equalTo') {
|
|
4176
4005
|
const s2 = unifyTerm(goal.s, goal.o, subst);
|
|
4177
4006
|
return s2 !== null ? [s2] : [];
|
|
4178
4007
|
}
|
|
4179
4008
|
|
|
4180
4009
|
// log:notEqualTo
|
|
4181
|
-
if (
|
|
4010
|
+
if (pv === LOG_NS + 'notEqualTo') {
|
|
4182
4011
|
const s2 = unifyTerm(goal.s, goal.o, subst);
|
|
4183
4012
|
if (s2 !== null) return [];
|
|
4184
4013
|
return [{ ...subst }];
|
|
@@ -4187,7 +4016,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4187
4016
|
// log:dtlit
|
|
4188
4017
|
// Schema: ( $s.1? $s.2? )? log:dtlit $o?
|
|
4189
4018
|
// true iff $o is a datatyped literal with string value $s.1 and datatype IRI $s.2
|
|
4190
|
-
if (
|
|
4019
|
+
if (pv === LOG_NS + 'dtlit') {
|
|
4191
4020
|
// Fully unbound (both arguments '?'-mode): treat as satisfiable, succeed once.
|
|
4192
4021
|
// Required by notation3tests "success-fullUnbound-*".
|
|
4193
4022
|
if (g.s instanceof Var && g.o instanceof Var) return [{ ...subst }];
|
|
@@ -4240,7 +4069,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4240
4069
|
// log:langlit
|
|
4241
4070
|
// Schema: ( $s.1? $s.2? )? log:langlit $o?
|
|
4242
4071
|
// true iff $o is a language-tagged literal with string value $s.1 and language tag $s.2
|
|
4243
|
-
if (
|
|
4072
|
+
if (pv === LOG_NS + 'langlit') {
|
|
4244
4073
|
// Fully unbound (both arguments '?'-mode): treat as satisfiable, succeed once.
|
|
4245
4074
|
if (g.s instanceof Var && g.o instanceof Var) return [{ ...subst }];
|
|
4246
4075
|
const results = [];
|
|
@@ -4294,7 +4123,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4294
4123
|
}
|
|
4295
4124
|
|
|
4296
4125
|
// log:implies — expose internal forward rules as data
|
|
4297
|
-
if (
|
|
4126
|
+
if (pv === LOG_NS + 'implies') {
|
|
4298
4127
|
const allFw = backRules.__allForwardRules || [];
|
|
4299
4128
|
const results = [];
|
|
4300
4129
|
|
|
@@ -4322,7 +4151,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4322
4151
|
}
|
|
4323
4152
|
|
|
4324
4153
|
// log:impliedBy — expose internal backward rules as data
|
|
4325
|
-
if (
|
|
4154
|
+
if (pv === LOG_NS + 'impliedBy') {
|
|
4326
4155
|
const allBw = backRules.__allBackwardRules || backRules;
|
|
4327
4156
|
const results = [];
|
|
4328
4157
|
|
|
@@ -4352,7 +4181,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4352
4181
|
|
|
4353
4182
|
// log:notIncludes (EYE-style: "not provable in scope")
|
|
4354
4183
|
// Delay until we have a frozen scope snapshot to avoid early success.
|
|
4355
|
-
if (
|
|
4184
|
+
if (pv === LOG_NS + 'notIncludes') {
|
|
4356
4185
|
if (!(g.o instanceof FormulaTerm)) return [];
|
|
4357
4186
|
|
|
4358
4187
|
let scopeFacts = null;
|
|
@@ -4375,7 +4204,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4375
4204
|
}
|
|
4376
4205
|
|
|
4377
4206
|
// log:collectAllIn (scoped)
|
|
4378
|
-
if (
|
|
4207
|
+
if (pv === LOG_NS + 'collectAllIn') {
|
|
4379
4208
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 3) return [];
|
|
4380
4209
|
const [valueTempl, clauseTerm, listTerm] = g.s.elems;
|
|
4381
4210
|
if (!(clauseTerm instanceof FormulaTerm)) return [];
|
|
@@ -4404,7 +4233,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4404
4233
|
}
|
|
4405
4234
|
|
|
4406
4235
|
// log:forAllIn (scoped)
|
|
4407
|
-
if (
|
|
4236
|
+
if (pv === LOG_NS + 'forAllIn') {
|
|
4408
4237
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
4409
4238
|
const [whereClause, thenClause] = g.s.elems;
|
|
4410
4239
|
if (!(whereClause instanceof FormulaTerm) || !(thenClause instanceof FormulaTerm)) return [];
|
|
@@ -4434,7 +4263,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4434
4263
|
}
|
|
4435
4264
|
|
|
4436
4265
|
// log:skolem
|
|
4437
|
-
if (
|
|
4266
|
+
if (pv === LOG_NS + 'skolem') {
|
|
4438
4267
|
// Subject must be ground; commonly a list, but we allow any ground term.
|
|
4439
4268
|
if (!isGroundTerm(g.s)) return [];
|
|
4440
4269
|
|
|
@@ -4451,7 +4280,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4451
4280
|
}
|
|
4452
4281
|
|
|
4453
4282
|
// log:uri
|
|
4454
|
-
if (
|
|
4283
|
+
if (pv === LOG_NS + 'uri') {
|
|
4455
4284
|
// Direction 1: subject is an IRI -> object is its string representation
|
|
4456
4285
|
if (g.s instanceof Iri) {
|
|
4457
4286
|
const uriStr = g.s.value; // raw IRI string, e.g. "https://www.w3.org"
|
|
@@ -4480,7 +4309,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4480
4309
|
// -----------------------------------------------------------------
|
|
4481
4310
|
|
|
4482
4311
|
// string:concatenation
|
|
4483
|
-
if (
|
|
4312
|
+
if (pv === STRING_NS + 'concatenation') {
|
|
4484
4313
|
if (!(g.s instanceof ListTerm)) return [];
|
|
4485
4314
|
const parts = [];
|
|
4486
4315
|
for (const t of g.s.elems) {
|
|
@@ -4500,7 +4329,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4500
4329
|
}
|
|
4501
4330
|
|
|
4502
4331
|
// string:contains
|
|
4503
|
-
if (
|
|
4332
|
+
if (pv === STRING_NS + 'contains') {
|
|
4504
4333
|
const sStr = termToJsString(g.s);
|
|
4505
4334
|
const oStr = termToJsString(g.o);
|
|
4506
4335
|
if (sStr === null || oStr === null) return [];
|
|
@@ -4508,7 +4337,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4508
4337
|
}
|
|
4509
4338
|
|
|
4510
4339
|
// string:containsIgnoringCase
|
|
4511
|
-
if (
|
|
4340
|
+
if (pv === STRING_NS + 'containsIgnoringCase') {
|
|
4512
4341
|
const sStr = termToJsString(g.s);
|
|
4513
4342
|
const oStr = termToJsString(g.o);
|
|
4514
4343
|
if (sStr === null || oStr === null) return [];
|
|
@@ -4516,7 +4345,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4516
4345
|
}
|
|
4517
4346
|
|
|
4518
4347
|
// string:endsWith
|
|
4519
|
-
if (
|
|
4348
|
+
if (pv === STRING_NS + 'endsWith') {
|
|
4520
4349
|
const sStr = termToJsString(g.s);
|
|
4521
4350
|
const oStr = termToJsString(g.o);
|
|
4522
4351
|
if (sStr === null || oStr === null) return [];
|
|
@@ -4524,7 +4353,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4524
4353
|
}
|
|
4525
4354
|
|
|
4526
4355
|
// string:equalIgnoringCase
|
|
4527
|
-
if (
|
|
4356
|
+
if (pv === STRING_NS + 'equalIgnoringCase') {
|
|
4528
4357
|
const sStr = termToJsString(g.s);
|
|
4529
4358
|
const oStr = termToJsString(g.o);
|
|
4530
4359
|
if (sStr === null || oStr === null) return [];
|
|
@@ -4533,7 +4362,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4533
4362
|
|
|
4534
4363
|
// string:format
|
|
4535
4364
|
// (limited: only %s and %% are supported, anything else ⇒ builtin fails)
|
|
4536
|
-
if (
|
|
4365
|
+
if (pv === STRING_NS + 'format') {
|
|
4537
4366
|
if (!(g.s instanceof ListTerm) || g.s.elems.length < 1) return [];
|
|
4538
4367
|
const fmtStr = termToJsString(g.s.elems[0]);
|
|
4539
4368
|
if (fmtStr === null) return [];
|
|
@@ -4560,10 +4389,10 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4560
4389
|
|
|
4561
4390
|
// string:jsonPointer
|
|
4562
4391
|
// Schema: ( $jsonText $pointer ) string:jsonPointer $value
|
|
4563
|
-
if (
|
|
4392
|
+
if (pv === STRING_NS + 'jsonPointer') {
|
|
4564
4393
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
4565
4394
|
|
|
4566
|
-
const jsonText = termToJsonText(g.s.elems[0]);
|
|
4395
|
+
const jsonText = termToJsonText(g.s.elems[0]);
|
|
4567
4396
|
const ptr = termToJsStringDecoded(g.s.elems[1]);
|
|
4568
4397
|
if (jsonText === null || ptr === null) return [];
|
|
4569
4398
|
|
|
@@ -4575,7 +4404,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4575
4404
|
}
|
|
4576
4405
|
|
|
4577
4406
|
// string:greaterThan
|
|
4578
|
-
if (
|
|
4407
|
+
if (pv === STRING_NS + 'greaterThan') {
|
|
4579
4408
|
const sStr = termToJsString(g.s);
|
|
4580
4409
|
const oStr = termToJsString(g.o);
|
|
4581
4410
|
if (sStr === null || oStr === null) return [];
|
|
@@ -4583,7 +4412,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4583
4412
|
}
|
|
4584
4413
|
|
|
4585
4414
|
// string:lessThan
|
|
4586
|
-
if (
|
|
4415
|
+
if (pv === STRING_NS + 'lessThan') {
|
|
4587
4416
|
const sStr = termToJsString(g.s);
|
|
4588
4417
|
const oStr = termToJsString(g.o);
|
|
4589
4418
|
if (sStr === null || oStr === null) return [];
|
|
@@ -4591,7 +4420,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4591
4420
|
}
|
|
4592
4421
|
|
|
4593
4422
|
// string:matches
|
|
4594
|
-
if (
|
|
4423
|
+
if (pv === STRING_NS + 'matches') {
|
|
4595
4424
|
const sStr = termToJsString(g.s);
|
|
4596
4425
|
const pattern = termToJsString(g.o);
|
|
4597
4426
|
if (sStr === null || pattern === null) return [];
|
|
@@ -4606,7 +4435,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4606
4435
|
}
|
|
4607
4436
|
|
|
4608
4437
|
// string:notEqualIgnoringCase
|
|
4609
|
-
if (
|
|
4438
|
+
if (pv === STRING_NS + 'notEqualIgnoringCase') {
|
|
4610
4439
|
const sStr = termToJsString(g.s);
|
|
4611
4440
|
const oStr = termToJsString(g.o);
|
|
4612
4441
|
if (sStr === null || oStr === null) return [];
|
|
@@ -4614,7 +4443,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4614
4443
|
}
|
|
4615
4444
|
|
|
4616
4445
|
// string:notGreaterThan (≤ in Unicode code order)
|
|
4617
|
-
if (
|
|
4446
|
+
if (pv === STRING_NS + 'notGreaterThan') {
|
|
4618
4447
|
const sStr = termToJsString(g.s);
|
|
4619
4448
|
const oStr = termToJsString(g.o);
|
|
4620
4449
|
if (sStr === null || oStr === null) return [];
|
|
@@ -4622,7 +4451,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4622
4451
|
}
|
|
4623
4452
|
|
|
4624
4453
|
// string:notLessThan (≥ in Unicode code order)
|
|
4625
|
-
if (
|
|
4454
|
+
if (pv === STRING_NS + 'notLessThan') {
|
|
4626
4455
|
const sStr = termToJsString(g.s);
|
|
4627
4456
|
const oStr = termToJsString(g.o);
|
|
4628
4457
|
if (sStr === null || oStr === null) return [];
|
|
@@ -4630,7 +4459,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4630
4459
|
}
|
|
4631
4460
|
|
|
4632
4461
|
// string:notMatches
|
|
4633
|
-
if (
|
|
4462
|
+
if (pv === STRING_NS + 'notMatches') {
|
|
4634
4463
|
const sStr = termToJsString(g.s);
|
|
4635
4464
|
const pattern = termToJsString(g.o);
|
|
4636
4465
|
if (sStr === null || pattern === null) return [];
|
|
@@ -4644,7 +4473,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4644
4473
|
}
|
|
4645
4474
|
|
|
4646
4475
|
// string:replace
|
|
4647
|
-
if (
|
|
4476
|
+
if (pv === STRING_NS + 'replace') {
|
|
4648
4477
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 3) return [];
|
|
4649
4478
|
const dataStr = termToJsString(g.s.elems[0]);
|
|
4650
4479
|
const searchStr = termToJsString(g.s.elems[1]);
|
|
@@ -4672,7 +4501,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4672
4501
|
}
|
|
4673
4502
|
|
|
4674
4503
|
// string:scrape
|
|
4675
|
-
if (
|
|
4504
|
+
if (pv === STRING_NS + 'scrape') {
|
|
4676
4505
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
4677
4506
|
const dataStr = termToJsString(g.s.elems[0]);
|
|
4678
4507
|
const pattern = termToJsString(g.s.elems[1]);
|
|
@@ -4701,7 +4530,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4701
4530
|
}
|
|
4702
4531
|
|
|
4703
4532
|
// string:startsWith
|
|
4704
|
-
if (
|
|
4533
|
+
if (pv === STRING_NS + 'startsWith') {
|
|
4705
4534
|
const sStr = termToJsString(g.s);
|
|
4706
4535
|
const oStr = termToJsString(g.o);
|
|
4707
4536
|
if (sStr === null || oStr === null) return [];
|
|
@@ -5065,134 +4894,121 @@ function forwardChain(facts, forwardRules, backRules) {
|
|
|
5065
4894
|
|
|
5066
4895
|
function runFixpoint() {
|
|
5067
4896
|
let anyChange = false;
|
|
4897
|
+
|
|
5068
4898
|
while (true) {
|
|
5069
4899
|
let changed = false;
|
|
5070
4900
|
|
|
5071
|
-
|
|
5072
|
-
|
|
4901
|
+
for (let i = 0; i < forwardRules.length; i++) {
|
|
4902
|
+
const r = forwardRules[i];
|
|
4903
|
+
const empty = {};
|
|
4904
|
+
const visited = [];
|
|
4905
|
+
const sols = proveGoals(r.premise.slice(), empty, facts, backRules, 0, visited, varGen);
|
|
5073
4906
|
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
const sols = proveGoals(r.premise.slice(), empty, facts, backRules, 0, visited, varGen);
|
|
4907
|
+
// Inference fuse
|
|
4908
|
+
if (r.isFuse && sols.length) {
|
|
4909
|
+
console.log('# Inference fuse triggered: a { ... } => false. rule fired.');
|
|
4910
|
+
process.exit(2);
|
|
4911
|
+
}
|
|
5080
4912
|
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
4913
|
+
for (const s of sols) {
|
|
4914
|
+
// IMPORTANT: one skolem map per *rule firing*
|
|
4915
|
+
const skMap = {};
|
|
4916
|
+
const instantiatedPremises = r.premise.map((b) => applySubstTriple(b, s));
|
|
4917
|
+
const fireKey = firingKey(i, instantiatedPremises);
|
|
4918
|
+
|
|
4919
|
+
for (const cpat of r.conclusion) {
|
|
4920
|
+
const instantiated = applySubstTriple(cpat, s);
|
|
4921
|
+
|
|
4922
|
+
const isFwRuleTriple =
|
|
4923
|
+
isLogImplies(instantiated.p) &&
|
|
4924
|
+
((instantiated.s instanceof FormulaTerm && instantiated.o instanceof FormulaTerm) ||
|
|
4925
|
+
(instantiated.s instanceof Literal && instantiated.s.value === 'true' && instantiated.o instanceof FormulaTerm) ||
|
|
4926
|
+
(instantiated.s instanceof FormulaTerm && instantiated.o instanceof Literal && instantiated.o.value === 'true'));
|
|
4927
|
+
|
|
4928
|
+
const isBwRuleTriple =
|
|
4929
|
+
isLogImpliedBy(instantiated.p) &&
|
|
4930
|
+
((instantiated.s instanceof FormulaTerm && instantiated.o instanceof FormulaTerm) ||
|
|
4931
|
+
(instantiated.s instanceof FormulaTerm && instantiated.o instanceof Literal && instantiated.o.value === 'true') ||
|
|
4932
|
+
(instantiated.s instanceof Literal && instantiated.s.value === 'true' && instantiated.o instanceof FormulaTerm));
|
|
4933
|
+
|
|
4934
|
+
if (isFwRuleTriple || isBwRuleTriple) {
|
|
4935
|
+
if (!hasFactIndexed(facts, instantiated)) {
|
|
4936
|
+
factList.push(instantiated);
|
|
4937
|
+
pushFactIndexed(facts, instantiated);
|
|
4938
|
+
derivedForward.push(new DerivedFact(instantiated, r, instantiatedPremises.slice(), { ...s }));
|
|
4939
|
+
changed = true;
|
|
4940
|
+
}
|
|
5086
4941
|
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
const
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
...s,
|
|
5116
|
-
}),
|
|
4942
|
+
// Promote rule-producing triples to live rules, treating literal true as {}.
|
|
4943
|
+
const left =
|
|
4944
|
+
instantiated.s instanceof FormulaTerm
|
|
4945
|
+
? instantiated.s.triples
|
|
4946
|
+
: instantiated.s instanceof Literal && instantiated.s.value === 'true'
|
|
4947
|
+
? []
|
|
4948
|
+
: null;
|
|
4949
|
+
|
|
4950
|
+
const right =
|
|
4951
|
+
instantiated.o instanceof FormulaTerm
|
|
4952
|
+
? instantiated.o.triples
|
|
4953
|
+
: instantiated.o instanceof Literal && instantiated.o.value === 'true'
|
|
4954
|
+
? []
|
|
4955
|
+
: null;
|
|
4956
|
+
|
|
4957
|
+
if (left !== null && right !== null) {
|
|
4958
|
+
if (isFwRuleTriple) {
|
|
4959
|
+
const [premise0, conclusion] = liftBlankRuleVars(left, right);
|
|
4960
|
+
const premise = reorderPremiseForConstraints(premise0);
|
|
4961
|
+
const headBlankLabels = collectBlankLabelsInTriples(conclusion);
|
|
4962
|
+
const newRule = new Rule(premise, conclusion, true, false, headBlankLabels);
|
|
4963
|
+
|
|
4964
|
+
const already = forwardRules.some(
|
|
4965
|
+
(rr) =>
|
|
4966
|
+
rr.isForward === newRule.isForward &&
|
|
4967
|
+
rr.isFuse === newRule.isFuse &&
|
|
4968
|
+
triplesListEqual(rr.premise, newRule.premise) &&
|
|
4969
|
+
triplesListEqual(rr.conclusion, newRule.conclusion),
|
|
5117
4970
|
);
|
|
5118
|
-
|
|
5119
|
-
}
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
: null;
|
|
5135
|
-
|
|
5136
|
-
if (left !== null && right !== null) {
|
|
5137
|
-
if (isFwRuleTriple) {
|
|
5138
|
-
const [premise0, conclusion] = liftBlankRuleVars(left, right);
|
|
5139
|
-
const premise = reorderPremiseForConstraints(premise0);
|
|
5140
|
-
|
|
5141
|
-
const headBlankLabels = collectBlankLabelsInTriples(conclusion);
|
|
5142
|
-
const newRule = new Rule(premise, conclusion, true, false, headBlankLabels);
|
|
5143
|
-
|
|
5144
|
-
const already = forwardRules.some(
|
|
5145
|
-
(rr) =>
|
|
5146
|
-
rr.isForward === newRule.isForward &&
|
|
5147
|
-
rr.isFuse === newRule.isFuse &&
|
|
5148
|
-
triplesListEqual(rr.premise, newRule.premise) &&
|
|
5149
|
-
triplesListEqual(rr.conclusion, newRule.conclusion),
|
|
5150
|
-
);
|
|
5151
|
-
if (!already) forwardRules.push(newRule);
|
|
5152
|
-
} else if (isBwRuleTriple) {
|
|
5153
|
-
const [premise, conclusion] = liftBlankRuleVars(right, left);
|
|
5154
|
-
|
|
5155
|
-
const headBlankLabels = collectBlankLabelsInTriples(conclusion);
|
|
5156
|
-
const newRule = new Rule(premise, conclusion, false, false, headBlankLabels);
|
|
5157
|
-
|
|
5158
|
-
const already = backRules.some(
|
|
5159
|
-
(rr) =>
|
|
5160
|
-
rr.isForward === newRule.isForward &&
|
|
5161
|
-
rr.isFuse === newRule.isFuse &&
|
|
5162
|
-
triplesListEqual(rr.premise, newRule.premise) &&
|
|
5163
|
-
triplesListEqual(rr.conclusion, newRule.conclusion),
|
|
5164
|
-
);
|
|
5165
|
-
if (!already) {
|
|
5166
|
-
backRules.push(newRule);
|
|
5167
|
-
indexBackRule(backRules, newRule);
|
|
5168
|
-
}
|
|
4971
|
+
if (!already) forwardRules.push(newRule);
|
|
4972
|
+
} else if (isBwRuleTriple) {
|
|
4973
|
+
const [premise, conclusion] = liftBlankRuleVars(right, left);
|
|
4974
|
+
const headBlankLabels = collectBlankLabelsInTriples(conclusion);
|
|
4975
|
+
const newRule = new Rule(premise, conclusion, false, false, headBlankLabels);
|
|
4976
|
+
|
|
4977
|
+
const already = backRules.some(
|
|
4978
|
+
(rr) =>
|
|
4979
|
+
rr.isForward === newRule.isForward &&
|
|
4980
|
+
rr.isFuse === newRule.isFuse &&
|
|
4981
|
+
triplesListEqual(rr.premise, newRule.premise) &&
|
|
4982
|
+
triplesListEqual(rr.conclusion, newRule.conclusion),
|
|
4983
|
+
);
|
|
4984
|
+
if (!already) {
|
|
4985
|
+
backRules.push(newRule);
|
|
4986
|
+
indexBackRule(backRules, newRule);
|
|
5169
4987
|
}
|
|
5170
4988
|
}
|
|
5171
|
-
|
|
5172
|
-
continue; // skip normal fact handling
|
|
5173
4989
|
}
|
|
5174
4990
|
|
|
5175
|
-
//
|
|
5176
|
-
|
|
4991
|
+
continue; // skip normal fact handling
|
|
4992
|
+
}
|
|
5177
4993
|
|
|
5178
|
-
|
|
5179
|
-
|
|
4994
|
+
// Only skolemize blank nodes that occur explicitly in the rule head
|
|
4995
|
+
const inst = skolemizeTripleForHeadBlanks(instantiated, r.headBlankLabels, skMap, skCounter, fireKey, headSkolemCache);
|
|
5180
4996
|
|
|
5181
|
-
|
|
5182
|
-
|
|
4997
|
+
if (!isGroundTriple(inst)) continue;
|
|
4998
|
+
if (hasFactIndexed(facts, inst)) continue;
|
|
5183
4999
|
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
}
|
|
5000
|
+
factList.push(inst);
|
|
5001
|
+
pushFactIndexed(facts, inst);
|
|
5002
|
+
derivedForward.push(new DerivedFact(inst, r, instantiatedPremises.slice(), { ...s }));
|
|
5003
|
+
changed = true;
|
|
5187
5004
|
}
|
|
5188
5005
|
}
|
|
5189
|
-
|
|
5190
|
-
if (!changed) break;
|
|
5191
5006
|
}
|
|
5192
5007
|
|
|
5193
|
-
if (changed) anyChange = true;
|
|
5194
5008
|
if (!changed) break;
|
|
5009
|
+
anyChange = true;
|
|
5195
5010
|
}
|
|
5011
|
+
|
|
5196
5012
|
return anyChange;
|
|
5197
5013
|
}
|
|
5198
5014
|
|