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 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 orient function-like rules backward during forward execution and queries
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
- // Explicit hybrid mode keeps forward materialization for ordinary rules, but
255
- // orients function-like derived predicates backward and proves them only when
256
- // a forward rule body or query asks for them.
257
- const hybrid = run(source, { hybrid: true });
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 hybridBackwardPredicates = options.hybrid || options.backwardBodyCalls
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
- for (const answer of entry.answers) {
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 && head.p && head.p.type === 'iri' && supported.has(head.p.value)) preferred.add(head.p.value);
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: options.queryMode === 'hybrid' || options.hybrid ? 'hybrid' : 'forward',
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
- if (shouldUseHybridForQuery(program, querySpec, options)) return { ...options, hybrid: true };
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 || mode === 'hybrid') return true;
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;