eyeling 1.7.1 → 1.7.2

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.
Files changed (2) hide show
  1. package/eyeling.js +87 -0
  2. package/package.json +1 -1
package/eyeling.js CHANGED
@@ -4638,6 +4638,58 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
4638
4638
  return [{ ...subst }];
4639
4639
  }
4640
4640
 
4641
+ // log:conjunction
4642
+ // Schema: ( $s.i+ )+ log:conjunction $o?
4643
+ // $o is a formula containing a copy of each formula in the subject list.
4644
+ // Duplicates are removed.
4645
+ if (pv === LOG_NS + 'conjunction') {
4646
+ if (!(g.s instanceof ListTerm)) return [];
4647
+
4648
+ const parts = g.s.elems;
4649
+ if (!parts.length) return [];
4650
+
4651
+ /** @type {Triple[]} */
4652
+ const merged = [];
4653
+
4654
+ // Fast-path dedup for IRI/Literal-only triples.
4655
+ const fastKeySet = new Set();
4656
+
4657
+ for (const part of parts) {
4658
+ // Support the empty formula token `true`.
4659
+ if (part instanceof Literal && part.value === 'true') continue;
4660
+
4661
+ if (!(part instanceof GraphTerm)) return [];
4662
+
4663
+ for (const tr of part.triples) {
4664
+ const k = tripleFastKey(tr);
4665
+ if (k !== null) {
4666
+ if (fastKeySet.has(k)) continue;
4667
+ fastKeySet.add(k);
4668
+ merged.push(tr);
4669
+ continue;
4670
+ }
4671
+
4672
+ // Fallback: structural equality (still respects plain-string == xsd:string).
4673
+ let dup = false;
4674
+ for (const ex of merged) {
4675
+ if (triplesEqual(tr, ex)) {
4676
+ dup = true;
4677
+ break;
4678
+ }
4679
+ }
4680
+ if (!dup) merged.push(tr);
4681
+ }
4682
+ }
4683
+
4684
+ const outFormula = new GraphTerm(merged);
4685
+
4686
+ // Allow blank nodes as a don't-care output (common in builtin schemas).
4687
+ if (g.o instanceof Blank) return [{ ...subst }];
4688
+
4689
+ const s2 = unifyTerm(g.o, outFormula, subst);
4690
+ return s2 !== null ? [s2] : [];
4691
+ }
4692
+
4641
4693
  // log:dtlit
4642
4694
  // Schema: ( $s.1? $s.2? )? log:dtlit $o?
4643
4695
  // true iff $o is a datatyped literal with string value $s.1 and datatype IRI $s.2
@@ -4804,6 +4856,41 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
4804
4856
  return results;
4805
4857
  }
4806
4858
 
4859
+ // log:includes (provable in scope)
4860
+ // Schema: $s+ log:includes $o+
4861
+ // When the subject is a formula, the scope is that concrete formula (syntactic containment).
4862
+ // Otherwise, the scope is the current document scope (facts + backward rules).
4863
+ if (pv === LOG_NS + 'includes') {
4864
+ // Empty formula is always included.
4865
+ if (g.o instanceof Literal && g.o.value === 'true') return [{ ...subst }];
4866
+ if (!(g.o instanceof GraphTerm)) return [];
4867
+
4868
+ /** @type {Triple[] | null} */
4869
+ let scopeFacts = null;
4870
+ /** @type {Rule[]} */
4871
+ let scopeBackRules = backRules;
4872
+
4873
+ // If the subject is a formula, treat it as a concrete scope graph.
4874
+ // Also support `true` as the empty formula.
4875
+ if (g.s instanceof GraphTerm) {
4876
+ scopeFacts = g.s.triples.slice();
4877
+ ensureFactIndexes(scopeFacts);
4878
+ Object.defineProperty(scopeFacts, '__scopedSnapshot', { value: scopeFacts, enumerable: false, writable: true });
4879
+ scopeBackRules = []; // concrete scope = syntactic containment (no extra rules)
4880
+ } else if (g.s instanceof Literal && g.s.value === 'true') {
4881
+ scopeFacts = [];
4882
+ ensureFactIndexes(scopeFacts);
4883
+ Object.defineProperty(scopeFacts, '__scopedSnapshot', { value: scopeFacts, enumerable: false, writable: true });
4884
+ scopeBackRules = [];
4885
+ } else {
4886
+ scopeFacts = facts; // dynamic scope
4887
+ }
4888
+
4889
+ const visited2 = [];
4890
+ // Start from the incoming substitution so bindings flow outward.
4891
+ return proveGoals(Array.from(g.o.triples), { ...subst }, scopeFacts, scopeBackRules, depth + 1, visited2, varGen);
4892
+ }
4893
+
4807
4894
  // log:notIncludes (not provable in scope)
4808
4895
  // Delay until we have a frozen scope snapshot to avoid early success.
4809
4896
  if (pv === LOG_NS + 'notIncludes') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.7.1",
3
+ "version": "1.7.2",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [