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.
- package/eyeling.js +87 -0
- 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') {
|