eyeleng 1.0.13 → 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 +24 -5
- package/dist/browser/eyeleng.browser.js +686 -14
- 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 +735 -24
- package/package.json +1 -1
- package/reports/w3c-shacl12-rules-earl.ttl +89 -89
- package/src/api.js +4 -1
- package/src/backward.js +535 -0
- package/src/cli.js +49 -10
- package/src/engine.js +69 -5
- package/src/query.js +72 -7
- package/src/store.js +2 -0
- package/test/api.test.js +181 -1
- package/test/cli.test.js +13 -2
- package/test/perf-baseline.json +7 -7
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
const { evaluate } = require('./engine.js');
|
|
17
17
|
const { analyze } = require('./analyze.js');
|
|
18
18
|
const { formatTriples, sortTriples, toJSON, formatTrace, formatBindings } = require('./format.js');
|
|
19
|
-
const { runQuery, queryResult } = require('./query.js');
|
|
19
|
+
const { runQuery, queryResult, queryProgram, queryRunOptions, shouldUseHybridForQuery } = require('./query.js');
|
|
20
20
|
const { resultTriples } = require('./output.js');
|
|
21
21
|
|
|
22
22
|
function parseInput(source, options = {}) {
|
|
@@ -130,6 +130,9 @@
|
|
|
130
130
|
runToString,
|
|
131
131
|
runQuery,
|
|
132
132
|
queryResult,
|
|
133
|
+
queryProgram,
|
|
134
|
+
queryRunOptions,
|
|
135
|
+
shouldUseHybridForQuery,
|
|
133
136
|
formatTriples,
|
|
134
137
|
formatBindings,
|
|
135
138
|
sortTriples,
|
|
@@ -4048,10 +4051,11 @@
|
|
|
4048
4051
|
"src/engine.js": function (require, module, exports) {
|
|
4049
4052
|
'use strict';
|
|
4050
4053
|
|
|
4051
|
-
const { TripleStore, bindingKey } = require('./store.js');
|
|
4054
|
+
const { TripleStore, bindingKey, instantiateTerm } = require('./store.js');
|
|
4052
4055
|
const { tripleKey, termKey, termEquals, blankNode, tripleTerm } = require('./term.js');
|
|
4053
4056
|
const { evalExpression, booleanValue, asTerm } = require('./builtins.js');
|
|
4054
4057
|
const { analyze } = require('./analyze.js');
|
|
4058
|
+
const { BackwardProver, preferredBackwardPredicates, ruleIsBackwardOriented } = require('./backward.js');
|
|
4055
4059
|
|
|
4056
4060
|
function evaluate(program, options = {}) {
|
|
4057
4061
|
const maxIterations = options.maxIterations ?? 10000;
|
|
@@ -4067,6 +4071,7 @@
|
|
|
4067
4071
|
applications: 0,
|
|
4068
4072
|
added: 0,
|
|
4069
4073
|
runOnce: !!rule.runOnce,
|
|
4074
|
+
backward: false,
|
|
4070
4075
|
}));
|
|
4071
4076
|
|
|
4072
4077
|
const analysis = options.analysis || analyze(program, options);
|
|
@@ -4083,6 +4088,18 @@
|
|
|
4083
4088
|
const relaxedRecursiveRunOnce = options.relaxedRecursion === false
|
|
4084
4089
|
? new Set()
|
|
4085
4090
|
: recursiveTermGenerationRuleIndexes(analysis);
|
|
4091
|
+
const useHybrid = options.hybrid !== false && !options.shacl12Conformance;
|
|
4092
|
+
const hybridBackwardPredicates = useHybrid || options.backwardBodyCalls
|
|
4093
|
+
? preferredBackwardPredicates(program, options)
|
|
4094
|
+
: new Set();
|
|
4095
|
+
const hybridBackwardRules = new Set();
|
|
4096
|
+
if (hybridBackwardPredicates.size > 0) {
|
|
4097
|
+
for (let ruleIndex = 0; ruleIndex < program.rules.length; ruleIndex += 1) {
|
|
4098
|
+
if (ruleIsBackwardOriented(program.rules[ruleIndex], hybridBackwardPredicates)) hybridBackwardRules.add(ruleIndex);
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
const hybridStats = hybridBackwardPredicates.size > 0 ? emptyBackwardStats() : null;
|
|
4102
|
+
for (const ruleIndex of hybridBackwardRules) perRule[ruleIndex].backward = true;
|
|
4086
4103
|
const baseContext = {
|
|
4087
4104
|
...evalOptions,
|
|
4088
4105
|
maxIterations,
|
|
@@ -4094,12 +4111,16 @@
|
|
|
4094
4111
|
iteration: 0,
|
|
4095
4112
|
startingIterations: 0,
|
|
4096
4113
|
recursiveLayer: false,
|
|
4114
|
+
hybridBackwardPredicates,
|
|
4115
|
+
hybridBackwardRules,
|
|
4116
|
+
hybridStats,
|
|
4097
4117
|
};
|
|
4098
4118
|
|
|
4099
4119
|
for (let layerIndex = 0; layerIndex < layerIndexes.length; layerIndex += 1) {
|
|
4100
4120
|
const layer = layerIndexes[layerIndex];
|
|
4101
|
-
const
|
|
4102
|
-
const
|
|
4121
|
+
const forwardLayer = hybridBackwardRules.size > 0 ? layer.filter((ruleIndex) => !hybridBackwardRules.has(ruleIndex)) : layer;
|
|
4122
|
+
const ordinary = forwardLayer.filter((ruleIndex) => !program.rules[ruleIndex].runOnce || relaxedRecursiveRunOnce.has(ruleIndex));
|
|
4123
|
+
const runOnce = forwardLayer.filter((ruleIndex) => program.rules[ruleIndex].runOnce && !relaxedRecursiveRunOnce.has(ruleIndex));
|
|
4103
4124
|
|
|
4104
4125
|
if (runOnce.length > 0) {
|
|
4105
4126
|
iterations += 1;
|
|
@@ -4132,6 +4153,7 @@
|
|
|
4132
4153
|
ruleApplications,
|
|
4133
4154
|
perRule,
|
|
4134
4155
|
trace,
|
|
4156
|
+
hybridStats,
|
|
4135
4157
|
};
|
|
4136
4158
|
}
|
|
4137
4159
|
|
|
@@ -4201,9 +4223,10 @@
|
|
|
4201
4223
|
const seenBindings = dedupeBindings ? new Set() : null;
|
|
4202
4224
|
const headBlankLabels = collectHeadBlankLabels(rule.head);
|
|
4203
4225
|
|
|
4204
|
-
const
|
|
4226
|
+
const bodyContext = prepareBodyContext(program, store, context);
|
|
4227
|
+
const bodyBindings = rule.body.length === 1 && rule.body[0].type === 'triple' && !shouldUseBackwardForTriple(rule.body[0].triple, {}, bodyContext)
|
|
4205
4228
|
? store.match(rule.body[0].triple, {})
|
|
4206
|
-
: evaluateBodyStream(rule.body, store, {},
|
|
4229
|
+
: evaluateBodyStream(rule.body, store, {}, bodyContext);
|
|
4207
4230
|
|
|
4208
4231
|
for (const binding of bodyBindings) {
|
|
4209
4232
|
if (seenBindings) {
|
|
@@ -4236,9 +4259,36 @@
|
|
|
4236
4259
|
}
|
|
4237
4260
|
}
|
|
4238
4261
|
|
|
4262
|
+
if (bodyContext.backwardProver && context.hybridStats) mergeBackwardStats(context.hybridStats, bodyContext.backwardProver.stats);
|
|
4239
4263
|
return { applications, added };
|
|
4240
4264
|
}
|
|
4241
4265
|
|
|
4266
|
+
function prepareBodyContext(program, store, context) {
|
|
4267
|
+
if (!context.hybridBackwardPredicates || context.hybridBackwardPredicates.size === 0) return context;
|
|
4268
|
+
return {
|
|
4269
|
+
...context,
|
|
4270
|
+
backwardProver: new BackwardProver(program, {
|
|
4271
|
+
...context,
|
|
4272
|
+
store,
|
|
4273
|
+
allowedPredicates: context.hybridBackwardPredicates,
|
|
4274
|
+
}),
|
|
4275
|
+
};
|
|
4276
|
+
}
|
|
4277
|
+
|
|
4278
|
+
function emptyBackwardStats() {
|
|
4279
|
+
return { mode: 'hybrid', goals: 0, facts: 0, rules: 0, memoHits: 0, memoStores: 0, maxDepth: 0 };
|
|
4280
|
+
}
|
|
4281
|
+
|
|
4282
|
+
function mergeBackwardStats(total, item) {
|
|
4283
|
+
if (!total || !item) return;
|
|
4284
|
+
total.goals += item.goals || 0;
|
|
4285
|
+
total.facts += item.facts || 0;
|
|
4286
|
+
total.rules += item.rules || 0;
|
|
4287
|
+
total.memoHits += item.memoHits || 0;
|
|
4288
|
+
total.memoStores += item.memoStores || 0;
|
|
4289
|
+
total.maxDepth = Math.max(total.maxDepth || 0, item.maxDepth || 0);
|
|
4290
|
+
}
|
|
4291
|
+
|
|
4242
4292
|
function recursiveTermGenerationRuleIndexes(analysis) {
|
|
4243
4293
|
const out = new Set();
|
|
4244
4294
|
if (!analysis || !analysis.dependency || !analysis.diagnostics) return out;
|
|
@@ -4347,9 +4397,20 @@
|
|
|
4347
4397
|
|
|
4348
4398
|
const clause = clauses[index];
|
|
4349
4399
|
if (clause.type === 'triple') {
|
|
4400
|
+
const seen = new Set();
|
|
4350
4401
|
for (const matched of store.match(clause.triple, initialBinding)) {
|
|
4402
|
+
const key = bindingKey(matched);
|
|
4403
|
+
seen.add(key);
|
|
4351
4404
|
yield* evaluateBodyStream(clauses, store, matched, options, index + 1);
|
|
4352
4405
|
}
|
|
4406
|
+
if (shouldUseBackwardForTriple(clause.triple, initialBinding, options)) {
|
|
4407
|
+
for (const matched of options.backwardProver.solveTriple(clause.triple, initialBinding)) {
|
|
4408
|
+
const key = bindingKey(matched);
|
|
4409
|
+
if (seen.has(key)) continue;
|
|
4410
|
+
seen.add(key);
|
|
4411
|
+
yield* evaluateBodyStream(clauses, store, matched, options, index + 1);
|
|
4412
|
+
}
|
|
4413
|
+
}
|
|
4353
4414
|
return;
|
|
4354
4415
|
}
|
|
4355
4416
|
|
|
@@ -4395,6 +4456,12 @@
|
|
|
4395
4456
|
throw new Error(`Unsupported body clause ${clause.type}`);
|
|
4396
4457
|
}
|
|
4397
4458
|
|
|
4459
|
+
function shouldUseBackwardForTriple(pattern, binding, options = {}) {
|
|
4460
|
+
if (!options.backwardProver || !options.hybridBackwardPredicates || options.hybridBackwardPredicates.size === 0) return false;
|
|
4461
|
+
const predicate = instantiateTerm(pattern.p, binding);
|
|
4462
|
+
return !!(predicate && predicate.type === 'iri' && options.hybridBackwardPredicates.has(predicate.value));
|
|
4463
|
+
}
|
|
4464
|
+
|
|
4398
4465
|
function bodyHasAny(clauses, store, initialBinding, options) {
|
|
4399
4466
|
for (const _ of evaluateBodyStream(clauses, store, initialBinding, options)) return true;
|
|
4400
4467
|
return false;
|
|
@@ -4427,6 +4494,7 @@
|
|
|
4427
4494
|
this.byPredicate = new Map();
|
|
4428
4495
|
this.byPredicateSubject = new Map();
|
|
4429
4496
|
this.byPredicateObject = new Map();
|
|
4497
|
+
this.version = 0;
|
|
4430
4498
|
for (const triple of triples) this.add(triple);
|
|
4431
4499
|
}
|
|
4432
4500
|
|
|
@@ -4441,6 +4509,7 @@
|
|
|
4441
4509
|
addIndex(this.byPredicate, predicate, key, normalized);
|
|
4442
4510
|
addNestedIndex(this.byPredicateSubject, predicate, subject, key, normalized);
|
|
4443
4511
|
addNestedIndex(this.byPredicateObject, predicate, object, key, normalized);
|
|
4512
|
+
this.version += 1;
|
|
4444
4513
|
return true;
|
|
4445
4514
|
}
|
|
4446
4515
|
|
|
@@ -5422,6 +5491,544 @@
|
|
|
5422
5491
|
canPossiblyGenerate,
|
|
5423
5492
|
};
|
|
5424
5493
|
|
|
5494
|
+
},
|
|
5495
|
+
"src/backward.js": function (require, module, exports) {
|
|
5496
|
+
'use strict';
|
|
5497
|
+
|
|
5498
|
+
const { TripleStore, bindingKey } = require('./store.js');
|
|
5499
|
+
const { tripleKey, termKey, termEquals } = require('./term.js');
|
|
5500
|
+
const { evalExpression, booleanValue, asTerm } = require('./builtins.js');
|
|
5501
|
+
|
|
5502
|
+
function backwardQuery(program, querySpec, options = {}) {
|
|
5503
|
+
const planner = planBackwardQuery(program, querySpec, options);
|
|
5504
|
+
if (!planner.ok) return { ok: false, reason: planner.reason };
|
|
5505
|
+
const prover = new BackwardProver(program, { ...options, allowedRuleIndexes: planner.ruleIndexes });
|
|
5506
|
+
const bindings = uniqueBindings(Array.from(prover.solveBody(querySpec.body, {})));
|
|
5507
|
+
return { ok: true, bindings, stats: prover.stats, plan: planner };
|
|
5508
|
+
}
|
|
5509
|
+
|
|
5510
|
+
function planBackwardQuery(program, querySpec, options = {}) {
|
|
5511
|
+
const clauses = querySpec && Array.isArray(querySpec.body) ? querySpec.body : [];
|
|
5512
|
+
if (!bodySupported(clauses, options)) return { ok: false, reason: 'query body contains clauses not supported by the backward prover yet' };
|
|
5513
|
+
|
|
5514
|
+
const reachable = reachableBackwardRuleIndexes(program, clauses, options);
|
|
5515
|
+
for (const ruleIndex of reachable.ruleIndexes) {
|
|
5516
|
+
const rule = (program.rules || [])[ruleIndex];
|
|
5517
|
+
if (!ruleSupported(rule, options)) return { ok: false, reason: `reachable rule ${rule.name || '<anonymous>'} is not supported by the backward prover yet` };
|
|
5518
|
+
}
|
|
5519
|
+
return { ok: true, ruleIndexes: reachable.ruleIndexes, predicates: reachable.predicates };
|
|
5520
|
+
}
|
|
5521
|
+
|
|
5522
|
+
function bodySupported(clauses, options = {}) {
|
|
5523
|
+
for (const clause of clauses || []) {
|
|
5524
|
+
if (clause.type === 'triple' || clause.type === 'filter' || clause.type === 'set' || clause.type === 'bind') continue;
|
|
5525
|
+
if (clause.type === 'not') {
|
|
5526
|
+
if (options.backwardNegation === false) return false;
|
|
5527
|
+
if (!bodySupported(clause.body, options)) return false;
|
|
5528
|
+
continue;
|
|
5529
|
+
}
|
|
5530
|
+
return false;
|
|
5531
|
+
}
|
|
5532
|
+
return true;
|
|
5533
|
+
}
|
|
5534
|
+
|
|
5535
|
+
function ruleSupported(rule, options = {}) {
|
|
5536
|
+
if (!Array.isArray(rule.head) || rule.head.length === 0) return false;
|
|
5537
|
+
for (const head of rule.head) {
|
|
5538
|
+
if (!head || !head.p || head.p.type !== 'iri') return false;
|
|
5539
|
+
if (containsBlank(head.s) || containsBlank(head.p) || containsBlank(head.o)) return false;
|
|
5540
|
+
}
|
|
5541
|
+
return bodySupported(rule.body || [], options);
|
|
5542
|
+
}
|
|
5543
|
+
|
|
5544
|
+
class BackwardProver {
|
|
5545
|
+
constructor(program, options = {}) {
|
|
5546
|
+
this.program = program;
|
|
5547
|
+
this.options = options;
|
|
5548
|
+
this.store = options.store || new TripleStore(program.data || []);
|
|
5549
|
+
this.maxDepth = options.backwardMaxDepth || options.maxDepth || 10000;
|
|
5550
|
+
this.solutionLimit = options.backwardSolutionLimit || options.solutionLimit || 1000000;
|
|
5551
|
+
this.allowedPredicates = normalizePredicateSet(options.allowedPredicates || options.backwardPredicates || null);
|
|
5552
|
+
this.allowedRuleIndexes = normalizeRuleIndexSet(options.allowedRuleIndexes || null);
|
|
5553
|
+
this.ruleHeads = indexRuleHeads(program.rules || [], { allowedPredicates: this.allowedPredicates, allowedRuleIndexes: this.allowedRuleIndexes });
|
|
5554
|
+
this.memo = new Map();
|
|
5555
|
+
this.active = new Set();
|
|
5556
|
+
this.freshCounter = 0;
|
|
5557
|
+
this.solutionCount = 0;
|
|
5558
|
+
this.stats = {
|
|
5559
|
+
mode: 'backward',
|
|
5560
|
+
goals: 0,
|
|
5561
|
+
facts: 0,
|
|
5562
|
+
rules: 0,
|
|
5563
|
+
memoHits: 0,
|
|
5564
|
+
memoStores: 0,
|
|
5565
|
+
maxDepth: 0,
|
|
5566
|
+
};
|
|
5567
|
+
}
|
|
5568
|
+
|
|
5569
|
+
*solveBody(clauses, binding = {}, depth = 0, index = 0) {
|
|
5570
|
+
if (depth > this.maxDepth) throw new Error(`Reached backwardMaxDepth=${this.maxDepth}; backward query may not terminate`);
|
|
5571
|
+
this.stats.maxDepth = Math.max(this.stats.maxDepth, depth);
|
|
5572
|
+
if (this.solutionCount >= this.solutionLimit) return;
|
|
5573
|
+
if (index >= clauses.length) {
|
|
5574
|
+
this.solutionCount += 1;
|
|
5575
|
+
yield resolveBinding(binding);
|
|
5576
|
+
return;
|
|
5577
|
+
}
|
|
5578
|
+
|
|
5579
|
+
const clause = clauses[index];
|
|
5580
|
+
if (clause.type === 'triple') {
|
|
5581
|
+
for (const matched of this.solveTriple(clause.triple, binding, depth + 1)) {
|
|
5582
|
+
yield* this.solveBody(clauses, matched, depth + 1, index + 1);
|
|
5583
|
+
}
|
|
5584
|
+
return;
|
|
5585
|
+
}
|
|
5586
|
+
|
|
5587
|
+
if (clause.type === 'filter') {
|
|
5588
|
+
try {
|
|
5589
|
+
if (booleanValue(evalExpression(clause.expr, resolveBinding(binding), this.options))) {
|
|
5590
|
+
yield* this.solveBody(clauses, binding, depth + 1, index + 1);
|
|
5591
|
+
}
|
|
5592
|
+
} catch (_) {
|
|
5593
|
+
// SPARQL-style FILTER errors reject the current solution.
|
|
5594
|
+
}
|
|
5595
|
+
return;
|
|
5596
|
+
}
|
|
5597
|
+
|
|
5598
|
+
if (clause.type === 'set' || clause.type === 'bind') {
|
|
5599
|
+
try {
|
|
5600
|
+
const resolved = resolveBinding(binding);
|
|
5601
|
+
const value = asTerm(evalExpression(clause.expr, resolved, this.options));
|
|
5602
|
+
const next = unifyTerms({ type: 'var', value: clause.variable }, value, binding);
|
|
5603
|
+
if (next) yield* this.solveBody(clauses, next, depth + 1, index + 1);
|
|
5604
|
+
} catch (_) {
|
|
5605
|
+
// Assignment errors drop the current solution.
|
|
5606
|
+
}
|
|
5607
|
+
return;
|
|
5608
|
+
}
|
|
5609
|
+
|
|
5610
|
+
if (clause.type === 'not') {
|
|
5611
|
+
let found = false;
|
|
5612
|
+
for (const _ of this.solveBody(clause.body, { ...binding }, depth + 1, 0)) { found = true; break; }
|
|
5613
|
+
if (!found) yield* this.solveBody(clauses, binding, depth + 1, index + 1);
|
|
5614
|
+
return;
|
|
5615
|
+
}
|
|
5616
|
+
|
|
5617
|
+
throw new Error(`Unsupported backward body clause ${clause.type}`);
|
|
5618
|
+
}
|
|
5619
|
+
|
|
5620
|
+
*solveTriple(pattern, binding = {}, depth = 0) {
|
|
5621
|
+
if (depth > this.maxDepth) throw new Error(`Reached backwardMaxDepth=${this.maxDepth}; backward query may not terminate`);
|
|
5622
|
+
this.stats.goals += 1;
|
|
5623
|
+
const resolvedPattern = resolvePattern(pattern, binding);
|
|
5624
|
+
const key = `${goalKey(resolvedPattern)}@store:${this.store.version || 0}`;
|
|
5625
|
+
const entry = this.memo.get(key);
|
|
5626
|
+
if (entry && entry.complete) {
|
|
5627
|
+
this.stats.memoHits += 1;
|
|
5628
|
+
yield* this.replayAnswers(pattern, binding, entry.answers);
|
|
5629
|
+
return;
|
|
5630
|
+
}
|
|
5631
|
+
if (this.active.has(key)) return;
|
|
5632
|
+
|
|
5633
|
+
const answers = [];
|
|
5634
|
+
const answerKeys = new Set();
|
|
5635
|
+
this.active.add(key);
|
|
5636
|
+
try {
|
|
5637
|
+
for (const fact of this.factCandidates(resolvedPattern, binding)) {
|
|
5638
|
+
const next = unifyTriples(pattern, fact, binding);
|
|
5639
|
+
if (!next) continue;
|
|
5640
|
+
rememberAnswer(answers, answerKeys, pattern, next);
|
|
5641
|
+
this.stats.facts += 1;
|
|
5642
|
+
}
|
|
5643
|
+
|
|
5644
|
+
for (const item of this.ruleCandidates(resolvedPattern)) {
|
|
5645
|
+
const suffix = `__b${++this.freshCounter}_${item.ruleIndex}`;
|
|
5646
|
+
const freshHead = freshTriple(item.head, suffix);
|
|
5647
|
+
const next = unifyTriples(pattern, freshHead, binding);
|
|
5648
|
+
if (!next) continue;
|
|
5649
|
+
const freshBody = (item.rule.body || []).map((clause) => freshClause(clause, suffix));
|
|
5650
|
+
for (const solved of this.solveBody(freshBody, next, depth + 1, 0)) {
|
|
5651
|
+
rememberAnswer(answers, answerKeys, pattern, solved);
|
|
5652
|
+
this.stats.rules += 1;
|
|
5653
|
+
}
|
|
5654
|
+
}
|
|
5655
|
+
} finally {
|
|
5656
|
+
this.active.delete(key);
|
|
5657
|
+
}
|
|
5658
|
+
|
|
5659
|
+
this.memo.set(key, { complete: true, answers });
|
|
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
|
+
}
|
|
5669
|
+
}
|
|
5670
|
+
|
|
5671
|
+
factCandidates(pattern, binding) {
|
|
5672
|
+
return this.store.candidates(pattern, binding);
|
|
5673
|
+
}
|
|
5674
|
+
|
|
5675
|
+
ruleCandidates(pattern) {
|
|
5676
|
+
const predicate = pattern.p && pattern.p.type === 'iri' ? pattern.p.value : null;
|
|
5677
|
+
if (predicate) return this.ruleHeads.byPredicate.get(predicate) || [];
|
|
5678
|
+
return this.ruleHeads.all;
|
|
5679
|
+
}
|
|
5680
|
+
}
|
|
5681
|
+
|
|
5682
|
+
function indexRuleHeads(rules, options = {}) {
|
|
5683
|
+
const allowedPredicates = options.allowedPredicates || null;
|
|
5684
|
+
const allowedRuleIndexes = options.allowedRuleIndexes || null;
|
|
5685
|
+
const byPredicate = new Map();
|
|
5686
|
+
const all = [];
|
|
5687
|
+
for (let ruleIndex = 0; ruleIndex < rules.length; ruleIndex += 1) {
|
|
5688
|
+
if (allowedRuleIndexes && !allowedRuleIndexes.has(ruleIndex)) continue;
|
|
5689
|
+
const rule = rules[ruleIndex];
|
|
5690
|
+
for (let headIndex = 0; headIndex < (rule.head || []).length; headIndex += 1) {
|
|
5691
|
+
const head = rule.head[headIndex];
|
|
5692
|
+
if (!head || !head.p || head.p.type !== 'iri') continue;
|
|
5693
|
+
if (allowedPredicates && !allowedPredicates.has(head.p.value)) continue;
|
|
5694
|
+
const item = { ruleIndex, headIndex, rule, head };
|
|
5695
|
+
all.push(item);
|
|
5696
|
+
const bucket = byPredicate.get(head.p.value);
|
|
5697
|
+
if (bucket) bucket.push(item);
|
|
5698
|
+
else byPredicate.set(head.p.value, [item]);
|
|
5699
|
+
}
|
|
5700
|
+
}
|
|
5701
|
+
return { byPredicate, all };
|
|
5702
|
+
}
|
|
5703
|
+
|
|
5704
|
+
function freshClause(clause, suffix) {
|
|
5705
|
+
if (clause.type === 'triple') return { ...clause, triple: freshTriple(clause.triple, suffix) };
|
|
5706
|
+
if (clause.type === 'filter') return { ...clause, expr: freshExpr(clause.expr, suffix) };
|
|
5707
|
+
if (clause.type === 'set' || clause.type === 'bind') return { ...clause, variable: freshVarName(clause.variable, suffix), expr: freshExpr(clause.expr, suffix) };
|
|
5708
|
+
if (clause.type === 'not') return { ...clause, body: clause.body.map((item) => freshClause(item, suffix)) };
|
|
5709
|
+
return clause;
|
|
5710
|
+
}
|
|
5711
|
+
|
|
5712
|
+
function freshTriple(triple, suffix) {
|
|
5713
|
+
return { s: freshTerm(triple.s, suffix), p: freshTerm(triple.p, suffix), o: freshTerm(triple.o, suffix) };
|
|
5714
|
+
}
|
|
5715
|
+
|
|
5716
|
+
function freshTerm(term, suffix) {
|
|
5717
|
+
if (!term) return term;
|
|
5718
|
+
if (term.type === 'var') return { type: 'var', value: freshVarName(term.value, suffix) };
|
|
5719
|
+
if (term.type === 'triple') return { type: 'triple', s: freshTerm(term.s, suffix), p: freshTerm(term.p, suffix), o: freshTerm(term.o, suffix) };
|
|
5720
|
+
return term;
|
|
5721
|
+
}
|
|
5722
|
+
|
|
5723
|
+
function freshExpr(expr, suffix) {
|
|
5724
|
+
if (!expr) return expr;
|
|
5725
|
+
if (expr.type === 'var') return { ...expr, name: freshVarName(expr.name, suffix) };
|
|
5726
|
+
if (expr.type === 'term') return { ...expr, value: freshTerm(expr.value, suffix) };
|
|
5727
|
+
if (expr.type === 'unary') return { ...expr, expr: freshExpr(expr.expr, suffix) };
|
|
5728
|
+
if (expr.type === 'binary') return { ...expr, left: freshExpr(expr.left, suffix), right: freshExpr(expr.right, suffix) };
|
|
5729
|
+
if (expr.type === 'call') return { ...expr, args: expr.args.map((arg) => freshExpr(arg, suffix)) };
|
|
5730
|
+
if (expr.type === 'list') return { ...expr, items: expr.items.map((item) => freshExpr(item, suffix)) };
|
|
5731
|
+
return expr;
|
|
5732
|
+
}
|
|
5733
|
+
|
|
5734
|
+
function freshVarName(name, suffix) {
|
|
5735
|
+
return `${name}${suffix}`;
|
|
5736
|
+
}
|
|
5737
|
+
|
|
5738
|
+
function resolvePattern(pattern, binding) {
|
|
5739
|
+
return { s: resolveTerm(pattern.s, binding, false), p: resolveTerm(pattern.p, binding, false), o: resolveTerm(pattern.o, binding, false) };
|
|
5740
|
+
}
|
|
5741
|
+
|
|
5742
|
+
function resolveBinding(binding) {
|
|
5743
|
+
const out = {};
|
|
5744
|
+
for (const name of Object.keys(binding)) out[name] = resolveTerm(binding[name], binding, false);
|
|
5745
|
+
return out;
|
|
5746
|
+
}
|
|
5747
|
+
|
|
5748
|
+
function resolveTerm(term, binding, preserveUnbound = true, seen = new Set()) {
|
|
5749
|
+
if (!term) return term;
|
|
5750
|
+
if (term.type === 'var') {
|
|
5751
|
+
const name = term.value;
|
|
5752
|
+
if (seen.has(name)) return preserveUnbound ? term : { type: 'var', value: name };
|
|
5753
|
+
if (!Object.prototype.hasOwnProperty.call(binding, name)) return preserveUnbound ? term : { type: 'var', value: name };
|
|
5754
|
+
seen.add(name);
|
|
5755
|
+
return resolveTerm(binding[name], binding, preserveUnbound, seen);
|
|
5756
|
+
}
|
|
5757
|
+
if (term.type === 'triple') {
|
|
5758
|
+
return {
|
|
5759
|
+
type: 'triple',
|
|
5760
|
+
s: resolveTerm(term.s, binding, preserveUnbound, new Set(seen)),
|
|
5761
|
+
p: resolveTerm(term.p, binding, preserveUnbound, new Set(seen)),
|
|
5762
|
+
o: resolveTerm(term.o, binding, preserveUnbound, new Set(seen)),
|
|
5763
|
+
};
|
|
5764
|
+
}
|
|
5765
|
+
return term;
|
|
5766
|
+
}
|
|
5767
|
+
|
|
5768
|
+
function unifyTriples(left, right, binding) {
|
|
5769
|
+
let next = unifyTerms(left.s, right.s, binding);
|
|
5770
|
+
if (!next) return null;
|
|
5771
|
+
next = unifyTerms(left.p, right.p, next);
|
|
5772
|
+
if (!next) return null;
|
|
5773
|
+
return unifyTerms(left.o, right.o, next);
|
|
5774
|
+
}
|
|
5775
|
+
|
|
5776
|
+
function unifyTerms(left, right, binding) {
|
|
5777
|
+
const a = resolveTerm(left, binding);
|
|
5778
|
+
const b = resolveTerm(right, binding);
|
|
5779
|
+
if (a.type === 'var' && b.type === 'var' && a.value === b.value) return binding;
|
|
5780
|
+
if (a.type === 'var') return bindVariable(a.value, b, binding);
|
|
5781
|
+
if (b.type === 'var') return bindVariable(b.value, a, binding);
|
|
5782
|
+
if (a.type === 'triple' || b.type === 'triple') {
|
|
5783
|
+
if (a.type !== 'triple' || b.type !== 'triple') return null;
|
|
5784
|
+
let next = unifyTerms(a.s, b.s, binding);
|
|
5785
|
+
if (!next) return null;
|
|
5786
|
+
next = unifyTerms(a.p, b.p, next);
|
|
5787
|
+
if (!next) return null;
|
|
5788
|
+
return unifyTerms(a.o, b.o, next);
|
|
5789
|
+
}
|
|
5790
|
+
return termEquals(a, b) ? binding : null;
|
|
5791
|
+
}
|
|
5792
|
+
|
|
5793
|
+
function bindVariable(name, term, binding) {
|
|
5794
|
+
const existing = binding[name];
|
|
5795
|
+
if (existing) return unifyTerms(existing, term, binding);
|
|
5796
|
+
if (term.type === 'var' && term.value === name) return binding;
|
|
5797
|
+
return { ...binding, [name]: term };
|
|
5798
|
+
}
|
|
5799
|
+
|
|
5800
|
+
function rememberAnswer(answers, answerKeys, pattern, binding) {
|
|
5801
|
+
const triple = {
|
|
5802
|
+
s: resolveTerm(pattern.s, binding),
|
|
5803
|
+
p: resolveTerm(pattern.p, binding),
|
|
5804
|
+
o: resolveTerm(pattern.o, binding),
|
|
5805
|
+
};
|
|
5806
|
+
if (triple.s.type === 'var' || triple.p.type === 'var' || triple.o.type === 'var') return;
|
|
5807
|
+
const key = tripleKey(triple);
|
|
5808
|
+
if (answerKeys.has(key)) return;
|
|
5809
|
+
answerKeys.add(key);
|
|
5810
|
+
answers.push(triple);
|
|
5811
|
+
}
|
|
5812
|
+
|
|
5813
|
+
function goalKey(pattern) {
|
|
5814
|
+
return `${safeTermKey(pattern.s)} ${safeTermKey(pattern.p)} ${safeTermKey(pattern.o)}`;
|
|
5815
|
+
}
|
|
5816
|
+
|
|
5817
|
+
function safeTermKey(term) {
|
|
5818
|
+
return term && term.type === 'var' ? '_' : termKey(term);
|
|
5819
|
+
}
|
|
5820
|
+
|
|
5821
|
+
function uniqueBindings(bindings) {
|
|
5822
|
+
const seen = new Set();
|
|
5823
|
+
const out = [];
|
|
5824
|
+
for (const binding of bindings) {
|
|
5825
|
+
const resolved = resolveBinding(binding);
|
|
5826
|
+
const key = bindingKey(resolved);
|
|
5827
|
+
if (seen.has(key)) continue;
|
|
5828
|
+
seen.add(key);
|
|
5829
|
+
out.push(resolved);
|
|
5830
|
+
}
|
|
5831
|
+
return out;
|
|
5832
|
+
}
|
|
5833
|
+
|
|
5834
|
+
function containsBlank(term) {
|
|
5835
|
+
if (!term) return false;
|
|
5836
|
+
if (term.type === 'blank') return true;
|
|
5837
|
+
if (term.type === 'triple') return containsBlank(term.s) || containsBlank(term.p) || containsBlank(term.o);
|
|
5838
|
+
return false;
|
|
5839
|
+
}
|
|
5840
|
+
|
|
5841
|
+
|
|
5842
|
+
|
|
5843
|
+
function reachableBackwardRuleIndexes(program, rootClauses, options = {}) {
|
|
5844
|
+
const rules = program.rules || [];
|
|
5845
|
+
const headIndex = new Map();
|
|
5846
|
+
const allHeadPredicates = new Set();
|
|
5847
|
+
const allRuleIndexes = new Set();
|
|
5848
|
+
for (let ruleIndex = 0; ruleIndex < rules.length; ruleIndex += 1) {
|
|
5849
|
+
const rule = rules[ruleIndex];
|
|
5850
|
+
for (const head of rule.head || []) {
|
|
5851
|
+
if (!head || !head.p || head.p.type !== 'iri') continue;
|
|
5852
|
+
allRuleIndexes.add(ruleIndex);
|
|
5853
|
+
allHeadPredicates.add(head.p.value);
|
|
5854
|
+
if (!headIndex.has(head.p.value)) headIndex.set(head.p.value, new Set());
|
|
5855
|
+
headIndex.get(head.p.value).add(ruleIndex);
|
|
5856
|
+
}
|
|
5857
|
+
}
|
|
5858
|
+
|
|
5859
|
+
const predicates = new Set();
|
|
5860
|
+
const ruleIndexes = new Set();
|
|
5861
|
+
const work = [];
|
|
5862
|
+
const enqueue = (predicate) => {
|
|
5863
|
+
if (!predicate) {
|
|
5864
|
+
for (const item of allHeadPredicates) enqueue(item);
|
|
5865
|
+
return;
|
|
5866
|
+
}
|
|
5867
|
+
if (predicates.has(predicate)) return;
|
|
5868
|
+
predicates.add(predicate);
|
|
5869
|
+
work.push(predicate);
|
|
5870
|
+
};
|
|
5871
|
+
|
|
5872
|
+
for (const predicate of bodyPredicateDemands(rootClauses || [])) enqueue(predicate);
|
|
5873
|
+
|
|
5874
|
+
while (work.length > 0) {
|
|
5875
|
+
const predicate = work.shift();
|
|
5876
|
+
const indexes = headIndex.get(predicate);
|
|
5877
|
+
if (!indexes) continue;
|
|
5878
|
+
for (const ruleIndex of indexes) {
|
|
5879
|
+
if (ruleIndexes.has(ruleIndex)) continue;
|
|
5880
|
+
ruleIndexes.add(ruleIndex);
|
|
5881
|
+
const rule = rules[ruleIndex];
|
|
5882
|
+
for (const needed of bodyPredicateDemands(rule.body || [])) enqueue(needed);
|
|
5883
|
+
}
|
|
5884
|
+
}
|
|
5885
|
+
|
|
5886
|
+
return { ruleIndexes, predicates };
|
|
5887
|
+
}
|
|
5888
|
+
|
|
5889
|
+
function bodyPredicateDemands(clauses) {
|
|
5890
|
+
const out = [];
|
|
5891
|
+
for (const clause of clauses || []) {
|
|
5892
|
+
if (clause.type === 'triple') {
|
|
5893
|
+
out.push(predicateDemand(clause.triple.p));
|
|
5894
|
+
continue;
|
|
5895
|
+
}
|
|
5896
|
+
if (clause.type === 'not') {
|
|
5897
|
+
out.push(...bodyPredicateDemands(clause.body || []));
|
|
5898
|
+
}
|
|
5899
|
+
}
|
|
5900
|
+
return out;
|
|
5901
|
+
}
|
|
5902
|
+
|
|
5903
|
+
function predicateDemand(term) {
|
|
5904
|
+
return term && term.type === 'iri' ? term.value : null;
|
|
5905
|
+
}
|
|
5906
|
+
|
|
5907
|
+
function normalizePredicateSet(value) {
|
|
5908
|
+
if (!value) return null;
|
|
5909
|
+
if (value instanceof Set) return value;
|
|
5910
|
+
if (Array.isArray(value)) return new Set(value);
|
|
5911
|
+
return new Set(String(value).split(',').map((item) => item.trim()).filter(Boolean));
|
|
5912
|
+
}
|
|
5913
|
+
|
|
5914
|
+
function normalizeRuleIndexSet(value) {
|
|
5915
|
+
if (!value) return null;
|
|
5916
|
+
if (value instanceof Set) return value;
|
|
5917
|
+
if (Array.isArray(value)) return new Set(value);
|
|
5918
|
+
return null;
|
|
5919
|
+
}
|
|
5920
|
+
|
|
5921
|
+
function supportedBackwardPredicates(program, options = {}) {
|
|
5922
|
+
const explicit = normalizePredicateSet(options.backwardPredicates || options.hybridPredicates || null);
|
|
5923
|
+
const byPredicate = new Map();
|
|
5924
|
+
for (const rule of program.rules || []) {
|
|
5925
|
+
const predicates = new Set();
|
|
5926
|
+
for (const head of rule.head || []) {
|
|
5927
|
+
if (head && head.p && head.p.type === 'iri') predicates.add(head.p.value);
|
|
5928
|
+
}
|
|
5929
|
+
for (const predicate of predicates) {
|
|
5930
|
+
if (!byPredicate.has(predicate)) byPredicate.set(predicate, []);
|
|
5931
|
+
byPredicate.get(predicate).push(rule);
|
|
5932
|
+
}
|
|
5933
|
+
}
|
|
5934
|
+
|
|
5935
|
+
const supported = new Set();
|
|
5936
|
+
for (const [predicate, rules] of byPredicate) {
|
|
5937
|
+
if (explicit && !explicit.has(predicate)) continue;
|
|
5938
|
+
if (rules.length > 0 && rules.every((rule) => ruleSupported(rule, options))) supported.add(predicate);
|
|
5939
|
+
}
|
|
5940
|
+
return supported;
|
|
5941
|
+
}
|
|
5942
|
+
|
|
5943
|
+
function preferredBackwardPredicates(program, options = {}) {
|
|
5944
|
+
const explicit = normalizePredicateSet(options.hybridPredicates || null);
|
|
5945
|
+
if (explicit) return supportedBackwardPredicates(program, { ...options, hybridPredicates: explicit });
|
|
5946
|
+
const supported = supportedBackwardPredicates(program, options);
|
|
5947
|
+
const preferred = new Set();
|
|
5948
|
+
const force = options.hybrid === true || options.hybridMode === 'force';
|
|
5949
|
+
const demanded = force ? null : demandedBodyPredicates(program);
|
|
5950
|
+
for (const rule of program.rules || []) {
|
|
5951
|
+
if (!ruleIsFunctionLike(rule)) continue;
|
|
5952
|
+
if (!force && ruleCreatesHeadTerms(rule)) continue;
|
|
5953
|
+
for (const head of rule.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);
|
|
5956
|
+
}
|
|
5957
|
+
}
|
|
5958
|
+
return preferred;
|
|
5959
|
+
}
|
|
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
|
+
|
|
5999
|
+
function ruleIsFunctionLike(rule) {
|
|
6000
|
+
return (rule.body || []).some((clause) => clause.type === 'set' || clause.type === 'bind');
|
|
6001
|
+
}
|
|
6002
|
+
|
|
6003
|
+
function ruleHeadPredicates(rule) {
|
|
6004
|
+
const predicates = new Set();
|
|
6005
|
+
for (const head of rule.head || []) {
|
|
6006
|
+
if (!head || !head.p || head.p.type !== 'iri') return null;
|
|
6007
|
+
predicates.add(head.p.value);
|
|
6008
|
+
}
|
|
6009
|
+
return predicates;
|
|
6010
|
+
}
|
|
6011
|
+
|
|
6012
|
+
function ruleIsBackwardOriented(rule, predicates) {
|
|
6013
|
+
if (!predicates || predicates.size === 0) return false;
|
|
6014
|
+
const heads = ruleHeadPredicates(rule);
|
|
6015
|
+
if (!heads || heads.size === 0) return false;
|
|
6016
|
+
for (const predicate of heads) if (!predicates.has(predicate)) return false;
|
|
6017
|
+
return true;
|
|
6018
|
+
}
|
|
6019
|
+
|
|
6020
|
+
module.exports = {
|
|
6021
|
+
BackwardProver,
|
|
6022
|
+
backwardQuery,
|
|
6023
|
+
planBackwardQuery,
|
|
6024
|
+
supportedBackwardPredicates,
|
|
6025
|
+
preferredBackwardPredicates,
|
|
6026
|
+
reachableBackwardRuleIndexes,
|
|
6027
|
+
ruleIsBackwardOriented,
|
|
6028
|
+
ruleSupported,
|
|
6029
|
+
resolveBinding,
|
|
6030
|
+
};
|
|
6031
|
+
|
|
5425
6032
|
},
|
|
5426
6033
|
"src/format.js": function (require, module, exports) {
|
|
5427
6034
|
'use strict';
|
|
@@ -5515,6 +6122,7 @@
|
|
|
5515
6122
|
const { parseQuery } = require('./parser.js');
|
|
5516
6123
|
const { TripleStore, bindingKey } = require('./store.js');
|
|
5517
6124
|
const { evaluateBody } = require('./engine.js');
|
|
6125
|
+
const { backwardQuery, planBackwardQuery, preferredBackwardPredicates } = require('./backward.js');
|
|
5518
6126
|
|
|
5519
6127
|
function queryResult(result, querySpec, options = {}) {
|
|
5520
6128
|
const store = new TripleStore(result.closure || []);
|
|
@@ -5525,28 +6133,92 @@
|
|
|
5525
6133
|
prefixes: result.prefixes,
|
|
5526
6134
|
select,
|
|
5527
6135
|
bindings: projectBindings(bindings, select),
|
|
6136
|
+
mode: result.hybridStats ? 'hybrid' : 'forward',
|
|
5528
6137
|
};
|
|
5529
6138
|
}
|
|
5530
6139
|
|
|
6140
|
+
function queryProgram(program, querySpec, options = {}) {
|
|
6141
|
+
const mode = options.queryMode || 'auto';
|
|
6142
|
+
if (mode !== 'forward' && mode !== 'hybrid') {
|
|
6143
|
+
const planned = planBackwardQuery(program, querySpec, options);
|
|
6144
|
+
if (planned.ok) {
|
|
6145
|
+
const result = backwardQuery(program, querySpec, options);
|
|
6146
|
+
if (result.ok) {
|
|
6147
|
+
const select = normalizeSelect(querySpec.select, result.bindings);
|
|
6148
|
+
return {
|
|
6149
|
+
baseIRI: program.baseIRI,
|
|
6150
|
+
prefixes: program.prefixes,
|
|
6151
|
+
select,
|
|
6152
|
+
bindings: projectBindings(result.bindings, select),
|
|
6153
|
+
mode: 'backward',
|
|
6154
|
+
stats: result.stats,
|
|
6155
|
+
};
|
|
6156
|
+
}
|
|
6157
|
+
if (mode === 'backward') throw new Error(result.reason || 'Backward query failed');
|
|
6158
|
+
} else if (mode === 'backward') {
|
|
6159
|
+
throw new Error(`Backward query is not supported for this ruleset: ${planned.reason}`);
|
|
6160
|
+
}
|
|
6161
|
+
}
|
|
6162
|
+
return null;
|
|
6163
|
+
}
|
|
6164
|
+
|
|
5531
6165
|
function runQuery(source, querySource = null, options = {}) {
|
|
5532
6166
|
const { run, compile } = require('./api.js');
|
|
5533
|
-
const { program, diagnostics } = compile(source, options);
|
|
5534
|
-
const result = run(program, options);
|
|
5535
|
-
result.diagnostics = diagnostics;
|
|
6167
|
+
const { program, diagnostics, analysis } = compile(source, options);
|
|
5536
6168
|
|
|
5537
6169
|
let querySpec;
|
|
5538
6170
|
if (querySource) querySpec = parseQuery(querySource, { ...options, prefixes: program.prefixes, baseIRI: program.baseIRI });
|
|
5539
6171
|
else throw new Error('No query supplied. Use --query or --query-file with a raw body pattern.');
|
|
5540
6172
|
|
|
5541
|
-
const
|
|
5542
|
-
|
|
6173
|
+
const direct = queryProgram(program, querySpec, options);
|
|
6174
|
+
if (direct) {
|
|
6175
|
+
return {
|
|
6176
|
+
baseIRI: program.baseIRI,
|
|
6177
|
+
version: program.version || null,
|
|
6178
|
+
imports: program.imports || [],
|
|
6179
|
+
prefixes: program.prefixes,
|
|
6180
|
+
input: program.data.slice(),
|
|
6181
|
+
inferred: [],
|
|
6182
|
+
closure: program.data.slice(),
|
|
6183
|
+
iterations: 0,
|
|
6184
|
+
layers: [],
|
|
6185
|
+
ruleApplications: 0,
|
|
6186
|
+
perRule: [],
|
|
6187
|
+
trace: [],
|
|
6188
|
+
diagnostics,
|
|
6189
|
+
analysis,
|
|
6190
|
+
query: direct,
|
|
6191
|
+
};
|
|
6192
|
+
}
|
|
6193
|
+
|
|
6194
|
+
const runOptions = queryRunOptions(program, querySpec, options);
|
|
6195
|
+
const result = run(program, runOptions);
|
|
6196
|
+
result.diagnostics = diagnostics;
|
|
6197
|
+
result.query = queryResult(result, querySpec, runOptions);
|
|
6198
|
+
return result;
|
|
6199
|
+
}
|
|
6200
|
+
|
|
6201
|
+
function queryRunOptions(program, querySpec, options = {}) {
|
|
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' };
|
|
6205
|
+
return options;
|
|
6206
|
+
}
|
|
6207
|
+
|
|
6208
|
+
function shouldUseHybridForQuery(program, querySpec, options = {}) {
|
|
6209
|
+
const mode = options.queryMode || 'auto';
|
|
6210
|
+
if (options.hybrid === false) return false;
|
|
6211
|
+
if (options.hybrid === true) return true;
|
|
6212
|
+
if (mode !== 'auto') return false;
|
|
6213
|
+
if (!querySpec) return false;
|
|
6214
|
+
return preferredBackwardPredicates(program, options).size > 0;
|
|
5543
6215
|
}
|
|
5544
6216
|
|
|
5545
6217
|
function normalizeSelect(select, bindings) {
|
|
5546
6218
|
if (select && select.length > 0) return select.slice();
|
|
5547
6219
|
const vars = new Set();
|
|
5548
6220
|
for (const binding of bindings) for (const key of Object.keys(binding)) vars.add(key);
|
|
5549
|
-
return Array.from(vars).sort();
|
|
6221
|
+
return Array.from(vars).sort().filter((name) => !name.includes('__b'));
|
|
5550
6222
|
}
|
|
5551
6223
|
|
|
5552
6224
|
function projectBindings(bindings, select) {
|
|
@@ -5564,7 +6236,7 @@
|
|
|
5564
6236
|
return out;
|
|
5565
6237
|
}
|
|
5566
6238
|
|
|
5567
|
-
module.exports = { runQuery, queryResult, parseQuery, normalizeSelect, projectBindings };
|
|
6239
|
+
module.exports = { runQuery, queryResult, queryProgram, queryRunOptions, shouldUseHybridForQuery, parseQuery, normalizeSelect, projectBindings };
|
|
5568
6240
|
|
|
5569
6241
|
},
|
|
5570
6242
|
"src/output.js": function (require, module, exports) {
|
|
@@ -5578,7 +6250,7 @@
|
|
|
5578
6250
|
|
|
5579
6251
|
},
|
|
5580
6252
|
};
|
|
5581
|
-
const __mappings = {"src/tokenizer.js":{},"src/assignments.js":{},"src/term.js":{},"src/rdfSyntax.js":{"./tokenizer.js":"src/tokenizer.js","./assignments.js":"src/assignments.js","./term.js":"src/term.js"},"src/builtins.js":{"./term.js":"src/term.js"},"src/parser.js":{"./tokenizer.js":"src/tokenizer.js","./rdfSyntax.js":"src/rdfSyntax.js","./builtins.js":"src/builtins.js","./assignments.js":"src/assignments.js","./term.js":"src/term.js"},"src/rdfMessages.js":{"./rdfSyntax.js":"src/rdfSyntax.js","./term.js":"src/term.js"},"src/store.js":{"./term.js":"src/term.js"},"src/analyze.js":{"./term.js":"src/term.js","./assignments.js":"src/assignments.js"},"src/engine.js":{"./store.js":"src/store.js","./term.js":"src/term.js","./builtins.js":"src/builtins.js","./analyze.js":"src/analyze.js"},"src/format.js":{"./term.js":"src/term.js"},"src/query.js":{"./parser.js":"src/parser.js","./store.js":"src/store.js","./engine.js":"src/engine.js","./api.js":"src/api.js"},"src/output.js":{},"src/api.js":{"./parser.js":"src/parser.js","./rdfSyntax.js":"src/rdfSyntax.js","./rdfMessages.js":"src/rdfMessages.js","./engine.js":"src/engine.js","./analyze.js":"src/analyze.js","./format.js":"src/format.js","./query.js":"src/query.js","./output.js":"src/output.js"},"src/index.js":{"./api.js":"src/api.js"}};
|
|
6253
|
+
const __mappings = {"src/tokenizer.js":{},"src/assignments.js":{},"src/term.js":{},"src/rdfSyntax.js":{"./tokenizer.js":"src/tokenizer.js","./assignments.js":"src/assignments.js","./term.js":"src/term.js"},"src/builtins.js":{"./term.js":"src/term.js"},"src/parser.js":{"./tokenizer.js":"src/tokenizer.js","./rdfSyntax.js":"src/rdfSyntax.js","./builtins.js":"src/builtins.js","./assignments.js":"src/assignments.js","./term.js":"src/term.js"},"src/rdfMessages.js":{"./rdfSyntax.js":"src/rdfSyntax.js","./term.js":"src/term.js"},"src/store.js":{"./term.js":"src/term.js"},"src/analyze.js":{"./term.js":"src/term.js","./assignments.js":"src/assignments.js"},"src/backward.js":{"./store.js":"src/store.js","./term.js":"src/term.js","./builtins.js":"src/builtins.js"},"src/engine.js":{"./store.js":"src/store.js","./term.js":"src/term.js","./builtins.js":"src/builtins.js","./analyze.js":"src/analyze.js","./backward.js":"src/backward.js"},"src/format.js":{"./term.js":"src/term.js"},"src/query.js":{"./parser.js":"src/parser.js","./store.js":"src/store.js","./engine.js":"src/engine.js","./backward.js":"src/backward.js","./api.js":"src/api.js"},"src/output.js":{},"src/api.js":{"./parser.js":"src/parser.js","./rdfSyntax.js":"src/rdfSyntax.js","./rdfMessages.js":"src/rdfMessages.js","./engine.js":"src/engine.js","./analyze.js":"src/analyze.js","./format.js":"src/format.js","./query.js":"src/query.js","./output.js":"src/output.js"},"src/index.js":{"./api.js":"src/api.js"}};
|
|
5582
6254
|
const __cache = {};
|
|
5583
6255
|
function __require(id) {
|
|
5584
6256
|
if (__cache[id]) return __cache[id].exports;
|