eyeleng 1.1.0 → 1.1.1
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/README.md +7 -5
- package/dist/browser/eyeleng.browser.js +60 -11
- package/examples/fibonacci.srl +89 -30009
- package/examples/output/bayes-diagnosis.trig +0 -4
- package/examples/output/bmi.trig +0 -2
- package/examples/output/dijkstra.trig +0 -8
- package/examples/output/fibonacci.trig +2 -19996
- package/examples/output/hanoi.trig +0 -10
- package/examples/output/spec-2-5-assignment-with-negation.trig +0 -1
- package/examples/output/sudoku.trig +0 -1
- package/examples/output/turing.trig +0 -5
- package/eyeleng.js +63 -13
- package/package.json +1 -1
- package/reports/w3c-shacl12-rules-earl.ttl +89 -89
- package/src/backward.js +52 -7
- package/src/cli.js +3 -2
- package/src/engine.js +2 -1
- package/src/query.js +6 -3
- package/test/api.test.js +54 -0
- package/test/cli.test.js +2 -0
- package/test/perf-baseline.json +7 -7
package/README.md
CHANGED
|
@@ -210,7 +210,8 @@ Important options:
|
|
|
210
210
|
--query TEXT run a raw SRL body pattern over the closure or backward planner
|
|
211
211
|
--query-file FILE read a raw SRL body pattern from a file
|
|
212
212
|
--query-mode MODE use auto, forward, or backward query planning (default auto)
|
|
213
|
-
--hybrid
|
|
213
|
+
--hybrid force aggressive hybrid orientation for function-like rules
|
|
214
|
+
--no-hybrid disable automatic hybrid forward/backward execution
|
|
214
215
|
--max-iterations N stop after N fixpoint iterations within a recursive layer
|
|
215
216
|
--no-imports parse IMPORTS/owl:imports but do not load imported rule sets
|
|
216
217
|
--rdf-messages parse input as an RDF Message Log
|
|
@@ -251,10 +252,11 @@ console.log(formatBindings(result.query.bindings, result.prefixes));
|
|
|
251
252
|
// hybrid plan before falling back to plain forward closure.
|
|
252
253
|
const justInTime = runQuery(source, ':alice :computedValue ?value', { queryMode: 'backward' });
|
|
253
254
|
|
|
254
|
-
//
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
|
|
255
|
+
// Run mode uses conservative auto-hybrid planning by default. It keeps
|
|
256
|
+
// ordinary rules materialized, but can prove demanded function-like predicates
|
|
257
|
+
// backward with tabling. Pass { hybrid: false } to force pure forward closure,
|
|
258
|
+
// or { hybrid: true } to force aggressive hybrid orientation.
|
|
259
|
+
const resultWithAutoHybrid = run(source);
|
|
258
260
|
|
|
259
261
|
// The backward planner is demand-driven: irrelevant unsupported rules do not
|
|
260
262
|
// prevent a query from using tabled backward proving.
|
|
@@ -4088,7 +4088,8 @@
|
|
|
4088
4088
|
const relaxedRecursiveRunOnce = options.relaxedRecursion === false
|
|
4089
4089
|
? new Set()
|
|
4090
4090
|
: recursiveTermGenerationRuleIndexes(analysis);
|
|
4091
|
-
const
|
|
4091
|
+
const useHybrid = options.hybrid !== false && !options.shacl12Conformance;
|
|
4092
|
+
const hybridBackwardPredicates = useHybrid || options.backwardBodyCalls
|
|
4092
4093
|
? preferredBackwardPredicates(program, options)
|
|
4093
4094
|
: new Set();
|
|
4094
4095
|
const hybridBackwardRules = new Set();
|
|
@@ -5624,10 +5625,7 @@
|
|
|
5624
5625
|
const entry = this.memo.get(key);
|
|
5625
5626
|
if (entry && entry.complete) {
|
|
5626
5627
|
this.stats.memoHits += 1;
|
|
5627
|
-
|
|
5628
|
-
const next = unifyTriples(pattern, answer, binding);
|
|
5629
|
-
if (next) yield next;
|
|
5630
|
-
}
|
|
5628
|
+
yield* this.replayAnswers(pattern, binding, entry.answers);
|
|
5631
5629
|
return;
|
|
5632
5630
|
}
|
|
5633
5631
|
if (this.active.has(key)) return;
|
|
@@ -5641,7 +5639,6 @@
|
|
|
5641
5639
|
if (!next) continue;
|
|
5642
5640
|
rememberAnswer(answers, answerKeys, pattern, next);
|
|
5643
5641
|
this.stats.facts += 1;
|
|
5644
|
-
yield next;
|
|
5645
5642
|
}
|
|
5646
5643
|
|
|
5647
5644
|
for (const item of this.ruleCandidates(resolvedPattern)) {
|
|
@@ -5653,7 +5650,6 @@
|
|
|
5653
5650
|
for (const solved of this.solveBody(freshBody, next, depth + 1, 0)) {
|
|
5654
5651
|
rememberAnswer(answers, answerKeys, pattern, solved);
|
|
5655
5652
|
this.stats.rules += 1;
|
|
5656
|
-
yield solved;
|
|
5657
5653
|
}
|
|
5658
5654
|
}
|
|
5659
5655
|
} finally {
|
|
@@ -5662,6 +5658,14 @@
|
|
|
5662
5658
|
|
|
5663
5659
|
this.memo.set(key, { complete: true, answers });
|
|
5664
5660
|
this.stats.memoStores += 1;
|
|
5661
|
+
yield* this.replayAnswers(pattern, binding, answers);
|
|
5662
|
+
}
|
|
5663
|
+
|
|
5664
|
+
*replayAnswers(pattern, binding, answers) {
|
|
5665
|
+
for (const answer of answers) {
|
|
5666
|
+
const next = unifyTriples(pattern, answer, binding);
|
|
5667
|
+
if (next) yield next;
|
|
5668
|
+
}
|
|
5665
5669
|
}
|
|
5666
5670
|
|
|
5667
5671
|
factCandidates(pattern, binding) {
|
|
@@ -5941,15 +5945,57 @@
|
|
|
5941
5945
|
if (explicit) return supportedBackwardPredicates(program, { ...options, hybridPredicates: explicit });
|
|
5942
5946
|
const supported = supportedBackwardPredicates(program, options);
|
|
5943
5947
|
const preferred = new Set();
|
|
5948
|
+
const force = options.hybrid === true || options.hybridMode === 'force';
|
|
5949
|
+
const demanded = force ? null : demandedBodyPredicates(program);
|
|
5944
5950
|
for (const rule of program.rules || []) {
|
|
5945
5951
|
if (!ruleIsFunctionLike(rule)) continue;
|
|
5952
|
+
if (!force && ruleCreatesHeadTerms(rule)) continue;
|
|
5946
5953
|
for (const head of rule.head || []) {
|
|
5947
|
-
if (head
|
|
5954
|
+
if (!head || !head.p || head.p.type !== 'iri' || !supported.has(head.p.value)) continue;
|
|
5955
|
+
if (force || demanded.has(head.p.value)) preferred.add(head.p.value);
|
|
5948
5956
|
}
|
|
5949
5957
|
}
|
|
5950
5958
|
return preferred;
|
|
5951
5959
|
}
|
|
5952
5960
|
|
|
5961
|
+
function demandedBodyPredicates(program) {
|
|
5962
|
+
const out = new Set();
|
|
5963
|
+
for (const rule of program.rules || []) {
|
|
5964
|
+
for (const predicate of bodyPredicateDemands(rule.body || [])) if (predicate) out.add(predicate);
|
|
5965
|
+
}
|
|
5966
|
+
return out;
|
|
5967
|
+
}
|
|
5968
|
+
|
|
5969
|
+
|
|
5970
|
+
function ruleCreatesHeadTerms(rule) {
|
|
5971
|
+
const headVars = new Set();
|
|
5972
|
+
for (const triple of rule.head || []) {
|
|
5973
|
+
for (const term of [triple.s, triple.p, triple.o]) {
|
|
5974
|
+
if (!term) continue;
|
|
5975
|
+
if (term.type === 'blank') return true;
|
|
5976
|
+
if (term.type === 'var') headVars.add(term.value);
|
|
5977
|
+
}
|
|
5978
|
+
}
|
|
5979
|
+
if (headVars.size === 0) return false;
|
|
5980
|
+
for (const clause of rule.body || []) {
|
|
5981
|
+
if ((clause.type === 'set' || clause.type === 'bind') && headVars.has(clause.variable) && expressionCreatesTerm(clause.expr)) return true;
|
|
5982
|
+
}
|
|
5983
|
+
return false;
|
|
5984
|
+
}
|
|
5985
|
+
|
|
5986
|
+
function expressionCreatesTerm(expr) {
|
|
5987
|
+
if (!expr) return false;
|
|
5988
|
+
if (expr.type === 'call') {
|
|
5989
|
+
const name = String(expr.name || '').toUpperCase();
|
|
5990
|
+
if (name === 'BNODE' || name === 'IRI' || name === 'URI' || name === 'TRIPLE' || name === 'UUID' || name === 'STRUUID') return true;
|
|
5991
|
+
return (expr.args || []).some(expressionCreatesTerm);
|
|
5992
|
+
}
|
|
5993
|
+
if (expr.type === 'binary') return expressionCreatesTerm(expr.left) || expressionCreatesTerm(expr.right);
|
|
5994
|
+
if (expr.type === 'unary') return expressionCreatesTerm(expr.expr);
|
|
5995
|
+
if (expr.type === 'in') return expressionCreatesTerm(expr.left) || (expr.values || []).some(expressionCreatesTerm);
|
|
5996
|
+
return false;
|
|
5997
|
+
}
|
|
5998
|
+
|
|
5953
5999
|
function ruleIsFunctionLike(rule) {
|
|
5954
6000
|
return (rule.body || []).some((clause) => clause.type === 'set' || clause.type === 'bind');
|
|
5955
6001
|
}
|
|
@@ -6087,7 +6133,7 @@
|
|
|
6087
6133
|
prefixes: result.prefixes,
|
|
6088
6134
|
select,
|
|
6089
6135
|
bindings: projectBindings(bindings, select),
|
|
6090
|
-
mode:
|
|
6136
|
+
mode: result.hybridStats ? 'hybrid' : 'forward',
|
|
6091
6137
|
};
|
|
6092
6138
|
}
|
|
6093
6139
|
|
|
@@ -6153,13 +6199,16 @@
|
|
|
6153
6199
|
}
|
|
6154
6200
|
|
|
6155
6201
|
function queryRunOptions(program, querySpec, options = {}) {
|
|
6156
|
-
|
|
6202
|
+
const mode = options.queryMode || 'auto';
|
|
6203
|
+
if (mode === 'forward') return { ...options, hybrid: false };
|
|
6204
|
+
if (shouldUseHybridForQuery(program, querySpec, options)) return { ...options, hybrid: options.hybrid ?? 'auto' };
|
|
6157
6205
|
return options;
|
|
6158
6206
|
}
|
|
6159
6207
|
|
|
6160
6208
|
function shouldUseHybridForQuery(program, querySpec, options = {}) {
|
|
6161
6209
|
const mode = options.queryMode || 'auto';
|
|
6162
|
-
if (options.hybrid
|
|
6210
|
+
if (options.hybrid === false) return false;
|
|
6211
|
+
if (options.hybrid === true) return true;
|
|
6163
6212
|
if (mode !== 'auto') return false;
|
|
6164
6213
|
if (!querySpec) return false;
|
|
6165
6214
|
return preferredBackwardPredicates(program, options).size > 0;
|