eyeling 1.22.6 → 1.22.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/HANDBOOK.md +245 -0
- package/dist/browser/eyeling.browser.js +188 -33
- package/examples/act-alarm-bit-interoperability.n3 +180 -0
- package/examples/act-barley-seed-lineage.n3 +565 -0
- package/examples/act-docking-abort.n3 +285 -0
- package/examples/act-gravity-mediator-witness.n3 +235 -0
- package/examples/act-isolation-breach.n3 +354 -0
- package/examples/act-photosynthetic-exciton-transfer.n3 +245 -0
- package/examples/act-sensor-memory-reset.n3 +190 -0
- package/examples/act-tunnel-junction-wake-switch.n3 +225 -0
- package/examples/act-yeast-self-reproduction.n3 +248 -0
- package/examples/complex-matrix-stability.n3 +288 -0
- package/examples/deck/act-barley-seed-lineage.md +593 -0
- package/examples/fundamental-theorem-arithmetic.n3 +244 -0
- package/examples/harborsmr.n3 +233 -0
- package/examples/meta-rule-audit.n3 +135 -0
- package/examples/output/act-alarm-bit-interoperability.txt +20 -0
- package/examples/output/act-barley-seed-lineage.txt +25 -0
- package/examples/output/act-docking-abort.txt +22 -0
- package/examples/output/act-gravity-mediator-witness.txt +24 -0
- package/examples/output/act-isolation-breach.txt +27 -0
- package/examples/output/act-photosynthetic-exciton-transfer.txt +20 -0
- package/examples/output/act-sensor-memory-reset.txt +20 -0
- package/examples/output/act-tunnel-junction-wake-switch.txt +21 -0
- package/examples/output/act-yeast-self-reproduction.txt +23 -0
- package/examples/output/complex-matrix-stability.txt +14 -0
- package/examples/output/fundamental-theorem-arithmetic.txt +15 -0
- package/examples/output/get-uuid.n3 +2 -2
- package/examples/output/harborsmr.txt +20 -0
- package/examples/output/meta-rule-audit.n3 +44 -0
- package/examples/output/theory-diff.n3 +22 -0
- package/examples/theory-diff.n3 +125 -0
- package/eyeling.js +188 -33
- package/lib/builtins.js +18 -1
- package/lib/cli.js +31 -5
- package/lib/engine.js +139 -27
- package/package.json +1 -1
- package/test/api.test.js +100 -0
package/eyeling.js
CHANGED
|
@@ -2354,6 +2354,23 @@ function __logNaturalPriorityFromTerm(t) {
|
|
|
2354
2354
|
// ===========================================================================
|
|
2355
2355
|
// Backward proof & builtins mutual recursion — declarations first
|
|
2356
2356
|
|
|
2357
|
+
function __varCameFromBoundSubstitution(goalTerm, subst) {
|
|
2358
|
+
if (!(goalTerm instanceof Var)) return false;
|
|
2359
|
+
if (!subst || !Object.prototype.hasOwnProperty.call(subst, goalTerm.name)) return false;
|
|
2360
|
+
|
|
2361
|
+
let cur = subst[goalTerm.name];
|
|
2362
|
+
const seen = new Set([goalTerm.name]);
|
|
2363
|
+
|
|
2364
|
+
while (cur instanceof Var) {
|
|
2365
|
+
if (seen.has(cur.name)) return true;
|
|
2366
|
+
seen.add(cur.name);
|
|
2367
|
+
if (!Object.prototype.hasOwnProperty.call(subst, cur.name)) return true;
|
|
2368
|
+
cur = subst[cur.name];
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
return false;
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2357
2374
|
function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
2358
2375
|
const g = applySubstTriple(goal, subst);
|
|
2359
2376
|
const pv = iriValue(g.p);
|
|
@@ -3820,7 +3837,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
|
3820
3837
|
// Schema: $s+ log:rawType $o-
|
|
3821
3838
|
// Returns one of log:Formula, log:Literal, rdf:List, or log:Other.
|
|
3822
3839
|
if (pv === LOG_NS + 'rawType') {
|
|
3823
|
-
if (g.s instanceof Var) return [];
|
|
3840
|
+
if (g.s instanceof Var && !__varCameFromBoundSubstitution(goal.s, subst)) return [];
|
|
3824
3841
|
|
|
3825
3842
|
let ty;
|
|
3826
3843
|
if (g.s instanceof GraphTerm) ty = internIri(LOG_NS + 'Formula');
|
|
@@ -4870,7 +4887,11 @@ module.exports = {
|
|
|
4870
4887
|
|
|
4871
4888
|
'use strict';
|
|
4872
4889
|
|
|
4890
|
+
const path = require('node:path');
|
|
4891
|
+
const { pathToFileURL } = require('node:url');
|
|
4892
|
+
|
|
4873
4893
|
const engine = require('./engine');
|
|
4894
|
+
const deref = require('./deref');
|
|
4874
4895
|
const { PrefixEnv } = require('./prelude');
|
|
4875
4896
|
|
|
4876
4897
|
function offsetToLineCol(text, offset) {
|
|
@@ -4913,6 +4934,29 @@ function readTextFromStdinSync() {
|
|
|
4913
4934
|
return fs.readFileSync(0, { encoding: 'utf8' });
|
|
4914
4935
|
}
|
|
4915
4936
|
|
|
4937
|
+
function __isNetworkOrFileIri(s) {
|
|
4938
|
+
return typeof s === 'string' && /^(https?:|file:\/\/)/i.test(s);
|
|
4939
|
+
}
|
|
4940
|
+
|
|
4941
|
+
function __sourceLabelToBaseIri(sourceLabel) {
|
|
4942
|
+
if (!sourceLabel || sourceLabel === '<stdin>') return '';
|
|
4943
|
+
if (__isNetworkOrFileIri(sourceLabel)) return deref.stripFragment(sourceLabel);
|
|
4944
|
+
return pathToFileURL(path.resolve(sourceLabel)).toString();
|
|
4945
|
+
}
|
|
4946
|
+
|
|
4947
|
+
function __readInputSourceSync(sourceLabel) {
|
|
4948
|
+
if (sourceLabel === '<stdin>') return readTextFromStdinSync();
|
|
4949
|
+
|
|
4950
|
+
if (__isNetworkOrFileIri(sourceLabel)) {
|
|
4951
|
+
const txt = deref.derefTextSync(sourceLabel);
|
|
4952
|
+
if (typeof txt !== 'string') throw new Error(`Failed to dereference ${sourceLabel}`);
|
|
4953
|
+
return txt;
|
|
4954
|
+
}
|
|
4955
|
+
|
|
4956
|
+
const fs = require('node:fs');
|
|
4957
|
+
return fs.readFileSync(sourceLabel, { encoding: 'utf8' });
|
|
4958
|
+
}
|
|
4959
|
+
|
|
4916
4960
|
function main() {
|
|
4917
4961
|
// Drop "node" and script name; keep only user-provided args
|
|
4918
4962
|
// Expand combined short options: -pt == -p -t
|
|
@@ -5026,13 +5070,11 @@ function main() {
|
|
|
5026
5070
|
}
|
|
5027
5071
|
|
|
5028
5072
|
const sourceLabel = useImplicitStdin || positional[0] === '-' ? '<stdin>' : positional[0];
|
|
5073
|
+
const baseIri = __sourceLabelToBaseIri(sourceLabel);
|
|
5074
|
+
|
|
5029
5075
|
let text;
|
|
5030
5076
|
try {
|
|
5031
|
-
|
|
5032
|
-
else {
|
|
5033
|
-
const fs = require('node:fs');
|
|
5034
|
-
text = fs.readFileSync(sourceLabel, { encoding: 'utf8' });
|
|
5035
|
-
}
|
|
5077
|
+
text = __readInputSourceSync(sourceLabel);
|
|
5036
5078
|
} catch (e) {
|
|
5037
5079
|
if (sourceLabel === '<stdin>') console.error(`Error reading stdin: ${e.message}`);
|
|
5038
5080
|
else console.error(`Error reading file ${JSON.stringify(sourceLabel)}: ${e.message}`);
|
|
@@ -5044,6 +5086,7 @@ function main() {
|
|
|
5044
5086
|
try {
|
|
5045
5087
|
toks = engine.lex(text);
|
|
5046
5088
|
const parser = new engine.Parser(toks);
|
|
5089
|
+
if (baseIri) parser.prefixes.setBase(baseIri);
|
|
5047
5090
|
[prefixes, triples, frules, brules, qrules] = parser.parseDocument();
|
|
5048
5091
|
// Make the parsed prefixes available to log:trace output (CLI path)
|
|
5049
5092
|
engine.setTracePrefixes(prefixes);
|
|
@@ -5951,21 +5994,80 @@ function __isStrictGroundTriple(tr) {
|
|
|
5951
5994
|
// -----------------------------------------------------------------------------
|
|
5952
5995
|
// Used to maintain O(1) membership sets for dynamically promoted rules, and to
|
|
5953
5996
|
// memoize per-firing head-blank skolemization.
|
|
5997
|
+
//
|
|
5998
|
+
// Important: variables and blank nodes *inside quoted formulas* are local to the
|
|
5999
|
+
// formula. Canonicalize those labels by first occurrence so alpha-equivalent
|
|
6000
|
+
// formulas (for example the repeated results of log:semantics after
|
|
6001
|
+
// standardize-apart) get the same identity key. Keep top-level blank labels
|
|
6002
|
+
// untouched so distinct existential witnesses in the global fact set do not
|
|
6003
|
+
// collapse together.
|
|
6004
|
+
function __keyFromTermForRuleIdentity(term) {
|
|
6005
|
+
const ctx = { quotedVar: new Map(), quotedBlank: new Map() };
|
|
6006
|
+
|
|
6007
|
+
function canonQuotedVar(name) {
|
|
6008
|
+
let out = ctx.quotedVar.get(name);
|
|
6009
|
+
if (!out) {
|
|
6010
|
+
out = `v${ctx.quotedVar.size}`;
|
|
6011
|
+
ctx.quotedVar.set(name, out);
|
|
6012
|
+
}
|
|
6013
|
+
return out;
|
|
6014
|
+
}
|
|
6015
|
+
|
|
6016
|
+
function canonQuotedBlank(label) {
|
|
6017
|
+
let out = ctx.quotedBlank.get(label);
|
|
6018
|
+
if (!out) {
|
|
6019
|
+
out = `b${ctx.quotedBlank.size}`;
|
|
6020
|
+
ctx.quotedBlank.set(label, out);
|
|
6021
|
+
}
|
|
6022
|
+
return out;
|
|
6023
|
+
}
|
|
6024
|
+
|
|
6025
|
+
function enc(u, inQuotedFormula) {
|
|
6026
|
+
if (u instanceof Iri) return ['I', u.value];
|
|
6027
|
+
if (u instanceof Literal) return ['L', u.value];
|
|
6028
|
+
if (u instanceof Blank) return inQuotedFormula ? ['BQ', canonQuotedBlank(u.label)] : ['B', u.label];
|
|
6029
|
+
if (u instanceof Var) return inQuotedFormula ? ['VQ', canonQuotedVar(u.name)] : ['V', u.name];
|
|
6030
|
+
if (u instanceof ListTerm) return ['List', u.elems.map((e) => enc(e, inQuotedFormula))];
|
|
6031
|
+
if (u instanceof OpenListTerm) {
|
|
6032
|
+
return [
|
|
6033
|
+
'OpenList',
|
|
6034
|
+
u.prefix.map((e) => enc(e, inQuotedFormula)),
|
|
6035
|
+
inQuotedFormula ? ['TailVQ', canonQuotedVar(u.tailVar)] : ['TailV', u.tailVar],
|
|
6036
|
+
];
|
|
6037
|
+
}
|
|
6038
|
+
if (u instanceof GraphTerm)
|
|
6039
|
+
return ['Graph', u.triples.map((tr) => [enc(tr.s, true), enc(tr.p, true), enc(tr.o, true)])];
|
|
6040
|
+
return ['Other', String(u)];
|
|
6041
|
+
}
|
|
6042
|
+
|
|
6043
|
+
return JSON.stringify(enc(term, false));
|
|
6044
|
+
}
|
|
6045
|
+
|
|
5954
6046
|
function __ruleKey(isForward, isFuse, premise, conclusion, dynamicConclusionTerm /* optional */) {
|
|
5955
6047
|
let out = (isForward ? 'F' : 'B') + (isFuse ? '!' : '') + '|P|';
|
|
5956
6048
|
for (let i = 0; i < premise.length; i++) {
|
|
5957
6049
|
const tr = premise[i];
|
|
5958
6050
|
if (i) out += '\n';
|
|
5959
|
-
out +=
|
|
6051
|
+
out +=
|
|
6052
|
+
__keyFromTermForRuleIdentity(tr.s) +
|
|
6053
|
+
'\t' +
|
|
6054
|
+
__keyFromTermForRuleIdentity(tr.p) +
|
|
6055
|
+
'\t' +
|
|
6056
|
+
__keyFromTermForRuleIdentity(tr.o);
|
|
5960
6057
|
}
|
|
5961
6058
|
out += '|C|';
|
|
5962
6059
|
for (let i = 0; i < conclusion.length; i++) {
|
|
5963
6060
|
const tr = conclusion[i];
|
|
5964
6061
|
if (i) out += '\n';
|
|
5965
|
-
out +=
|
|
6062
|
+
out +=
|
|
6063
|
+
__keyFromTermForRuleIdentity(tr.s) +
|
|
6064
|
+
'\t' +
|
|
6065
|
+
__keyFromTermForRuleIdentity(tr.p) +
|
|
6066
|
+
'\t' +
|
|
6067
|
+
__keyFromTermForRuleIdentity(tr.o);
|
|
5966
6068
|
}
|
|
5967
6069
|
if (dynamicConclusionTerm) {
|
|
5968
|
-
out += '|T|' +
|
|
6070
|
+
out += '|T|' + __keyFromTermForRuleIdentity(dynamicConclusionTerm);
|
|
5969
6071
|
}
|
|
5970
6072
|
return out;
|
|
5971
6073
|
}
|
|
@@ -5976,7 +6078,12 @@ function __firingKey(ruleIndex, instantiatedPremises) {
|
|
|
5976
6078
|
for (let i = 0; i < instantiatedPremises.length; i++) {
|
|
5977
6079
|
const tr = instantiatedPremises[i];
|
|
5978
6080
|
if (i) out += '\n';
|
|
5979
|
-
out +=
|
|
6081
|
+
out +=
|
|
6082
|
+
__keyFromTermForRuleIdentity(tr.s) +
|
|
6083
|
+
'\t' +
|
|
6084
|
+
__keyFromTermForRuleIdentity(tr.p) +
|
|
6085
|
+
'\t' +
|
|
6086
|
+
__keyFromTermForRuleIdentity(tr.o);
|
|
5980
6087
|
}
|
|
5981
6088
|
return out;
|
|
5982
6089
|
}
|
|
@@ -6534,14 +6641,44 @@ function collectProtectedNamesInTerm(t, protectedVars, protectedBlanks) {
|
|
|
6534
6641
|
}
|
|
6535
6642
|
}
|
|
6536
6643
|
|
|
6537
|
-
function
|
|
6644
|
+
function collectProtectedNamesFromTermViaSubst(term, subst, protectedVars, protectedBlanks, seenVarNames) {
|
|
6645
|
+
if (term instanceof Var) {
|
|
6646
|
+
if (!subst || !Object.prototype.hasOwnProperty.call(subst, term.name)) return;
|
|
6647
|
+
if (seenVarNames.has(term.name)) return;
|
|
6648
|
+
seenVarNames.add(term.name);
|
|
6649
|
+
collectProtectedNamesInTerm(subst[term.name], protectedVars, protectedBlanks);
|
|
6650
|
+
return;
|
|
6651
|
+
}
|
|
6652
|
+
|
|
6653
|
+
if (term instanceof ListTerm) {
|
|
6654
|
+
for (const e of term.elems)
|
|
6655
|
+
collectProtectedNamesFromTermViaSubst(e, subst, protectedVars, protectedBlanks, seenVarNames);
|
|
6656
|
+
return;
|
|
6657
|
+
}
|
|
6658
|
+
|
|
6659
|
+
if (term instanceof OpenListTerm) {
|
|
6660
|
+
for (const e of term.prefix)
|
|
6661
|
+
collectProtectedNamesFromTermViaSubst(e, subst, protectedVars, protectedBlanks, seenVarNames);
|
|
6662
|
+
if (subst && Object.prototype.hasOwnProperty.call(subst, term.tailVar) && !seenVarNames.has(term.tailVar)) {
|
|
6663
|
+
seenVarNames.add(term.tailVar);
|
|
6664
|
+
collectProtectedNamesInTerm(subst[term.tailVar], protectedVars, protectedBlanks);
|
|
6665
|
+
}
|
|
6666
|
+
return;
|
|
6667
|
+
}
|
|
6668
|
+
|
|
6669
|
+
if (term instanceof GraphTerm) {
|
|
6670
|
+
for (const tr of term.triples) {
|
|
6671
|
+
collectProtectedNamesFromTermViaSubst(tr.s, subst, protectedVars, protectedBlanks, seenVarNames);
|
|
6672
|
+
collectProtectedNamesFromTermViaSubst(tr.p, subst, protectedVars, protectedBlanks, seenVarNames);
|
|
6673
|
+
collectProtectedNamesFromTermViaSubst(tr.o, subst, protectedVars, protectedBlanks, seenVarNames);
|
|
6674
|
+
}
|
|
6675
|
+
}
|
|
6676
|
+
}
|
|
6677
|
+
|
|
6678
|
+
function collectProtectedNamesForTerm(term, subst) {
|
|
6538
6679
|
const protectedVars = new Set();
|
|
6539
6680
|
const protectedBlanks = new Set();
|
|
6540
|
-
|
|
6541
|
-
for (const k in subst) {
|
|
6542
|
-
if (!Object.prototype.hasOwnProperty.call(subst, k)) continue;
|
|
6543
|
-
collectProtectedNamesInTerm(subst[k], protectedVars, protectedBlanks);
|
|
6544
|
-
}
|
|
6681
|
+
collectProtectedNamesFromTermViaSubst(term, subst, protectedVars, protectedBlanks, new Set());
|
|
6545
6682
|
return { protectedVars, protectedBlanks };
|
|
6546
6683
|
}
|
|
6547
6684
|
|
|
@@ -7503,6 +7640,9 @@ function unifyTermListAppend(a, b, subst) {
|
|
|
7503
7640
|
}
|
|
7504
7641
|
|
|
7505
7642
|
function unifyTermWithOptions(a, b, subst, opts) {
|
|
7643
|
+
const aRaw = a;
|
|
7644
|
+
const bRaw = b;
|
|
7645
|
+
|
|
7506
7646
|
a = applySubstTerm(a, subst);
|
|
7507
7647
|
b = applySubstTerm(b, subst);
|
|
7508
7648
|
|
|
@@ -7610,13 +7750,14 @@ function unifyTermWithOptions(a, b, subst, opts) {
|
|
|
7610
7750
|
|
|
7611
7751
|
// Graphs
|
|
7612
7752
|
if (a instanceof GraphTerm && b instanceof GraphTerm) {
|
|
7613
|
-
const
|
|
7753
|
+
const protectedNamesA = collectProtectedNamesForTerm(aRaw, subst);
|
|
7754
|
+
const protectedNamesB = collectProtectedNamesForTerm(bRaw, subst);
|
|
7614
7755
|
if (
|
|
7615
7756
|
alphaEqGraphTriples(a.triples, b.triples, {
|
|
7616
|
-
protectedVarsA:
|
|
7617
|
-
protectedVarsB:
|
|
7618
|
-
protectedBlanksA:
|
|
7619
|
-
protectedBlanksB:
|
|
7757
|
+
protectedVarsA: protectedNamesA.protectedVars,
|
|
7758
|
+
protectedVarsB: protectedNamesB.protectedVars,
|
|
7759
|
+
protectedBlanksA: protectedNamesA.protectedBlanks,
|
|
7760
|
+
protectedBlanksB: protectedNamesB.protectedBlanks,
|
|
7620
7761
|
})
|
|
7621
7762
|
) {
|
|
7622
7763
|
return subst;
|
|
@@ -7920,6 +8061,9 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
|
|
|
7920
8061
|
}
|
|
7921
8062
|
|
|
7922
8063
|
function unifyTermTrail(a, b) {
|
|
8064
|
+
const aRaw = a;
|
|
8065
|
+
const bRaw = b;
|
|
8066
|
+
|
|
7923
8067
|
a = applySubstTerm(a, substMut);
|
|
7924
8068
|
b = applySubstTerm(b, substMut);
|
|
7925
8069
|
|
|
@@ -7997,24 +8141,25 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
|
|
|
7997
8141
|
|
|
7998
8142
|
// Graphs
|
|
7999
8143
|
if (a instanceof GraphTerm && b instanceof GraphTerm) {
|
|
8000
|
-
const
|
|
8144
|
+
const protectedNamesA = collectProtectedNamesForTerm(aRaw, substMut);
|
|
8145
|
+
const protectedNamesB = collectProtectedNamesForTerm(bRaw, substMut);
|
|
8001
8146
|
if (
|
|
8002
8147
|
alphaEqGraphTriples(a.triples, b.triples, {
|
|
8003
|
-
protectedVarsA:
|
|
8004
|
-
protectedVarsB:
|
|
8005
|
-
protectedBlanksA:
|
|
8006
|
-
protectedBlanksB:
|
|
8148
|
+
protectedVarsA: protectedNamesA.protectedVars,
|
|
8149
|
+
protectedVarsB: protectedNamesB.protectedVars,
|
|
8150
|
+
protectedBlanksA: protectedNamesA.protectedBlanks,
|
|
8151
|
+
protectedBlanksB: protectedNamesB.protectedBlanks,
|
|
8007
8152
|
})
|
|
8008
8153
|
) {
|
|
8009
8154
|
return true;
|
|
8010
8155
|
}
|
|
8011
|
-
|
|
8012
|
-
|
|
8013
|
-
if (delta === null) return false;
|
|
8156
|
+
const merged = unifyGraphTriples(a.triples, b.triples, substMut);
|
|
8157
|
+
if (merged === null) return false;
|
|
8014
8158
|
const mark = trail.length;
|
|
8015
|
-
for (const k in
|
|
8016
|
-
if (!Object.prototype.hasOwnProperty.call(
|
|
8017
|
-
if (
|
|
8159
|
+
for (const k in merged) {
|
|
8160
|
+
if (!Object.prototype.hasOwnProperty.call(merged, k)) continue;
|
|
8161
|
+
if (Object.prototype.hasOwnProperty.call(substMut, k)) continue;
|
|
8162
|
+
if (!bindVarTrail(k, merged[k])) {
|
|
8018
8163
|
undoTo(mark);
|
|
8019
8164
|
return false;
|
|
8020
8165
|
}
|
|
@@ -8216,7 +8361,17 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
|
|
|
8216
8361
|
if (remaining <= 0) continue;
|
|
8217
8362
|
const builtinMax = Number.isFinite(remaining) && !restGoals.length ? remaining : undefined;
|
|
8218
8363
|
|
|
8219
|
-
|
|
8364
|
+
const builtinGoalForEval = goalPredicateIri === LOG_NS + 'rawType' ? rawGoal : goal0;
|
|
8365
|
+
const builtinSubstForEval = goalPredicateIri === LOG_NS + 'rawType' ? substMut : {};
|
|
8366
|
+
let deltas = evalBuiltin(
|
|
8367
|
+
builtinGoalForEval,
|
|
8368
|
+
builtinSubstForEval,
|
|
8369
|
+
facts,
|
|
8370
|
+
backRules,
|
|
8371
|
+
frame.curDepth,
|
|
8372
|
+
varGen,
|
|
8373
|
+
builtinMax,
|
|
8374
|
+
);
|
|
8220
8375
|
|
|
8221
8376
|
const dc = typeof frame.deferCount === 'number' ? frame.deferCount : 0;
|
|
8222
8377
|
const builtinDeltasAreVacuous = deltas.length > 0 && deltas.every((d) => Object.keys(d).length === 0);
|
package/lib/builtins.js
CHANGED
|
@@ -1875,6 +1875,23 @@ function __logNaturalPriorityFromTerm(t) {
|
|
|
1875
1875
|
// ===========================================================================
|
|
1876
1876
|
// Backward proof & builtins mutual recursion — declarations first
|
|
1877
1877
|
|
|
1878
|
+
function __varCameFromBoundSubstitution(goalTerm, subst) {
|
|
1879
|
+
if (!(goalTerm instanceof Var)) return false;
|
|
1880
|
+
if (!subst || !Object.prototype.hasOwnProperty.call(subst, goalTerm.name)) return false;
|
|
1881
|
+
|
|
1882
|
+
let cur = subst[goalTerm.name];
|
|
1883
|
+
const seen = new Set([goalTerm.name]);
|
|
1884
|
+
|
|
1885
|
+
while (cur instanceof Var) {
|
|
1886
|
+
if (seen.has(cur.name)) return true;
|
|
1887
|
+
seen.add(cur.name);
|
|
1888
|
+
if (!Object.prototype.hasOwnProperty.call(subst, cur.name)) return true;
|
|
1889
|
+
cur = subst[cur.name];
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
return false;
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1878
1895
|
function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
1879
1896
|
const g = applySubstTriple(goal, subst);
|
|
1880
1897
|
const pv = iriValue(g.p);
|
|
@@ -3341,7 +3358,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
|
3341
3358
|
// Schema: $s+ log:rawType $o-
|
|
3342
3359
|
// Returns one of log:Formula, log:Literal, rdf:List, or log:Other.
|
|
3343
3360
|
if (pv === LOG_NS + 'rawType') {
|
|
3344
|
-
if (g.s instanceof Var) return [];
|
|
3361
|
+
if (g.s instanceof Var && !__varCameFromBoundSubstitution(goal.s, subst)) return [];
|
|
3345
3362
|
|
|
3346
3363
|
let ty;
|
|
3347
3364
|
if (g.s instanceof GraphTerm) ty = internIri(LOG_NS + 'Formula');
|
package/lib/cli.js
CHANGED
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
|
+
const path = require('node:path');
|
|
11
|
+
const { pathToFileURL } = require('node:url');
|
|
12
|
+
|
|
10
13
|
const engine = require('./engine');
|
|
14
|
+
const deref = require('./deref');
|
|
11
15
|
const { PrefixEnv } = require('./prelude');
|
|
12
16
|
|
|
13
17
|
function offsetToLineCol(text, offset) {
|
|
@@ -50,6 +54,29 @@ function readTextFromStdinSync() {
|
|
|
50
54
|
return fs.readFileSync(0, { encoding: 'utf8' });
|
|
51
55
|
}
|
|
52
56
|
|
|
57
|
+
function __isNetworkOrFileIri(s) {
|
|
58
|
+
return typeof s === 'string' && /^(https?:|file:\/\/)/i.test(s);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function __sourceLabelToBaseIri(sourceLabel) {
|
|
62
|
+
if (!sourceLabel || sourceLabel === '<stdin>') return '';
|
|
63
|
+
if (__isNetworkOrFileIri(sourceLabel)) return deref.stripFragment(sourceLabel);
|
|
64
|
+
return pathToFileURL(path.resolve(sourceLabel)).toString();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function __readInputSourceSync(sourceLabel) {
|
|
68
|
+
if (sourceLabel === '<stdin>') return readTextFromStdinSync();
|
|
69
|
+
|
|
70
|
+
if (__isNetworkOrFileIri(sourceLabel)) {
|
|
71
|
+
const txt = deref.derefTextSync(sourceLabel);
|
|
72
|
+
if (typeof txt !== 'string') throw new Error(`Failed to dereference ${sourceLabel}`);
|
|
73
|
+
return txt;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const fs = require('node:fs');
|
|
77
|
+
return fs.readFileSync(sourceLabel, { encoding: 'utf8' });
|
|
78
|
+
}
|
|
79
|
+
|
|
53
80
|
function main() {
|
|
54
81
|
// Drop "node" and script name; keep only user-provided args
|
|
55
82
|
// Expand combined short options: -pt == -p -t
|
|
@@ -163,13 +190,11 @@ function main() {
|
|
|
163
190
|
}
|
|
164
191
|
|
|
165
192
|
const sourceLabel = useImplicitStdin || positional[0] === '-' ? '<stdin>' : positional[0];
|
|
193
|
+
const baseIri = __sourceLabelToBaseIri(sourceLabel);
|
|
194
|
+
|
|
166
195
|
let text;
|
|
167
196
|
try {
|
|
168
|
-
|
|
169
|
-
else {
|
|
170
|
-
const fs = require('node:fs');
|
|
171
|
-
text = fs.readFileSync(sourceLabel, { encoding: 'utf8' });
|
|
172
|
-
}
|
|
197
|
+
text = __readInputSourceSync(sourceLabel);
|
|
173
198
|
} catch (e) {
|
|
174
199
|
if (sourceLabel === '<stdin>') console.error(`Error reading stdin: ${e.message}`);
|
|
175
200
|
else console.error(`Error reading file ${JSON.stringify(sourceLabel)}: ${e.message}`);
|
|
@@ -181,6 +206,7 @@ function main() {
|
|
|
181
206
|
try {
|
|
182
207
|
toks = engine.lex(text);
|
|
183
208
|
const parser = new engine.Parser(toks);
|
|
209
|
+
if (baseIri) parser.prefixes.setBase(baseIri);
|
|
184
210
|
[prefixes, triples, frules, brules, qrules] = parser.parseDocument();
|
|
185
211
|
// Make the parsed prefixes available to log:trace output (CLI path)
|
|
186
212
|
engine.setTracePrefixes(prefixes);
|
package/lib/engine.js
CHANGED
|
@@ -199,21 +199,80 @@ function __isStrictGroundTriple(tr) {
|
|
|
199
199
|
// -----------------------------------------------------------------------------
|
|
200
200
|
// Used to maintain O(1) membership sets for dynamically promoted rules, and to
|
|
201
201
|
// memoize per-firing head-blank skolemization.
|
|
202
|
+
//
|
|
203
|
+
// Important: variables and blank nodes *inside quoted formulas* are local to the
|
|
204
|
+
// formula. Canonicalize those labels by first occurrence so alpha-equivalent
|
|
205
|
+
// formulas (for example the repeated results of log:semantics after
|
|
206
|
+
// standardize-apart) get the same identity key. Keep top-level blank labels
|
|
207
|
+
// untouched so distinct existential witnesses in the global fact set do not
|
|
208
|
+
// collapse together.
|
|
209
|
+
function __keyFromTermForRuleIdentity(term) {
|
|
210
|
+
const ctx = { quotedVar: new Map(), quotedBlank: new Map() };
|
|
211
|
+
|
|
212
|
+
function canonQuotedVar(name) {
|
|
213
|
+
let out = ctx.quotedVar.get(name);
|
|
214
|
+
if (!out) {
|
|
215
|
+
out = `v${ctx.quotedVar.size}`;
|
|
216
|
+
ctx.quotedVar.set(name, out);
|
|
217
|
+
}
|
|
218
|
+
return out;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function canonQuotedBlank(label) {
|
|
222
|
+
let out = ctx.quotedBlank.get(label);
|
|
223
|
+
if (!out) {
|
|
224
|
+
out = `b${ctx.quotedBlank.size}`;
|
|
225
|
+
ctx.quotedBlank.set(label, out);
|
|
226
|
+
}
|
|
227
|
+
return out;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function enc(u, inQuotedFormula) {
|
|
231
|
+
if (u instanceof Iri) return ['I', u.value];
|
|
232
|
+
if (u instanceof Literal) return ['L', u.value];
|
|
233
|
+
if (u instanceof Blank) return inQuotedFormula ? ['BQ', canonQuotedBlank(u.label)] : ['B', u.label];
|
|
234
|
+
if (u instanceof Var) return inQuotedFormula ? ['VQ', canonQuotedVar(u.name)] : ['V', u.name];
|
|
235
|
+
if (u instanceof ListTerm) return ['List', u.elems.map((e) => enc(e, inQuotedFormula))];
|
|
236
|
+
if (u instanceof OpenListTerm) {
|
|
237
|
+
return [
|
|
238
|
+
'OpenList',
|
|
239
|
+
u.prefix.map((e) => enc(e, inQuotedFormula)),
|
|
240
|
+
inQuotedFormula ? ['TailVQ', canonQuotedVar(u.tailVar)] : ['TailV', u.tailVar],
|
|
241
|
+
];
|
|
242
|
+
}
|
|
243
|
+
if (u instanceof GraphTerm)
|
|
244
|
+
return ['Graph', u.triples.map((tr) => [enc(tr.s, true), enc(tr.p, true), enc(tr.o, true)])];
|
|
245
|
+
return ['Other', String(u)];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return JSON.stringify(enc(term, false));
|
|
249
|
+
}
|
|
250
|
+
|
|
202
251
|
function __ruleKey(isForward, isFuse, premise, conclusion, dynamicConclusionTerm /* optional */) {
|
|
203
252
|
let out = (isForward ? 'F' : 'B') + (isFuse ? '!' : '') + '|P|';
|
|
204
253
|
for (let i = 0; i < premise.length; i++) {
|
|
205
254
|
const tr = premise[i];
|
|
206
255
|
if (i) out += '\n';
|
|
207
|
-
out +=
|
|
256
|
+
out +=
|
|
257
|
+
__keyFromTermForRuleIdentity(tr.s) +
|
|
258
|
+
'\t' +
|
|
259
|
+
__keyFromTermForRuleIdentity(tr.p) +
|
|
260
|
+
'\t' +
|
|
261
|
+
__keyFromTermForRuleIdentity(tr.o);
|
|
208
262
|
}
|
|
209
263
|
out += '|C|';
|
|
210
264
|
for (let i = 0; i < conclusion.length; i++) {
|
|
211
265
|
const tr = conclusion[i];
|
|
212
266
|
if (i) out += '\n';
|
|
213
|
-
out +=
|
|
267
|
+
out +=
|
|
268
|
+
__keyFromTermForRuleIdentity(tr.s) +
|
|
269
|
+
'\t' +
|
|
270
|
+
__keyFromTermForRuleIdentity(tr.p) +
|
|
271
|
+
'\t' +
|
|
272
|
+
__keyFromTermForRuleIdentity(tr.o);
|
|
214
273
|
}
|
|
215
274
|
if (dynamicConclusionTerm) {
|
|
216
|
-
out += '|T|' +
|
|
275
|
+
out += '|T|' + __keyFromTermForRuleIdentity(dynamicConclusionTerm);
|
|
217
276
|
}
|
|
218
277
|
return out;
|
|
219
278
|
}
|
|
@@ -224,7 +283,12 @@ function __firingKey(ruleIndex, instantiatedPremises) {
|
|
|
224
283
|
for (let i = 0; i < instantiatedPremises.length; i++) {
|
|
225
284
|
const tr = instantiatedPremises[i];
|
|
226
285
|
if (i) out += '\n';
|
|
227
|
-
out +=
|
|
286
|
+
out +=
|
|
287
|
+
__keyFromTermForRuleIdentity(tr.s) +
|
|
288
|
+
'\t' +
|
|
289
|
+
__keyFromTermForRuleIdentity(tr.p) +
|
|
290
|
+
'\t' +
|
|
291
|
+
__keyFromTermForRuleIdentity(tr.o);
|
|
228
292
|
}
|
|
229
293
|
return out;
|
|
230
294
|
}
|
|
@@ -782,14 +846,44 @@ function collectProtectedNamesInTerm(t, protectedVars, protectedBlanks) {
|
|
|
782
846
|
}
|
|
783
847
|
}
|
|
784
848
|
|
|
785
|
-
function
|
|
849
|
+
function collectProtectedNamesFromTermViaSubst(term, subst, protectedVars, protectedBlanks, seenVarNames) {
|
|
850
|
+
if (term instanceof Var) {
|
|
851
|
+
if (!subst || !Object.prototype.hasOwnProperty.call(subst, term.name)) return;
|
|
852
|
+
if (seenVarNames.has(term.name)) return;
|
|
853
|
+
seenVarNames.add(term.name);
|
|
854
|
+
collectProtectedNamesInTerm(subst[term.name], protectedVars, protectedBlanks);
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
if (term instanceof ListTerm) {
|
|
859
|
+
for (const e of term.elems)
|
|
860
|
+
collectProtectedNamesFromTermViaSubst(e, subst, protectedVars, protectedBlanks, seenVarNames);
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
if (term instanceof OpenListTerm) {
|
|
865
|
+
for (const e of term.prefix)
|
|
866
|
+
collectProtectedNamesFromTermViaSubst(e, subst, protectedVars, protectedBlanks, seenVarNames);
|
|
867
|
+
if (subst && Object.prototype.hasOwnProperty.call(subst, term.tailVar) && !seenVarNames.has(term.tailVar)) {
|
|
868
|
+
seenVarNames.add(term.tailVar);
|
|
869
|
+
collectProtectedNamesInTerm(subst[term.tailVar], protectedVars, protectedBlanks);
|
|
870
|
+
}
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
if (term instanceof GraphTerm) {
|
|
875
|
+
for (const tr of term.triples) {
|
|
876
|
+
collectProtectedNamesFromTermViaSubst(tr.s, subst, protectedVars, protectedBlanks, seenVarNames);
|
|
877
|
+
collectProtectedNamesFromTermViaSubst(tr.p, subst, protectedVars, protectedBlanks, seenVarNames);
|
|
878
|
+
collectProtectedNamesFromTermViaSubst(tr.o, subst, protectedVars, protectedBlanks, seenVarNames);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
function collectProtectedNamesForTerm(term, subst) {
|
|
786
884
|
const protectedVars = new Set();
|
|
787
885
|
const protectedBlanks = new Set();
|
|
788
|
-
|
|
789
|
-
for (const k in subst) {
|
|
790
|
-
if (!Object.prototype.hasOwnProperty.call(subst, k)) continue;
|
|
791
|
-
collectProtectedNamesInTerm(subst[k], protectedVars, protectedBlanks);
|
|
792
|
-
}
|
|
886
|
+
collectProtectedNamesFromTermViaSubst(term, subst, protectedVars, protectedBlanks, new Set());
|
|
793
887
|
return { protectedVars, protectedBlanks };
|
|
794
888
|
}
|
|
795
889
|
|
|
@@ -1751,6 +1845,9 @@ function unifyTermListAppend(a, b, subst) {
|
|
|
1751
1845
|
}
|
|
1752
1846
|
|
|
1753
1847
|
function unifyTermWithOptions(a, b, subst, opts) {
|
|
1848
|
+
const aRaw = a;
|
|
1849
|
+
const bRaw = b;
|
|
1850
|
+
|
|
1754
1851
|
a = applySubstTerm(a, subst);
|
|
1755
1852
|
b = applySubstTerm(b, subst);
|
|
1756
1853
|
|
|
@@ -1858,13 +1955,14 @@ function unifyTermWithOptions(a, b, subst, opts) {
|
|
|
1858
1955
|
|
|
1859
1956
|
// Graphs
|
|
1860
1957
|
if (a instanceof GraphTerm && b instanceof GraphTerm) {
|
|
1861
|
-
const
|
|
1958
|
+
const protectedNamesA = collectProtectedNamesForTerm(aRaw, subst);
|
|
1959
|
+
const protectedNamesB = collectProtectedNamesForTerm(bRaw, subst);
|
|
1862
1960
|
if (
|
|
1863
1961
|
alphaEqGraphTriples(a.triples, b.triples, {
|
|
1864
|
-
protectedVarsA:
|
|
1865
|
-
protectedVarsB:
|
|
1866
|
-
protectedBlanksA:
|
|
1867
|
-
protectedBlanksB:
|
|
1962
|
+
protectedVarsA: protectedNamesA.protectedVars,
|
|
1963
|
+
protectedVarsB: protectedNamesB.protectedVars,
|
|
1964
|
+
protectedBlanksA: protectedNamesA.protectedBlanks,
|
|
1965
|
+
protectedBlanksB: protectedNamesB.protectedBlanks,
|
|
1868
1966
|
})
|
|
1869
1967
|
) {
|
|
1870
1968
|
return subst;
|
|
@@ -2168,6 +2266,9 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
|
|
|
2168
2266
|
}
|
|
2169
2267
|
|
|
2170
2268
|
function unifyTermTrail(a, b) {
|
|
2269
|
+
const aRaw = a;
|
|
2270
|
+
const bRaw = b;
|
|
2271
|
+
|
|
2171
2272
|
a = applySubstTerm(a, substMut);
|
|
2172
2273
|
b = applySubstTerm(b, substMut);
|
|
2173
2274
|
|
|
@@ -2245,24 +2346,25 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
|
|
|
2245
2346
|
|
|
2246
2347
|
// Graphs
|
|
2247
2348
|
if (a instanceof GraphTerm && b instanceof GraphTerm) {
|
|
2248
|
-
const
|
|
2349
|
+
const protectedNamesA = collectProtectedNamesForTerm(aRaw, substMut);
|
|
2350
|
+
const protectedNamesB = collectProtectedNamesForTerm(bRaw, substMut);
|
|
2249
2351
|
if (
|
|
2250
2352
|
alphaEqGraphTriples(a.triples, b.triples, {
|
|
2251
|
-
protectedVarsA:
|
|
2252
|
-
protectedVarsB:
|
|
2253
|
-
protectedBlanksA:
|
|
2254
|
-
protectedBlanksB:
|
|
2353
|
+
protectedVarsA: protectedNamesA.protectedVars,
|
|
2354
|
+
protectedVarsB: protectedNamesB.protectedVars,
|
|
2355
|
+
protectedBlanksA: protectedNamesA.protectedBlanks,
|
|
2356
|
+
protectedBlanksB: protectedNamesB.protectedBlanks,
|
|
2255
2357
|
})
|
|
2256
2358
|
) {
|
|
2257
2359
|
return true;
|
|
2258
2360
|
}
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
if (delta === null) return false;
|
|
2361
|
+
const merged = unifyGraphTriples(a.triples, b.triples, substMut);
|
|
2362
|
+
if (merged === null) return false;
|
|
2262
2363
|
const mark = trail.length;
|
|
2263
|
-
for (const k in
|
|
2264
|
-
if (!Object.prototype.hasOwnProperty.call(
|
|
2265
|
-
if (
|
|
2364
|
+
for (const k in merged) {
|
|
2365
|
+
if (!Object.prototype.hasOwnProperty.call(merged, k)) continue;
|
|
2366
|
+
if (Object.prototype.hasOwnProperty.call(substMut, k)) continue;
|
|
2367
|
+
if (!bindVarTrail(k, merged[k])) {
|
|
2266
2368
|
undoTo(mark);
|
|
2267
2369
|
return false;
|
|
2268
2370
|
}
|
|
@@ -2464,7 +2566,17 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
|
|
|
2464
2566
|
if (remaining <= 0) continue;
|
|
2465
2567
|
const builtinMax = Number.isFinite(remaining) && !restGoals.length ? remaining : undefined;
|
|
2466
2568
|
|
|
2467
|
-
|
|
2569
|
+
const builtinGoalForEval = goalPredicateIri === LOG_NS + 'rawType' ? rawGoal : goal0;
|
|
2570
|
+
const builtinSubstForEval = goalPredicateIri === LOG_NS + 'rawType' ? substMut : {};
|
|
2571
|
+
let deltas = evalBuiltin(
|
|
2572
|
+
builtinGoalForEval,
|
|
2573
|
+
builtinSubstForEval,
|
|
2574
|
+
facts,
|
|
2575
|
+
backRules,
|
|
2576
|
+
frame.curDepth,
|
|
2577
|
+
varGen,
|
|
2578
|
+
builtinMax,
|
|
2579
|
+
);
|
|
2468
2580
|
|
|
2469
2581
|
const dc = typeof frame.deferCount === 'number' ? frame.deferCount : 0;
|
|
2470
2582
|
const builtinDeltasAreVacuous = deltas.length > 0 && deltas.every((d) => Object.keys(d).length === 0);
|