eyeling 1.5.34 → 1.5.35

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.
@@ -0,0 +1,31 @@
1
+ @prefix : <http://example.org/ns#> .
2
+
3
+ # ----------------------------------------------------------------------
4
+ # Proof for derived triple:
5
+ # :result :is true .
6
+ # It holds because the following instance of the rule body is provable:
7
+ # :X :val (1 2) .
8
+ # (1 2) :head 1 .
9
+ # (1 2) :tail (2) .
10
+ # 1 log:equalTo 1 .
11
+ # (2) log:equalTo (2) .
12
+ # via the schematic forward rule:
13
+ # {
14
+ # ?X :val ?Y .
15
+ # ?Y :head ?H .
16
+ # ?Y :tail ?T .
17
+ # ?H log:equalTo 1 .
18
+ # ?T log:equalTo (2) .
19
+ # } => {
20
+ # :result :is true .
21
+ # } .
22
+ # with substitution (on rule variables):
23
+ # ?H = 1
24
+ # ?T = (2)
25
+ # ?X = :X
26
+ # ?Y = (1 2)
27
+ # Therefore the derived triple above is entailed by the rules and facts.
28
+ # ----------------------------------------------------------------------
29
+
30
+ :result :is true .
31
+
@@ -0,0 +1,22 @@
1
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
2
+ @prefix list: <http://www.w3.org/2000/10/swap/list#> .
3
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
4
+ @prefix : <http://example.org/ns#> .
5
+
6
+ :X :val (1 2).
7
+
8
+ { ?X :head ?Y } <= { ?X list:first ?Y }.
9
+ { ?X :tail ?Y } <= { ?X list:rest ?Y} .
10
+
11
+ {
12
+ ?X :val ?Y.
13
+ ?Y :head ?H .
14
+ ?Y :tail ?T .
15
+
16
+ ?H log:equalTo 1.
17
+ ?T log:equalTo (2).
18
+ }
19
+ =>
20
+ {
21
+ :result :is true.
22
+ }.
package/eyeling.js CHANGED
@@ -2951,6 +2951,40 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2951
2951
  return [];
2952
2952
  }
2953
2953
 
2954
+ // rdf:first (alias of list:first)
2955
+ // Schema: $s+ rdf:first $o-
2956
+ if (g.p instanceof Iri && g.p.value === RDF_NS + "first") {
2957
+ if (!(g.s instanceof ListTerm)) return [];
2958
+ if (!g.s.elems.length) return [];
2959
+ const first = g.s.elems[0];
2960
+ const s2 = unifyTerm(g.o, first, subst);
2961
+ return s2 !== null ? [s2] : [];
2962
+ }
2963
+
2964
+ // rdf:rest (alias of list:rest)
2965
+ // Schema: $s+ rdf:rest $o-
2966
+ if (g.p instanceof Iri && g.p.value === RDF_NS + "rest") {
2967
+ // Closed list: (a b c) -> (b c)
2968
+ if (g.s instanceof ListTerm) {
2969
+ if (!g.s.elems.length) return [];
2970
+ const rest = new ListTerm(g.s.elems.slice(1));
2971
+ const s2 = unifyTerm(g.o, rest, subst);
2972
+ return s2 !== null ? [s2] : [];
2973
+ }
2974
+ // Open list: (a b ... ?T) -> (b ... ?T)
2975
+ if (g.s instanceof OpenListTerm) {
2976
+ if (!g.s.prefix.length) return [];
2977
+ if (g.s.prefix.length === 1) {
2978
+ const s2 = unifyTerm(g.o, new Var(g.s.tailVar), subst);
2979
+ return s2 !== null ? [s2] : [];
2980
+ }
2981
+ const rest = new OpenListTerm(g.s.prefix.slice(1), g.s.tailVar);
2982
+ const s2 = unifyTerm(g.o, rest, subst);
2983
+ return s2 !== null ? [s2] : [];
2984
+ }
2985
+ return [];
2986
+ }
2987
+
2954
2988
  // list:iterate
2955
2989
  // true iff $s is a list and $o is a list (index value),
2956
2990
  // where index is a valid 0-based index into $s and value is the element at that index.
@@ -4278,6 +4312,135 @@ function printExplanation(df, prefixes) {
4278
4312
  // Misc helpers
4279
4313
  // ============================================================================
4280
4314
 
4315
+ // Turn RDF Collections described with rdf:first/rdf:rest (+ rdf:nil) into ListTerm terms.
4316
+ // This mutates triples/rules in-place so list:* builtins work on RDF-serialized lists too.
4317
+ function materializeRdfLists(triples, forwardRules, backwardRules) {
4318
+ const RDF_FIRST = RDF_NS + "first";
4319
+ const RDF_REST = RDF_NS + "rest";
4320
+ const RDF_NIL = RDF_NS + "nil";
4321
+
4322
+ function nodeKey(t) {
4323
+ if (t instanceof Blank) return "B:" + t.label;
4324
+ if (t instanceof Iri) return "I:" + t.value;
4325
+ return null;
4326
+ }
4327
+
4328
+ // Collect first/rest arcs from *input triples*
4329
+ const firstMap = new Map(); // key(subject) -> Term (object)
4330
+ const restMap = new Map(); // key(subject) -> Term (object)
4331
+ for (const tr of triples) {
4332
+ if (!(tr.p instanceof Iri)) continue;
4333
+ const k = nodeKey(tr.s);
4334
+ if (!k) continue;
4335
+ if (tr.p.value === RDF_FIRST) firstMap.set(k, tr.o);
4336
+ else if (tr.p.value === RDF_REST) restMap.set(k, tr.o);
4337
+ }
4338
+ if (!firstMap.size && !restMap.size) return;
4339
+
4340
+ const cache = new Map(); // key(node) -> ListTerm
4341
+ const visiting = new Set(); // cycle guard
4342
+
4343
+ function buildListForKey(k) {
4344
+ if (cache.has(k)) return cache.get(k);
4345
+ if (visiting.has(k)) return null; // cycle => not a well-formed list
4346
+ visiting.add(k);
4347
+
4348
+ // rdf:nil => ()
4349
+ if (k === "I:" + RDF_NIL) {
4350
+ const empty = new ListTerm([]);
4351
+ cache.set(k, empty);
4352
+ visiting.delete(k);
4353
+ return empty;
4354
+ }
4355
+
4356
+ const head = firstMap.get(k);
4357
+ const tail = restMap.get(k);
4358
+ if (head === undefined || tail === undefined) {
4359
+ visiting.delete(k);
4360
+ return null; // not a full cons cell
4361
+ }
4362
+
4363
+ const headTerm = rewriteTerm(head);
4364
+
4365
+ let tailListElems = null;
4366
+ if (tail instanceof Iri && tail.value === RDF_NIL) {
4367
+ tailListElems = [];
4368
+ } else {
4369
+ const tk = nodeKey(tail);
4370
+ if (!tk) {
4371
+ visiting.delete(k);
4372
+ return null;
4373
+ }
4374
+ const tailList = buildListForKey(tk);
4375
+ if (!tailList) {
4376
+ visiting.delete(k);
4377
+ return null;
4378
+ }
4379
+ tailListElems = tailList.elems;
4380
+ }
4381
+
4382
+ const out = new ListTerm([headTerm, ...tailListElems]);
4383
+ cache.set(k, out);
4384
+ visiting.delete(k);
4385
+ return out;
4386
+ }
4387
+
4388
+ function rewriteTerm(t) {
4389
+ // Replace list nodes (Blank/Iri) by their constructed ListTerm when possible
4390
+ const k = nodeKey(t);
4391
+ if (k) {
4392
+ const built = buildListForKey(k);
4393
+ if (built) return built;
4394
+ // Also rewrite rdf:nil even if not otherwise referenced
4395
+ if (t instanceof Iri && t.value === RDF_NIL) return new ListTerm([]);
4396
+ return t;
4397
+ }
4398
+ if (t instanceof ListTerm) {
4399
+ let changed = false;
4400
+ const elems = t.elems.map(e => {
4401
+ const r = rewriteTerm(e);
4402
+ if (r !== e) changed = true;
4403
+ return r;
4404
+ });
4405
+ return changed ? new ListTerm(elems) : t;
4406
+ }
4407
+ if (t instanceof OpenListTerm) {
4408
+ let changed = false;
4409
+ const prefix = t.prefix.map(e => {
4410
+ const r = rewriteTerm(e);
4411
+ if (r !== e) changed = true;
4412
+ return r;
4413
+ });
4414
+ return changed ? new OpenListTerm(prefix, t.tailVar) : t;
4415
+ }
4416
+ if (t instanceof FormulaTerm) {
4417
+ for (const tr of t.triples) rewriteTriple(tr);
4418
+ return t;
4419
+ }
4420
+ return t;
4421
+ }
4422
+
4423
+ function rewriteTriple(tr) {
4424
+ tr.s = rewriteTerm(tr.s);
4425
+ tr.p = rewriteTerm(tr.p);
4426
+ tr.o = rewriteTerm(tr.o);
4427
+ }
4428
+
4429
+ // Pre-build all reachable list heads
4430
+ for (const k of firstMap.keys()) buildListForKey(k);
4431
+
4432
+ // Rewrite input triples + rules in-place
4433
+ for (const tr of triples) rewriteTriple(tr);
4434
+ for (const r of forwardRules) {
4435
+ for (const tr of r.premise) rewriteTriple(tr);
4436
+ for (const tr of r.conclusion) rewriteTriple(tr);
4437
+ }
4438
+ for (const r of backwardRules) {
4439
+ for (const tr of r.premise) rewriteTriple(tr);
4440
+ for (const tr of r.conclusion) rewriteTriple(tr);
4441
+ }
4442
+ }
4443
+
4281
4444
  function localIsoDateTimeString(d) {
4282
4445
  function pad(n, width = 2) {
4283
4446
  return String(n).padStart(width, "0");
@@ -4365,6 +4528,9 @@ function main() {
4365
4528
  const [prefixes, triples, frules, brules] = parser.parseDocument();
4366
4529
  // console.log(JSON.stringify([prefixes, triples, frules, brules], null, 2));
4367
4530
 
4531
+ // Build internal ListTerm values from rdf:first/rdf:rest (+ rdf:nil) input triples
4532
+ materializeRdfLists(triples, frules, brules);
4533
+
4368
4534
  const facts = triples.filter(tr => isGroundTriple(tr));
4369
4535
  const derived = forwardChain(facts, frules, brules);
4370
4536
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.5.34",
3
+ "version": "1.5.35",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [