eyeling 1.11.14 → 1.11.15

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 CHANGED
@@ -1251,13 +1251,13 @@ function materializeRdfLists(triples, forwardRules, backwardRules) {
1251
1251
  }
1252
1252
 
1253
1253
  function rewriteTerm(t) {
1254
+ // rdf:nil is the empty list ()
1255
+ if (t instanceof Iri && t.value === RDF_NIL) return new ListTerm([]);
1254
1256
  // Replace list nodes (Blank/Iri) by their constructed ListTerm when possible
1255
1257
  const k = nodeKey(t);
1256
1258
  if (k) {
1257
1259
  const built = buildListForKey(k);
1258
1260
  if (built) return built;
1259
- // Also rewrite rdf:nil even if not otherwise referenced
1260
- if (t instanceof Iri && t.value === RDF_NIL) return new ListTerm([]);
1261
1261
  return t;
1262
1262
  }
1263
1263
  if (t instanceof ListTerm) {
@@ -4405,6 +4405,11 @@ const {
4405
4405
  collectBlankLabelsInTriples,
4406
4406
  } = require('./prelude');
4407
4407
 
4408
+ // In N3/Turtle, rdf:nil is the canonical IRI for the empty RDF list.
4409
+ // Eyeling represents list literals with ListTerm; ensure rdf:nil unifies with ().
4410
+ const RDF_NIL_IRI = RDF_NS + 'nil';
4411
+ const __EMPTY_LIST = new ListTerm([]);
4412
+
4408
4413
  const { lex, N3SyntaxError } = require('./lexer');
4409
4414
  const { Parser } = require('./parser');
4410
4415
  const { liftBlankRuleVars } = require('./rules');
@@ -4718,6 +4723,10 @@ function termsEqual(a, b) {
4718
4723
  if (a === b) return true;
4719
4724
  if (!a || !b) return false;
4720
4725
  if (a.__tid && b.__tid && a.__tid === b.__tid) return true;
4726
+
4727
+ // rdf:nil is equivalent to the empty list ()
4728
+ if (a instanceof Iri && a.value === RDF_NIL_IRI && b instanceof ListTerm && b.elems.length === 0) return true;
4729
+ if (b instanceof Iri && b.value === RDF_NIL_IRI && a instanceof ListTerm && a.elems.length === 0) return true;
4721
4730
  if (a.constructor !== b.constructor) return false;
4722
4731
 
4723
4732
  if (a instanceof Iri) return a.value === b.value;
@@ -4777,6 +4786,10 @@ function termsEqualNoIntDecimal(a, b) {
4777
4786
  if (a === b) return true;
4778
4787
  if (!a || !b) return false;
4779
4788
  if (a.__tid && b.__tid && a.__tid === b.__tid) return true;
4789
+
4790
+ // rdf:nil is equivalent to the empty list ()
4791
+ if (a instanceof Iri && a.value === RDF_NIL_IRI && b instanceof ListTerm && b.elems.length === 0) return true;
4792
+ if (b instanceof Iri && b.value === RDF_NIL_IRI && a instanceof ListTerm && a.elems.length === 0) return true;
4780
4793
  if (a.constructor !== b.constructor) return false;
4781
4794
 
4782
4795
  if (a instanceof Iri) return a.value === b.value;
@@ -5427,6 +5440,11 @@ function unifyTermWithOptions(a, b, subst, opts) {
5427
5440
  a = applySubstTerm(a, subst);
5428
5441
  b = applySubstTerm(b, subst);
5429
5442
 
5443
+ // Normalize rdf:nil IRI to the empty list term, so it unifies with () and
5444
+ // list builtins treat it consistently.
5445
+ if (a instanceof Iri && a.value === RDF_NIL_IRI) a = __EMPTY_LIST;
5446
+ if (b instanceof Iri && b.value === RDF_NIL_IRI) b = __EMPTY_LIST;
5447
+
5430
5448
  // Variable binding
5431
5449
  if (a instanceof Var) {
5432
5450
  const v = a.name;
package/lib/builtins.js CHANGED
@@ -1239,13 +1239,13 @@ function materializeRdfLists(triples, forwardRules, backwardRules) {
1239
1239
  }
1240
1240
 
1241
1241
  function rewriteTerm(t) {
1242
+ // rdf:nil is the empty list ()
1243
+ if (t instanceof Iri && t.value === RDF_NIL) return new ListTerm([]);
1242
1244
  // Replace list nodes (Blank/Iri) by their constructed ListTerm when possible
1243
1245
  const k = nodeKey(t);
1244
1246
  if (k) {
1245
1247
  const built = buildListForKey(k);
1246
1248
  if (built) return built;
1247
- // Also rewrite rdf:nil even if not otherwise referenced
1248
- if (t instanceof Iri && t.value === RDF_NIL) return new ListTerm([]);
1249
1249
  return t;
1250
1250
  }
1251
1251
  if (t instanceof ListTerm) {
package/lib/engine.js CHANGED
@@ -27,6 +27,11 @@ const {
27
27
  collectBlankLabelsInTriples,
28
28
  } = require('./prelude');
29
29
 
30
+ // In N3/Turtle, rdf:nil is the canonical IRI for the empty RDF list.
31
+ // Eyeling represents list literals with ListTerm; ensure rdf:nil unifies with ().
32
+ const RDF_NIL_IRI = RDF_NS + 'nil';
33
+ const __EMPTY_LIST = new ListTerm([]);
34
+
30
35
  const { lex, N3SyntaxError } = require('./lexer');
31
36
  const { Parser } = require('./parser');
32
37
  const { liftBlankRuleVars } = require('./rules');
@@ -340,6 +345,10 @@ function termsEqual(a, b) {
340
345
  if (a === b) return true;
341
346
  if (!a || !b) return false;
342
347
  if (a.__tid && b.__tid && a.__tid === b.__tid) return true;
348
+
349
+ // rdf:nil is equivalent to the empty list ()
350
+ if (a instanceof Iri && a.value === RDF_NIL_IRI && b instanceof ListTerm && b.elems.length === 0) return true;
351
+ if (b instanceof Iri && b.value === RDF_NIL_IRI && a instanceof ListTerm && a.elems.length === 0) return true;
343
352
  if (a.constructor !== b.constructor) return false;
344
353
 
345
354
  if (a instanceof Iri) return a.value === b.value;
@@ -399,6 +408,10 @@ function termsEqualNoIntDecimal(a, b) {
399
408
  if (a === b) return true;
400
409
  if (!a || !b) return false;
401
410
  if (a.__tid && b.__tid && a.__tid === b.__tid) return true;
411
+
412
+ // rdf:nil is equivalent to the empty list ()
413
+ if (a instanceof Iri && a.value === RDF_NIL_IRI && b instanceof ListTerm && b.elems.length === 0) return true;
414
+ if (b instanceof Iri && b.value === RDF_NIL_IRI && a instanceof ListTerm && a.elems.length === 0) return true;
402
415
  if (a.constructor !== b.constructor) return false;
403
416
 
404
417
  if (a instanceof Iri) return a.value === b.value;
@@ -1049,6 +1062,11 @@ function unifyTermWithOptions(a, b, subst, opts) {
1049
1062
  a = applySubstTerm(a, subst);
1050
1063
  b = applySubstTerm(b, subst);
1051
1064
 
1065
+ // Normalize rdf:nil IRI to the empty list term, so it unifies with () and
1066
+ // list builtins treat it consistently.
1067
+ if (a instanceof Iri && a.value === RDF_NIL_IRI) a = __EMPTY_LIST;
1068
+ if (b instanceof Iri && b.value === RDF_NIL_IRI) b = __EMPTY_LIST;
1069
+
1052
1070
  // Variable binding
1053
1071
  if (a instanceof Var) {
1054
1072
  const v = a.name;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.11.14",
3
+ "version": "1.11.15",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
package/test/api.test.js CHANGED
@@ -749,6 +749,27 @@ ${U('a')} <-${U('p')} ${U('b')}.`,
749
749
  expect: [new RegExp(`${EX}s>\\s+<${EX}second>\\s+<${EX}b>\\s*\\.`)],
750
750
  },
751
751
 
752
+ {
753
+ name: '49b rdf:nil matches empty list in rdf:rest (issue #7)',
754
+ opt: { proofComments: false },
755
+ input: `@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
756
+
757
+ ${U('o1')} ${U('path')} (${U('c')} ${U('d')}).
758
+
759
+ { ?o ${U('path')} ?path. } => { ?path rdf:type ${U('P')}. }.
760
+ { ?p1 rdf:type ${U('P')}. ?p1 rdf:rest ?p2. } => { ?p2 rdf:type ${U('P')}. }.
761
+
762
+ # query1 uses ()
763
+ { ?p rdf:type ${U('P')}. ?p rdf:rest (). } => { ${U('result')} ${U('query1')} (?p). }.
764
+ # query2 uses rdf:nil
765
+ { ?p rdf:type ${U('P')}. ?p rdf:rest rdf:nil. } => { ${U('result')} ${U('query2')} (?p). }.
766
+ `,
767
+ expect: [
768
+ new RegExp(`${EX}result>\\s+<${EX}query1>\\s+\\(\\(\\s*<${EX}d>\\s*\\)\\)\\s*\\.`),
769
+ new RegExp(`${EX}result>\\s+<${EX}query2>\\s+\\(\\(\\s*<${EX}d>\\s*\\)\\)\\s*\\.`),
770
+ ],
771
+ },
772
+
752
773
  {
753
774
  name: '50 rdf collection materialization: rdf:first/rdf:rest triples become list terms',
754
775
  opt: { proofComments: false },