n3 2.9.2 → 2.10.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.
@@ -23,6 +23,8 @@ const {
23
23
  // eslint-disable-next-line prefer-const
24
24
  let DEFAULTGRAPH;
25
25
  let _blankNodeCounter = 0;
26
+ // The ID of the empty IRI, which cannot be '' as that is the ID of the default graph
27
+ const EMPTY_IRI_ID = '<>';
26
28
  const escapedLiteral = /^"(.*".*)(?="[^"]*$)/;
27
29
 
28
30
  // ## DataFactory singleton
@@ -82,7 +84,10 @@ class NamedNode extends Term {
82
84
  * the constructor assumes an already-validated IRI.
83
85
  */
84
86
  constructor(iri) {
85
- super(iri);
87
+ super(iri === '' ? EMPTY_IRI_ID : iri);
88
+ if (iri === '') Object.defineProperty(this, 'value', {
89
+ value: ''
90
+ });
86
91
  }
87
92
 
88
93
  // ### The term type of this term
@@ -291,6 +296,9 @@ function termFromId(id, factory, nested) {
291
296
  case '[':
292
297
  id = JSON.parse(id);
293
298
  break;
299
+ case '<':
300
+ if (id === EMPTY_IRI_ID) return factory.namedNode('');
301
+ // falls through
294
302
  default:
295
303
  if (!nested || !Array.isArray(id)) {
296
304
  return factory.namedNode(id);
@@ -312,7 +320,7 @@ function termToId(term, nested) {
312
320
  // Term instantiated with another library
313
321
  switch (term.termType) {
314
322
  case 'NamedNode':
315
- return term.value;
323
+ return term.value || EMPTY_IRI_ID;
316
324
  case 'BlankNode':
317
325
  return `_:${term.value}`;
318
326
  case 'Variable':
package/lib/N3Store.js CHANGED
@@ -866,9 +866,14 @@ class N3Store {
866
866
 
867
867
  // ### `extractLists` finds and removes all list triples
868
868
  // and returns the items per list.
869
+ // With `allowExtraArcs`, lists whose nodes carry arcs other than
870
+ // rdf:first/rdf:rest are returned as well,
871
+ // but left intact when `remove` is set. Ambiguous unreferenced list heads
872
+ // with multiple non-list arcs remain errors.
869
873
  extractLists({
870
874
  remove = false,
871
- ignoreErrors = false
875
+ ignoreErrors = false,
876
+ allowExtraArcs = false
872
877
  } = {}) {
873
878
  // Keys are the list heads' term values, so a null-prototype map keeps
874
879
  // them from colliding with inherited Object members such as `toString`
@@ -879,13 +884,15 @@ class N3Store {
879
884
 
880
885
  // Traverse each list from its tail
881
886
  const tails = this.getQuads(null, _IRIs.default.rdf.rest, _IRIs.default.rdf.nil, null);
882
- const toRemove = remove ? [...tails] : [];
887
+ const toRemove = [];
883
888
  tails.forEach(tailQuad => {
884
889
  const items = []; // the members found as objects of rdf:first quads
885
890
  let malformed = false; // signals whether the current list is malformed
891
+ let extraArcs = false; // signals whether the current list has extra arcs
886
892
  let head; // the head of the list (_:b1 in above example)
887
893
  let headPos; // set to subject or object when head is set
888
894
  const graph = tailQuad.graph; // make sure list is in exactly one graph
895
+ const listQuads = []; // rdf:first/rdf:rest quads of this list
889
896
 
890
897
  // Traverse the list from tail to end
891
898
  let current = tailQuad.subject;
@@ -904,16 +911,20 @@ class N3Store {
904
911
 
905
912
  // one rdf:first
906
913
  else if (quad.predicate.value === _IRIs.default.rdf.first) {
907
- if (first) malformed = onError(current, 'has multiple rdf:first arcs');else toRemove.push(first = quad);
914
+ if (first) malformed = onError(current, 'has multiple rdf:first arcs');else first = quad;
915
+ if (remove && first === quad) listQuads.push(quad);
908
916
  }
909
917
 
910
918
  // one rdf:rest
911
919
  else if (quad.predicate.value === _IRIs.default.rdf.rest) {
912
- if (rest) malformed = onError(current, 'has multiple rdf:rest arcs');else toRemove.push(rest = quad);
920
+ if (rest) malformed = onError(current, 'has multiple rdf:rest arcs');else rest = quad;
921
+ if (remove && rest === quad) listQuads.push(quad);
913
922
  }
914
923
 
915
924
  // alien triple
916
- else if (objectQuads.length) malformed = onError(current, 'can\'t be subject and object');else {
925
+ else if (objectQuads.length) {
926
+ if (allowExtraArcs) extraArcs = true;else malformed = onError(current, `has non-list arc ${quad.predicate.value}`);
927
+ } else {
917
928
  head = quad; // e.g. { (1 2 3) :p :o }
918
929
  headPos = 'subject';
919
930
  }
@@ -939,9 +950,12 @@ class N3Store {
939
950
  }
940
951
 
941
952
  // Don't remove any quads if the list is malformed
942
- if (malformed) remove = false;
943
- // Store the list under the value of its head
944
- else if (head) lists[head[headPos].value] = items;
953
+ if (malformed) remove = false;else {
954
+ // Store the list under the value of its head
955
+ if (head) lists[head[headPos].value] = items;
956
+ // Leave lists with extra arcs fully intact; otherwise queue this list once.
957
+ if (remove && !extraArcs) toRemove.push(...listQuads);
958
+ }
945
959
  });
946
960
 
947
961
  // Remove list quads if requested
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n3",
3
- "version": "2.9.2",
3
+ "version": "2.10.1",
4
4
  "description": "Lightning fast, asynchronous, streaming Turtle / N3 / RDF library.",
5
5
  "author": "Ruben Verborgh <ruben.verborgh@gmail.com>",
6
6
  "keywords": [
@@ -60,7 +60,7 @@
60
60
  "test": "jest",
61
61
  "lint": "eslint src perf test spec",
62
62
  "prepare": "npm run build",
63
- "spec": "npm run spec-1-1 && npm run spec-1-2",
63
+ "spec": "npm run spec-1-1 && npm run spec-1-2 && npm run spec-n3",
64
64
  "spec-1-1": "npm run spec-1-1-turtle && npm run spec-1-1-ntriples && npm run spec-1-1-nquads && npm run spec-1-1-trig",
65
65
  "spec-1-1-earl": "npm run spec-1-1-earl-turtle && npm run spec-1-1-earl-ntriples && npm run spec-1-1-earl-nquads && npm run spec-1-1-earl-trig",
66
66
  "spec-1-1-ntriples": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-n-triples/manifest.ttl -i '{ \"format\": \"n-triples\" }' -c .rdf-test-suite-cache/",
@@ -81,7 +81,11 @@
81
81
  "spec-1-2-earl-nquads": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-n-quads/syntax/manifest.ttl -i '{ \"format\": \"n-quads\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-nquads.ttl",
82
82
  "spec-1-2-earl-turtle": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-turtle/syntax/manifest.ttl -i '{ \"format\": \"turtle\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-turtle.ttl",
83
83
  "spec-1-2-earl-trig": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-trig/syntax/manifest.ttl -i '{ \"format\": \"trig\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-trig.ttl",
84
- "spec-clean": "rm -r .rdf-test-suite-cache/",
84
+ "spec-n3": "npm run spec-n3-parser && npm run spec-n3-extended",
85
+ "spec-n3-fetch": "sh scripts/fetch-n3-tests.sh",
86
+ "spec-n3-parser": "npm run spec-n3-fetch && rdf-test-suite spec/parser.js https://w3c-cg.github.io/N3/tests/N3Tests/manifest-parser.ttl -i '{ \"format\": \"text/n3\" }' -m 'https://w3c-cg.github.io/N3/tests/~.n3-tests/tests/'",
87
+ "spec-n3-extended": "npm run spec-n3-fetch && rdf-test-suite spec/parser.js https://w3c-cg.github.io/N3/tests/N3Tests/manifest-extended.ttl -i '{ \"format\": \"text/n3\" }' -m 'https://w3c-cg.github.io/N3/tests/~.n3-tests/tests/'",
88
+ "spec-clean": "rm -rf .rdf-test-suite-cache/ .n3-tests/",
85
89
  "docs": "cd src && docco *.js -o ../docs && cd .."
86
90
  },
87
91
  "repository": {
@@ -8,6 +8,8 @@ const { rdf, xsd } = namespaces;
8
8
  // eslint-disable-next-line prefer-const
9
9
  let DEFAULTGRAPH;
10
10
  let _blankNodeCounter = 0;
11
+ // The ID of the empty IRI, which cannot be '' as that is the ID of the default graph
12
+ const EMPTY_IRI_ID = '<>';
11
13
 
12
14
  const escapedLiteral = /^"(.*".*)(?="[^"]*$)/;
13
15
 
@@ -72,7 +74,9 @@ export class NamedNode extends Term {
72
74
  * the constructor assumes an already-validated IRI.
73
75
  */
74
76
  constructor(iri) {
75
- super(iri);
77
+ super(iri === '' ? EMPTY_IRI_ID : iri);
78
+ if (iri === '')
79
+ Object.defineProperty(this, 'value', { value: '' });
76
80
  }
77
81
 
78
82
  // ### The term type of this term
@@ -285,6 +289,10 @@ export function termFromId(id, factory, nested) {
285
289
  case '[':
286
290
  id = JSON.parse(id);
287
291
  break;
292
+ case '<':
293
+ if (id === EMPTY_IRI_ID)
294
+ return factory.namedNode('');
295
+ // falls through
288
296
  default:
289
297
  if (!nested || !Array.isArray(id)) {
290
298
  return factory.namedNode(id);
@@ -313,7 +321,7 @@ export function termToId(term, nested) {
313
321
 
314
322
  // Term instantiated with another library
315
323
  switch (term.termType) {
316
- case 'NamedNode': return term.value;
324
+ case 'NamedNode': return term.value || EMPTY_IRI_ID;
317
325
  case 'BlankNode': return `_:${term.value}`;
318
326
  case 'Variable': return `?${term.value}`;
319
327
  case 'DefaultGraph': return '';
package/src/N3Store.js CHANGED
@@ -916,7 +916,11 @@ export default class N3Store {
916
916
 
917
917
  // ### `extractLists` finds and removes all list triples
918
918
  // and returns the items per list.
919
- extractLists({ remove = false, ignoreErrors = false } = {}) {
919
+ // With `allowExtraArcs`, lists whose nodes carry arcs other than
920
+ // rdf:first/rdf:rest are returned as well,
921
+ // but left intact when `remove` is set. Ambiguous unreferenced list heads
922
+ // with multiple non-list arcs remain errors.
923
+ extractLists({ remove = false, ignoreErrors = false, allowExtraArcs = false } = {}) {
920
924
  // Keys are the list heads' term values, so a null-prototype map keeps
921
925
  // them from colliding with inherited Object members such as `toString`
922
926
  const lists = Object.create(null);
@@ -925,13 +929,15 @@ export default class N3Store {
925
929
 
926
930
  // Traverse each list from its tail
927
931
  const tails = this.getQuads(null, namespaces.rdf.rest, namespaces.rdf.nil, null);
928
- const toRemove = remove ? [...tails] : [];
932
+ const toRemove = [];
929
933
  tails.forEach(tailQuad => {
930
934
  const items = []; // the members found as objects of rdf:first quads
931
935
  let malformed = false; // signals whether the current list is malformed
936
+ let extraArcs = false; // signals whether the current list has extra arcs
932
937
  let head; // the head of the list (_:b1 in above example)
933
938
  let headPos; // set to subject or object when head is set
934
939
  const graph = tailQuad.graph; // make sure list is in exactly one graph
940
+ const listQuads = []; // rdf:first/rdf:rest quads of this list
935
941
 
936
942
  // Traverse the list from tail to end
937
943
  let current = tailQuad.subject;
@@ -953,7 +959,9 @@ export default class N3Store {
953
959
  if (first)
954
960
  malformed = onError(current, 'has multiple rdf:first arcs');
955
961
  else
956
- toRemove.push(first = quad);
962
+ first = quad;
963
+ if (remove && first === quad)
964
+ listQuads.push(quad);
957
965
  }
958
966
 
959
967
  // one rdf:rest
@@ -961,12 +969,18 @@ export default class N3Store {
961
969
  if (rest)
962
970
  malformed = onError(current, 'has multiple rdf:rest arcs');
963
971
  else
964
- toRemove.push(rest = quad);
972
+ rest = quad;
973
+ if (remove && rest === quad)
974
+ listQuads.push(quad);
965
975
  }
966
976
 
967
977
  // alien triple
968
- else if (objectQuads.length)
969
- malformed = onError(current, 'can\'t be subject and object');
978
+ else if (objectQuads.length) {
979
+ if (allowExtraArcs)
980
+ extraArcs = true;
981
+ else
982
+ malformed = onError(current, `has non-list arc ${quad.predicate.value}`);
983
+ }
970
984
  else {
971
985
  head = quad; // e.g. { (1 2 3) :p :o }
972
986
  headPos = 'subject';
@@ -1003,9 +1017,14 @@ export default class N3Store {
1003
1017
  // Don't remove any quads if the list is malformed
1004
1018
  if (malformed)
1005
1019
  remove = false;
1006
- // Store the list under the value of its head
1007
- else if (head)
1008
- lists[head[headPos].value] = items;
1020
+ else {
1021
+ // Store the list under the value of its head
1022
+ if (head)
1023
+ lists[head[headPos].value] = items;
1024
+ // Leave lists with extra arcs fully intact; otherwise queue this list once.
1025
+ if (remove && !extraArcs)
1026
+ toRemove.push(...listQuads);
1027
+ }
1009
1028
  });
1010
1029
 
1011
1030
  // Remove list quads if requested