n3 2.2.16 → 2.4.0

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/IRIs.js CHANGED
@@ -11,6 +11,7 @@ var _default = exports.default = {
11
11
  xsd: {
12
12
  decimal: `${XSD}decimal`,
13
13
  boolean: `${XSD}boolean`,
14
+ dateTime: `${XSD}dateTime`,
14
15
  double: `${XSD}double`,
15
16
  integer: `${XSD}integer`,
16
17
  string: `${XSD}string`
@@ -412,7 +412,7 @@ function literal(value, languageOrDataType) {
412
412
  return new Literal(`"${value}"@${languageOrDataType.language.toLowerCase()}${languageOrDataType.direction ? `--${languageOrDataType.direction.toLowerCase()}` : ''}`);
413
413
  }
414
414
 
415
- // Automatically determine datatype for booleans and numbers
415
+ // Automatically determine datatype for booleans, numbers, and dates
416
416
  let datatype = languageOrDataType ? languageOrDataType.value : '';
417
417
  if (datatype === '') {
418
418
  // Convert a boolean
@@ -424,6 +424,11 @@ function literal(value, languageOrDataType) {
424
424
  if (!Number.isNaN(value)) value = value > 0 ? 'INF' : '-INF';
425
425
  }
426
426
  }
427
+ // Convert a valid date
428
+ else if (value instanceof Date && !Number.isNaN(value.getTime())) {
429
+ datatype = xsd.dateTime;
430
+ value = value.toISOString();
431
+ }
427
432
  }
428
433
 
429
434
  // Create a datatyped literal
package/lib/N3Parser.js CHANGED
@@ -97,7 +97,7 @@ class N3Parser {
97
97
  });
98
98
  return;
99
99
  }
100
- this._contextStack.push({
100
+ const context = {
101
101
  type,
102
102
  subject,
103
103
  predicate,
@@ -107,7 +107,14 @@ class N3Parser {
107
107
  blankPrefix: this._prefixes._,
108
108
  quantified: this._quantified,
109
109
  emptyFormula: this._emptyFormula
110
- });
110
+ };
111
+ // Prefix and base declarations are scoped to their formula
112
+ if (type === 'formula') {
113
+ context.prefixes = this._prefixes;
114
+ context.base = [this._base, this._basePath, this._baseRoot, this._baseScheme];
115
+ this._prefixes = Object.create(this._prefixes);
116
+ }
117
+ this._contextStack.push(context);
111
118
  // Every new scope resets the predicate direction
112
119
  this._inversePredicate = false;
113
120
  // In N3, blank nodes are scoped to a formula
@@ -138,7 +145,10 @@ class N3Parser {
138
145
  // Restore N3 context settings
139
146
  if (this._n3Mode) {
140
147
  this._inversePredicate = context.inverse;
141
- this._prefixes._ = context.blankPrefix;
148
+ if (type === 'formula') {
149
+ this._prefixes = context.prefixes;
150
+ [this._base, this._basePath, this._baseRoot, this._baseScheme] = context.base;
151
+ } else this._prefixes._ = context.blankPrefix;
142
152
  this._quantified = context.quantified;
143
153
  this._emptyFormula = context.emptyFormula;
144
154
  }
@@ -188,6 +198,28 @@ class N3Parser {
188
198
  }
189
199
  }
190
200
 
201
+ // ### `_readInFormulaContext` reads a token at the statement level of a formula
202
+ _readInFormulaContext(token) {
203
+ switch (token.type) {
204
+ case 'PREFIX':
205
+ this._sparqlStyle = true;
206
+ case '@prefix':
207
+ return this._readPrefix;
208
+ case 'BASE':
209
+ this._sparqlStyle = true;
210
+ case '@base':
211
+ return this._readBaseIRI;
212
+ default:
213
+ return this._readSubject(token);
214
+ }
215
+ }
216
+
217
+ // ### `_getStatementReader` returns the reader for the current statement scope
218
+ _getStatementReader() {
219
+ const context = this._contextStack[this._contextStack.length - 1];
220
+ return context && context.type === 'formula' ? this._readInFormulaContext : this._readInTopContext;
221
+ }
222
+
191
223
  // ### `_readEntity` reads an IRI, prefixed name, blank node, or variable
192
224
  _readEntity(token, quantifier) {
193
225
  let value;
@@ -252,7 +284,7 @@ class N3Parser {
252
284
  // Start a new formula
253
285
  if (!this._n3Mode) return this._error('Unexpected graph', token);
254
286
  this._saveContext('formula', this._graph, this._graph = this._factory.blankNode(), null, null);
255
- return this._readSubject;
287
+ return this._readInFormulaContext;
256
288
  case '}':
257
289
  // No subject; the graph in which we are reading is closed instead
258
290
  return this._readPunctuation(token);
@@ -302,6 +334,7 @@ class N3Parser {
302
334
  // ### `_readPredicate` reads a quad's predicate
303
335
  _readPredicate(token) {
304
336
  const type = token.type;
337
+ let pathable = false;
305
338
  switch (type) {
306
339
  case 'inverse':
307
340
  this._inversePredicate = true;
@@ -326,6 +359,7 @@ class N3Parser {
326
359
  this._literalValue = token.value;
327
360
  return this._completePredicateLiteral;
328
361
  } else this._predicate = this._factory.literal(token.value, this._factory.namedNode(token.prefix));
362
+ pathable = true;
329
363
  break;
330
364
  case '(':
331
365
  // In N3, a list can be a predicate
@@ -336,14 +370,23 @@ class N3Parser {
336
370
  this._saveContext('blank', this._graph, this._subject, this._subject = this._factory.blankNode(), null);
337
371
  return this._readBlankNodeHead;
338
372
  }
373
+ return this._error('Disallowed blank node as predicate', token);
374
+ case '{':
375
+ // In N3, a formula can be a predicate
376
+ if (this._n3Mode) {
377
+ this._saveContext('formula', this._graph, this._subject, this._graph = this._factory.blankNode(), null);
378
+ return this._readSubject;
379
+ }
380
+ return this._readEntity(token);
339
381
  case 'blank':
340
382
  if (!this._n3Mode) return this._error('Disallowed blank node as predicate', token);
341
383
  default:
342
384
  if ((this._predicate = this._readEntity(token)) === undefined) return;
385
+ pathable = this._n3Mode;
343
386
  }
344
387
  this._validAnnotation = true;
345
388
  // The next token must be an object
346
- return this._readObject;
389
+ return pathable ? this._getPathReader(this._readObject, 'predicate') : this._readObject;
347
390
  }
348
391
 
349
392
  // ### `_readObject` reads a quad's object
@@ -372,7 +415,7 @@ class N3Parser {
372
415
  // Start a new formula
373
416
  if (!this._n3Mode) return this._error('Unexpected graph', token);
374
417
  this._saveContext('formula', this._graph, this._subject, this._predicate, this._graph = this._factory.blankNode());
375
- return this._readSubject;
418
+ return this._readInFormulaContext;
376
419
  case '<<(':
377
420
  this._saveContext('<<(', this._graph, this._subject, this._predicate, null);
378
421
  this._graph = null;
@@ -432,7 +475,7 @@ class N3Parser {
432
475
  // If the blank node was the object, restore previous context and read punctuation
433
476
  if (this._object !== null) return this._getContextEndReader();
434
477
  // If the blank node was the predicate, continue reading the object
435
- else if (this._predicate !== null) return this._readObject;
478
+ else if (this._predicate !== null) return this._getPathReader(this._readObject, 'predicate');
436
479
  // If the blank node was the subject, continue reading the predicate
437
480
  else
438
481
  // If the blank node was empty, it could be a named graph label
@@ -504,7 +547,7 @@ class N3Parser {
504
547
  // Was this list the parent's predicate?
505
548
  else if (this._object === null) {
506
549
  // The next token is the object
507
- next = this._readObject;
550
+ next = this._getPathReader(this._readObject, 'predicate');
508
551
  // No list tail if this was an empty list
509
552
  if (this._predicate === this.RDF_NIL) return next;
510
553
  }
@@ -551,7 +594,7 @@ class N3Parser {
551
594
  // Stack the current list quad and start the formula
552
595
  this._saveContext('formula', this._graph, list, this.RDF_FIRST, this._graph = item);
553
596
  this._subject = null;
554
- return this._readSubject;
597
+ return this._readInFormulaContext;
555
598
  case '<<(':
556
599
  this._saveContext('<<(', this._graph, null, null, null);
557
600
  this._graph = null;
@@ -662,12 +705,12 @@ class N3Parser {
662
705
  this._literalLanguage = undefined;
663
706
  token = null;
664
707
  }
665
- if (component === 'subject') {
666
- // A subject literal implies N3 mode, so the literal might start a path
667
- const reader = this._getPathEndReader(token, this._readPredicateOrNamedGraph);
668
- return reader || this._readPredicateOrNamedGraph(token);
708
+ if (component === 'subject' || component === 'predicate') {
709
+ // A subject or predicate literal implies N3 mode, so it might start a path
710
+ const next = component === 'subject' ? this._readPredicateOrNamedGraph : this._readObject;
711
+ const reader = this._getPathEndReader(token, next, component);
712
+ return reader || next.call(this, token);
669
713
  }
670
- if (component === 'predicate') return token === null ? this._readObject : this._readObject(token);
671
714
  return this._completeObjectLiteralPost(token, listItem);
672
715
  }
673
716
 
@@ -691,15 +734,12 @@ class N3Parser {
691
734
  return this._readDirCode;
692
735
  }
693
736
 
694
- // A subject literal implies N3 mode, so it might start a path.
695
- if (component === 'subject') {
696
- const reader = this._getPathEndReader(completed.token, next);
697
- if (reader) return reader;
698
- }
737
+ // A subject or predicate literal implies N3 mode, so it might start a path.
738
+ const reader = this._getPathEndReader(completed.token, next, component);
739
+ if (reader) return reader;
699
740
 
700
- // If the token was consumed as a datatype, continue with the next component;
701
- // otherwise, consume the token now
702
- return completed.token === null ? next : next.call(this, completed.token);
741
+ // Consume the non-path token now
742
+ return next.call(this, completed.token);
703
743
  }
704
744
 
705
745
  // Completes a literal in subject position
@@ -764,12 +804,13 @@ class N3Parser {
764
804
  // is read as the boolean literal true, following the N3 spec tests
765
805
  // and the direction discussed in https://github.com/w3c-cg/N3/issues/185
766
806
  if (empty && this._emptyFormulaAsTrue) {
767
- if (this._subject === formula) this._subject = this.N3_TRUE;else this._object = this.N3_TRUE;
807
+ if (this._subject === formula) this._subject = this.N3_TRUE;else if (this._predicate === formula) this._predicate = this.N3_TRUE;else this._object = this.N3_TRUE;
768
808
  }
769
809
 
770
- // If the formula was the subject, continue reading the predicate.
771
- // If the formula was the object, read punctuation.
772
- return this._object === null ? this._readPredicate : this._getContextEndReader();
810
+ // Continue according to the formula's position in the enclosing statement
811
+ if (this._object !== null) return this._getPathReader(this._getContextEndReader(), 'object');
812
+ if (this._predicate !== null) return this._getPathReader(this._readObject, 'predicate');
813
+ return this._getPathReader(this._readPredicate, 'subject');
773
814
  }
774
815
 
775
816
  // ### `_readPunctuation` reads punctuation between quads or quad parts
@@ -789,7 +830,7 @@ class N3Parser {
789
830
  case '.':
790
831
  this._subject = null;
791
832
  this._tripleTerm = null;
792
- next = this._contextStack.length ? this._readSubject : this._readInTopContext;
833
+ next = this._getStatementReader();
793
834
  if (inversePredicate) this._inversePredicate = false;
794
835
  break;
795
836
  // Semicolon means the subject is shared; predicate and object are different
@@ -947,10 +988,10 @@ class N3Parser {
947
988
  // SPARQL-style declarations don't have punctuation
948
989
  if (this._sparqlStyle) {
949
990
  this._sparqlStyle = false;
950
- return this._readInTopContext(token);
991
+ return this._getStatementReader().call(this, token);
951
992
  }
952
993
  if (token.type !== '.') return this._error('Expected declaration to end with a dot', token);
953
- return this._readInTopContext;
994
+ return this._getStatementReader();
954
995
  }
955
996
 
956
997
  // Reads a list of quantified symbols from a @forSome or @forAll statement
@@ -995,17 +1036,18 @@ class N3Parser {
995
1036
  }
996
1037
 
997
1038
  // ### `_getPathReader` reads a potential path and then resumes with the given function
998
- _getPathReader(afterPath) {
1039
+ _getPathReader(afterPath, position) {
999
1040
  this._afterPath = afterPath;
1041
+ this._pathPosition = position || (this._predicate === null ? 'subject' : 'object');
1000
1042
  return this._readPath;
1001
1043
  }
1002
1044
 
1003
1045
  // ### `_getPathEndReader` continues reading after a term that might start a path,
1004
1046
  // given the pending token that follows the term (or `null` if it was consumed)
1005
- _getPathEndReader(token, afterPath) {
1047
+ _getPathEndReader(token, afterPath, position) {
1006
1048
  // Other pending tokens are not handled here
1007
1049
  if (token !== null && token.type !== '!' && token.type !== '^') return null;
1008
- const reader = this._getPathReader(afterPath);
1050
+ const reader = this._getPathReader(afterPath, position);
1009
1051
  // If no token is pending, wait for the next one; otherwise, consume it now
1010
1052
  return token === null ? reader : reader.call(this, token);
1011
1053
  }
@@ -1021,6 +1063,7 @@ class N3Parser {
1021
1063
  return this._readBackwardPath;
1022
1064
  // Not a path; resume reading where we left off
1023
1065
  default:
1066
+ const afterPath = this._afterPath;
1024
1067
  const stack = this._contextStack,
1025
1068
  parent = stack.length && stack[stack.length - 1];
1026
1069
  // If we were reading a list item, we still need to output it
@@ -1032,7 +1075,9 @@ class N3Parser {
1032
1075
  // Output the list item
1033
1076
  this._emit(this._subject, this.RDF_FIRST, item, this._graph);
1034
1077
  }
1035
- return this._afterPath(token);
1078
+ this._afterPath = null;
1079
+ this._pathPosition = null;
1080
+ return afterPath.call(this, token);
1036
1081
  }
1037
1082
  }
1038
1083
 
@@ -1042,10 +1087,8 @@ class N3Parser {
1042
1087
  const object = this._factory.blankNode();
1043
1088
  // The next token is the predicate
1044
1089
  if ((predicate = this._readEntity(token)) === undefined) return;
1045
- // If we were reading a subject, replace the subject by the path's object
1046
- if (this._predicate === null) subject = this._subject, this._subject = object;
1047
- // If we were reading an object, replace the subject by the path's object
1048
- else subject = this._object, this._object = object;
1090
+ // Replace the path expression with the generated object in its current position
1091
+ if (this._pathPosition === 'subject') subject = this._subject, this._subject = object;else if (this._pathPosition === 'predicate') subject = this._predicate, this._predicate = object;else subject = this._object, this._object = object;
1049
1092
  // Emit the path's current quad and read its next section
1050
1093
  this._emit(subject, predicate, object, this._graph);
1051
1094
  return this._readPath;
@@ -1057,10 +1100,8 @@ class N3Parser {
1057
1100
  let predicate, object;
1058
1101
  // The next token is the predicate
1059
1102
  if ((predicate = this._readEntity(token)) === undefined) return;
1060
- // If we were reading a subject, replace the subject by the path's subject
1061
- if (this._predicate === null) object = this._subject, this._subject = subject;
1062
- // If we were reading an object, replace the subject by the path's subject
1063
- else object = this._object, this._object = subject;
1103
+ // Replace the path expression with the generated subject in its current position
1104
+ if (this._pathPosition === 'subject') object = this._subject, this._subject = subject;else if (this._pathPosition === 'predicate') object = this._predicate, this._predicate = subject;else object = this._object, this._object = subject;
1064
1105
  // Emit the path's current quad and read its next section
1065
1106
  this._emit(subject, predicate, object, this._graph);
1066
1107
  return this._readPath;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n3",
3
- "version": "2.2.16",
3
+ "version": "2.4.0",
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/IRIs.js CHANGED
@@ -6,6 +6,7 @@ export default {
6
6
  xsd: {
7
7
  decimal: `${XSD}decimal`,
8
8
  boolean: `${XSD}boolean`,
9
+ dateTime: `${XSD}dateTime`,
9
10
  double: `${XSD}double`,
10
11
  integer: `${XSD}integer`,
11
12
  string: `${XSD}string`,
@@ -424,7 +424,7 @@ function literal(value, languageOrDataType) {
424
424
  return new Literal(`"${value}"@${languageOrDataType.language.toLowerCase()}${languageOrDataType.direction ? `--${languageOrDataType.direction.toLowerCase()}` : ''}`);
425
425
  }
426
426
 
427
- // Automatically determine datatype for booleans and numbers
427
+ // Automatically determine datatype for booleans, numbers, and dates
428
428
  let datatype = languageOrDataType ? languageOrDataType.value : '';
429
429
  if (datatype === '') {
430
430
  // Convert a boolean
@@ -440,6 +440,11 @@ function literal(value, languageOrDataType) {
440
440
  value = value > 0 ? 'INF' : '-INF';
441
441
  }
442
442
  }
443
+ // Convert a valid date
444
+ else if (value instanceof Date && !Number.isNaN(value.getTime())) {
445
+ datatype = xsd.dateTime;
446
+ value = value.toISOString();
447
+ }
443
448
  }
444
449
 
445
450
  // Create a datatyped literal