n3 2.2.12 → 2.2.14

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/lib/N3Parser.js CHANGED
@@ -36,6 +36,9 @@ class N3Parser {
36
36
  this._supportsQuads = !(isTurtle || isTriG || isNTriples || isN3);
37
37
  // Whether the log:isImpliedBy predicate is supported
38
38
  this._isImpliedBy = options.isImpliedBy;
39
+ // Whether an empty formula is read as the boolean literal true,
40
+ // as in the N3 spec tests (opt-in until the next major version)
41
+ this._emptyFormulaAsTrue = !!options.emptyFormulaAsTrue;
39
42
  // Disable relative IRIs in N-Triples or N-Quads mode
40
43
  if (isLineMode) this._resolveRelativeIRI = iri => {
41
44
  return null;
@@ -83,26 +86,39 @@ class N3Parser {
83
86
  // ### `_saveContext` stores the current parsing context
84
87
  // when entering a new scope (list, blank node, formula)
85
88
  _saveContext(type, graph, subject, predicate, object) {
86
- const n3Mode = this._n3Mode;
89
+ // Only N3 contexts need the extra parser state.
90
+ if (!this._n3Mode) {
91
+ this._contextStack.push({
92
+ type,
93
+ subject,
94
+ predicate,
95
+ object,
96
+ graph
97
+ });
98
+ return;
99
+ }
87
100
  this._contextStack.push({
88
101
  type,
89
102
  subject,
90
103
  predicate,
91
104
  object,
92
105
  graph,
93
- inverse: n3Mode ? this._inversePredicate : false,
94
- blankPrefix: n3Mode ? this._prefixes._ : '',
95
- quantified: n3Mode ? this._quantified : null
106
+ inverse: this._inversePredicate,
107
+ blankPrefix: this._prefixes._,
108
+ quantified: this._quantified,
109
+ emptyFormula: this._emptyFormula
96
110
  });
97
- // The settings below only apply to N3 streams
98
- if (n3Mode) {
99
- // Every new scope resets the predicate direction
100
- this._inversePredicate = false;
101
- // In N3, blank nodes are scoped to a formula
102
- // (using a dot as separator, as a blank node label cannot start with it)
103
- this._prefixes._ = this._graph ? `${this._graph.value}.` : '.';
104
- // Quantifiers are scoped to a formula
105
- this._quantified = Object.create(this._quantified);
111
+ // Every new scope resets the predicate direction
112
+ this._inversePredicate = false;
113
+ // In N3, blank nodes are scoped to a formula
114
+ // (using a dot as separator, as a blank node label cannot start with it)
115
+ this._prefixes._ = this._graph ? `${this._graph.value}.` : '.';
116
+ // Quantifiers are scoped to a formula
117
+ this._quantified = Object.create(this._quantified);
118
+ // A formula starts empty and must not inherit its parent's subject
119
+ if (type === 'formula') {
120
+ this._subject = null;
121
+ this._emptyFormula = true;
106
122
  }
107
123
  }
108
124
 
@@ -124,6 +140,7 @@ class N3Parser {
124
140
  this._inversePredicate = context.inverse;
125
141
  this._prefixes._ = context.blankPrefix;
126
142
  this._quantified = context.quantified;
143
+ this._emptyFormula = context.emptyFormula;
127
144
  }
128
145
  }
129
146
 
@@ -209,6 +226,8 @@ class N3Parser {
209
226
  // ### `_readSubject` reads a quad's subject
210
227
  _readSubject(token) {
211
228
  this._predicate = null;
229
+ // Any statement token means the enclosing formula is not empty
230
+ if (token.type !== '}') this._emptyFormula = false;
212
231
  switch (token.type) {
213
232
  case '[':
214
233
  // Start a new quad with a new blank node as subject
@@ -581,10 +600,10 @@ class N3Parser {
581
600
  }
582
601
 
583
602
  // ### `_completeLiteral` completes a literal with an optional datatype or language
603
+ // Defers possible direction tags without allocating bound callbacks.
584
604
  _completeLiteral(token, component) {
585
- // Create a simple string literal by default
586
- let literal = this._factory.literal(this._literalValue);
587
- let readCb;
605
+ let literal,
606
+ readCb = false;
588
607
  switch (token.type) {
589
608
  // Create a datatyped literal
590
609
  case 'type':
@@ -603,8 +622,13 @@ class N3Parser {
603
622
  literal = this._factory.literal(this._literalValue, token.value);
604
623
  this._literalLanguage = token.value;
605
624
  token = null;
606
- readCb = this._readDirCode.bind(this, component);
625
+ // Save state for a possible direction tag
626
+ this._literalComponent = component;
627
+ readCb = true;
607
628
  break;
629
+ // Create a simple string literal by default
630
+ default:
631
+ literal = this._factory.literal(this._literalValue);
608
632
  }
609
633
  return {
610
634
  token,
@@ -612,7 +636,11 @@ class N3Parser {
612
636
  readCb
613
637
  };
614
638
  }
615
- _readDirCode(component, listItem, token) {
639
+
640
+ // ### `_readDirCode` reads an optional directional language tag
641
+ _readDirCode(token) {
642
+ const component = this._literalComponent,
643
+ listItem = this._literalListItem;
616
644
  // Attempt to read a dircode
617
645
  if (token.type === 'dircode') {
618
646
  const term = this._factory.literal(this._literalValue, {
@@ -647,7 +675,10 @@ class N3Parser {
647
675
  }
648
676
 
649
677
  // Postpone completion if the literal is only partially completed (such as lang+dir).
650
- if (completed.readCb) return completed.readCb.bind(this, false);
678
+ if (completed.readCb) {
679
+ this._literalListItem = false;
680
+ return this._readDirCode;
681
+ }
651
682
 
652
683
  // A subject literal implies N3 mode, so it might start a path.
653
684
  if (component === 'subject') {
@@ -677,7 +708,10 @@ class N3Parser {
677
708
  this._object = completed.literal;
678
709
 
679
710
  // Postpone completion if the literal is only partially completed (such as lang+dir).
680
- if (completed.readCb) return completed.readCb.bind(this, listItem);
711
+ if (completed.readCb) {
712
+ this._literalListItem = listItem;
713
+ return this._readDirCode;
714
+ }
681
715
  return this._completeObjectLiteralPost(completed.token, listItem);
682
716
  }
683
717
  _completeObjectLiteralPost(token, listItem) {
@@ -710,9 +744,18 @@ class N3Parser {
710
744
 
711
745
  // Store the last quad of the formula
712
746
  if (this._subject !== null) this._emit(this._subject, this._predicate, this._object, this._graph);
713
-
747
+ const formula = this._graph,
748
+ empty = this._emptyFormula;
714
749
  // Restore the parent context containing this formula
715
750
  this._restoreContext('formula', token);
751
+
752
+ // When the emptyFormulaAsTrue option is set, an empty formula
753
+ // is read as the boolean literal true, following the N3 spec tests
754
+ // and the direction discussed in https://github.com/w3c-cg/N3/issues/185
755
+ if (empty && this._emptyFormulaAsTrue) {
756
+ if (this._subject === formula) this._subject = this.N3_TRUE;else this._object = this.N3_TRUE;
757
+ }
758
+
716
759
  // If the formula was the subject, continue reading the predicate.
717
760
  // If the formula was the object, read punctuation.
718
761
  return this._object === null ? this._readPredicate : this._getContextEndReader();
@@ -1293,6 +1336,7 @@ class N3Parser {
1293
1336
  this._versionCallback = onVersion || noop;
1294
1337
  this._inversePredicate = false;
1295
1338
  this._quantified = Object.create(null);
1339
+ this._emptyFormula = false;
1296
1340
 
1297
1341
  // Parse synchronously if no quad callback is given
1298
1342
  if (!onQuad) {
@@ -1345,6 +1389,7 @@ function initDataFactory(parser, factory) {
1345
1389
  parser.RDF_REIFIES = factory.namedNode(_IRIs.default.rdf.reifies);
1346
1390
  parser.N3_FORALL = factory.namedNode(_IRIs.default.r.forAll);
1347
1391
  parser.N3_FORSOME = factory.namedNode(_IRIs.default.r.forSome);
1392
+ parser.N3_TRUE = factory.literal('true', factory.namedNode(_IRIs.default.xsd.boolean));
1348
1393
  parser.ABBREVIATIONS = {
1349
1394
  'a': factory.namedNode(_IRIs.default.rdf.type),
1350
1395
  '=': factory.namedNode(_IRIs.default.owl.sameAs),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n3",
3
- "version": "2.2.12",
3
+ "version": "2.2.14",
4
4
  "description": "Lightning fast, asynchronous, streaming Turtle / N3 / RDF library.",
5
5
  "author": "Ruben Verborgh <ruben.verborgh@gmail.com>",
6
6
  "keywords": [
package/src/N3Parser.js CHANGED
@@ -29,6 +29,9 @@ export default class N3Parser {
29
29
  this._supportsQuads = !(isTurtle || isTriG || isNTriples || isN3);
30
30
  // Whether the log:isImpliedBy predicate is supported
31
31
  this._isImpliedBy = options.isImpliedBy;
32
+ // Whether an empty formula is read as the boolean literal true,
33
+ // as in the N3 spec tests (opt-in until the next major version)
34
+ this._emptyFormulaAsTrue = !!options.emptyFormulaAsTrue;
32
35
  // Disable relative IRIs in N-Triples or N-Quads mode
33
36
  if (isLineMode)
34
37
  this._resolveRelativeIRI = iri => { return null; };
@@ -75,23 +78,30 @@ export default class N3Parser {
75
78
  // ### `_saveContext` stores the current parsing context
76
79
  // when entering a new scope (list, blank node, formula)
77
80
  _saveContext(type, graph, subject, predicate, object) {
78
- const n3Mode = this._n3Mode;
81
+ // Only N3 contexts need the extra parser state.
82
+ if (!this._n3Mode) {
83
+ this._contextStack.push({ type, subject, predicate, object, graph });
84
+ return;
85
+ }
79
86
  this._contextStack.push({
80
87
  type,
81
88
  subject, predicate, object, graph,
82
- inverse: n3Mode ? this._inversePredicate : false,
83
- blankPrefix: n3Mode ? this._prefixes._ : '',
84
- quantified: n3Mode ? this._quantified : null,
89
+ inverse: this._inversePredicate,
90
+ blankPrefix: this._prefixes._,
91
+ quantified: this._quantified,
92
+ emptyFormula: this._emptyFormula,
85
93
  });
86
- // The settings below only apply to N3 streams
87
- if (n3Mode) {
88
- // Every new scope resets the predicate direction
89
- this._inversePredicate = false;
90
- // In N3, blank nodes are scoped to a formula
91
- // (using a dot as separator, as a blank node label cannot start with it)
92
- this._prefixes._ = (this._graph ? `${this._graph.value}.` : '.');
93
- // Quantifiers are scoped to a formula
94
- this._quantified = Object.create(this._quantified);
94
+ // Every new scope resets the predicate direction
95
+ this._inversePredicate = false;
96
+ // In N3, blank nodes are scoped to a formula
97
+ // (using a dot as separator, as a blank node label cannot start with it)
98
+ this._prefixes._ = (this._graph ? `${this._graph.value}.` : '.');
99
+ // Quantifiers are scoped to a formula
100
+ this._quantified = Object.create(this._quantified);
101
+ // A formula starts empty and must not inherit its parent's subject
102
+ if (type === 'formula') {
103
+ this._subject = null;
104
+ this._emptyFormula = true;
95
105
  }
96
106
  }
97
107
 
@@ -114,6 +124,7 @@ export default class N3Parser {
114
124
  this._inversePredicate = context.inverse;
115
125
  this._prefixes._ = context.blankPrefix;
116
126
  this._quantified = context.quantified;
127
+ this._emptyFormula = context.emptyFormula;
117
128
  }
118
129
  }
119
130
 
@@ -205,6 +216,9 @@ export default class N3Parser {
205
216
  // ### `_readSubject` reads a quad's subject
206
217
  _readSubject(token) {
207
218
  this._predicate = null;
219
+ // Any statement token means the enclosing formula is not empty
220
+ if (token.type !== '}')
221
+ this._emptyFormula = false;
208
222
  switch (token.type) {
209
223
  case '[':
210
224
  // Start a new quad with a new blank node as subject
@@ -626,10 +640,9 @@ export default class N3Parser {
626
640
  }
627
641
 
628
642
  // ### `_completeLiteral` completes a literal with an optional datatype or language
643
+ // Defers possible direction tags without allocating bound callbacks.
629
644
  _completeLiteral(token, component) {
630
- // Create a simple string literal by default
631
- let literal = this._factory.literal(this._literalValue);
632
- let readCb;
645
+ let literal, readCb = false;
633
646
 
634
647
  switch (token.type) {
635
648
  // Create a datatyped literal
@@ -650,14 +663,21 @@ export default class N3Parser {
650
663
  literal = this._factory.literal(this._literalValue, token.value);
651
664
  this._literalLanguage = token.value;
652
665
  token = null;
653
- readCb = this._readDirCode.bind(this, component);
666
+ // Save state for a possible direction tag
667
+ this._literalComponent = component;
668
+ readCb = true;
654
669
  break;
670
+ // Create a simple string literal by default
671
+ default:
672
+ literal = this._factory.literal(this._literalValue);
655
673
  }
656
674
 
657
675
  return { token, literal, readCb };
658
676
  }
659
677
 
660
- _readDirCode(component, listItem, token) {
678
+ // ### `_readDirCode` reads an optional directional language tag
679
+ _readDirCode(token) {
680
+ const component = this._literalComponent, listItem = this._literalListItem;
661
681
  // Attempt to read a dircode
662
682
  if (token.type === 'dircode') {
663
683
  const term = this._factory.literal(this._literalValue, { language: this._literalLanguage, direction: token.value });
@@ -699,8 +719,10 @@ export default class N3Parser {
699
719
  }
700
720
 
701
721
  // Postpone completion if the literal is only partially completed (such as lang+dir).
702
- if (completed.readCb)
703
- return completed.readCb.bind(this, false);
722
+ if (completed.readCb) {
723
+ this._literalListItem = false;
724
+ return this._readDirCode;
725
+ }
704
726
 
705
727
  // A subject literal implies N3 mode, so it might start a path.
706
728
  if (component === 'subject') {
@@ -733,8 +755,10 @@ export default class N3Parser {
733
755
  this._object = completed.literal;
734
756
 
735
757
  // Postpone completion if the literal is only partially completed (such as lang+dir).
736
- if (completed.readCb)
737
- return completed.readCb.bind(this, listItem);
758
+ if (completed.readCb) {
759
+ this._literalListItem = listItem;
760
+ return this._readDirCode;
761
+ }
738
762
 
739
763
  return this._completeObjectLiteralPost(completed.token, listItem);
740
764
  }
@@ -774,8 +798,20 @@ export default class N3Parser {
774
798
  if (this._subject !== null)
775
799
  this._emit(this._subject, this._predicate, this._object, this._graph);
776
800
 
801
+ const formula = this._graph, empty = this._emptyFormula;
777
802
  // Restore the parent context containing this formula
778
803
  this._restoreContext('formula', token);
804
+
805
+ // When the emptyFormulaAsTrue option is set, an empty formula
806
+ // is read as the boolean literal true, following the N3 spec tests
807
+ // and the direction discussed in https://github.com/w3c-cg/N3/issues/185
808
+ if (empty && this._emptyFormulaAsTrue) {
809
+ if (this._subject === formula)
810
+ this._subject = this.N3_TRUE;
811
+ else
812
+ this._object = this.N3_TRUE;
813
+ }
814
+
779
815
  // If the formula was the subject, continue reading the predicate.
780
816
  // If the formula was the object, read punctuation.
781
817
  return this._object === null ? this._readPredicate : this._getContextEndReader();
@@ -1392,6 +1428,7 @@ export default class N3Parser {
1392
1428
  this._versionCallback = onVersion || noop;
1393
1429
  this._inversePredicate = false;
1394
1430
  this._quantified = Object.create(null);
1431
+ this._emptyFormula = false;
1395
1432
 
1396
1433
  // Parse synchronously if no quad callback is given
1397
1434
  if (!onQuad) {
@@ -1451,6 +1488,7 @@ function initDataFactory(parser, factory) {
1451
1488
  parser.RDF_REIFIES = factory.namedNode(namespaces.rdf.reifies);
1452
1489
  parser.N3_FORALL = factory.namedNode(namespaces.r.forAll);
1453
1490
  parser.N3_FORSOME = factory.namedNode(namespaces.r.forSome);
1491
+ parser.N3_TRUE = factory.literal('true', factory.namedNode(namespaces.xsd.boolean));
1454
1492
  parser.ABBREVIATIONS = {
1455
1493
  'a': factory.namedNode(namespaces.rdf.type),
1456
1494
  '=': factory.namedNode(namespaces.owl.sameAs),