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/src/N3Parser.js CHANGED
@@ -83,14 +83,21 @@ export default class N3Parser {
83
83
  this._contextStack.push({ type, subject, predicate, object, graph });
84
84
  return;
85
85
  }
86
- this._contextStack.push({
86
+ const context = {
87
87
  type,
88
88
  subject, predicate, object, graph,
89
89
  inverse: this._inversePredicate,
90
90
  blankPrefix: this._prefixes._,
91
91
  quantified: this._quantified,
92
92
  emptyFormula: this._emptyFormula,
93
- });
93
+ };
94
+ // Prefix and base declarations are scoped to their formula
95
+ if (type === 'formula') {
96
+ context.prefixes = this._prefixes;
97
+ context.base = [this._base, this._basePath, this._baseRoot, this._baseScheme];
98
+ this._prefixes = Object.create(this._prefixes);
99
+ }
100
+ this._contextStack.push(context);
94
101
  // Every new scope resets the predicate direction
95
102
  this._inversePredicate = false;
96
103
  // In N3, blank nodes are scoped to a formula
@@ -122,7 +129,12 @@ export default class N3Parser {
122
129
  // Restore N3 context settings
123
130
  if (this._n3Mode) {
124
131
  this._inversePredicate = context.inverse;
125
- this._prefixes._ = context.blankPrefix;
132
+ if (type === 'formula') {
133
+ this._prefixes = context.prefixes;
134
+ [this._base, this._basePath, this._baseRoot, this._baseScheme] = context.base;
135
+ }
136
+ else
137
+ this._prefixes._ = context.blankPrefix;
126
138
  this._quantified = context.quantified;
127
139
  this._emptyFormula = context.emptyFormula;
128
140
  }
@@ -175,6 +187,28 @@ export default class N3Parser {
175
187
  }
176
188
  }
177
189
 
190
+ // ### `_readInFormulaContext` reads a token at the statement level of a formula
191
+ _readInFormulaContext(token) {
192
+ switch (token.type) {
193
+ case 'PREFIX':
194
+ this._sparqlStyle = true;
195
+ case '@prefix':
196
+ return this._readPrefix;
197
+ case 'BASE':
198
+ this._sparqlStyle = true;
199
+ case '@base':
200
+ return this._readBaseIRI;
201
+ default:
202
+ return this._readSubject(token);
203
+ }
204
+ }
205
+
206
+ // ### `_getStatementReader` returns the reader for the current statement scope
207
+ _getStatementReader() {
208
+ const context = this._contextStack[this._contextStack.length - 1];
209
+ return context && context.type === 'formula' ? this._readInFormulaContext : this._readInTopContext;
210
+ }
211
+
178
212
  // ### `_readEntity` reads an IRI, prefixed name, blank node, or variable
179
213
  _readEntity(token, quantifier) {
180
214
  let value;
@@ -245,7 +279,7 @@ export default class N3Parser {
245
279
  return this._error('Unexpected graph', token);
246
280
  this._saveContext('formula', this._graph,
247
281
  this._graph = this._factory.blankNode(), null, null);
248
- return this._readSubject;
282
+ return this._readInFormulaContext;
249
283
  case '}':
250
284
  // No subject; the graph in which we are reading is closed instead
251
285
  return this._readPunctuation(token);
@@ -303,6 +337,7 @@ export default class N3Parser {
303
337
  // ### `_readPredicate` reads a quad's predicate
304
338
  _readPredicate(token) {
305
339
  const type = token.type;
340
+ let pathable = false;
306
341
  switch (type) {
307
342
  case 'inverse':
308
343
  this._inversePredicate = true;
@@ -334,6 +369,7 @@ export default class N3Parser {
334
369
  else
335
370
  this._predicate = this._factory.literal(token.value, this._factory.namedNode(token.prefix));
336
371
 
372
+ pathable = true;
337
373
  break;
338
374
  case '(':
339
375
  // In N3, a list can be a predicate
@@ -347,16 +383,26 @@ export default class N3Parser {
347
383
  this._subject = this._factory.blankNode(), null);
348
384
  return this._readBlankNodeHead;
349
385
  }
386
+ return this._error('Disallowed blank node as predicate', token);
387
+ case '{':
388
+ // In N3, a formula can be a predicate
389
+ if (this._n3Mode) {
390
+ this._saveContext('formula', this._graph, this._subject,
391
+ this._graph = this._factory.blankNode(), null);
392
+ return this._readSubject;
393
+ }
394
+ return this._readEntity(token);
350
395
  case 'blank':
351
396
  if (!this._n3Mode)
352
397
  return this._error('Disallowed blank node as predicate', token);
353
398
  default:
354
399
  if ((this._predicate = this._readEntity(token)) === undefined)
355
400
  return;
401
+ pathable = this._n3Mode;
356
402
  }
357
403
  this._validAnnotation = true;
358
404
  // The next token must be an object
359
- return this._readObject;
405
+ return pathable ? this._getPathReader(this._readObject, 'predicate') : this._readObject;
360
406
  }
361
407
 
362
408
  // ### `_readObject` reads a quad's object
@@ -389,7 +435,7 @@ export default class N3Parser {
389
435
  return this._error('Unexpected graph', token);
390
436
  this._saveContext('formula', this._graph, this._subject, this._predicate,
391
437
  this._graph = this._factory.blankNode());
392
- return this._readSubject;
438
+ return this._readInFormulaContext;
393
439
  case '<<(':
394
440
  this._saveContext('<<(', this._graph, this._subject, this._predicate, null);
395
441
  this._graph = null;
@@ -456,7 +502,7 @@ export default class N3Parser {
456
502
  return this._getContextEndReader();
457
503
  // If the blank node was the predicate, continue reading the object
458
504
  else if (this._predicate !== null)
459
- return this._readObject;
505
+ return this._getPathReader(this._readObject, 'predicate');
460
506
  // If the blank node was the subject, continue reading the predicate
461
507
  else
462
508
  // If the blank node was empty, it could be a named graph label
@@ -529,7 +575,7 @@ export default class N3Parser {
529
575
  // Was this list the parent's predicate?
530
576
  else if (this._object === null) {
531
577
  // The next token is the object
532
- next = this._readObject;
578
+ next = this._getPathReader(this._readObject, 'predicate');
533
579
  // No list tail if this was an empty list
534
580
  if (this._predicate === this.RDF_NIL)
535
581
  return next;
@@ -585,7 +631,7 @@ export default class N3Parser {
585
631
  this._saveContext('formula', this._graph, list, this.RDF_FIRST,
586
632
  this._graph = item);
587
633
  this._subject = null;
588
- return this._readSubject;
634
+ return this._readInFormulaContext;
589
635
  case '<<(':
590
636
  this._saveContext('<<(', this._graph, null, null, null);
591
637
  this._graph = null;
@@ -706,13 +752,12 @@ export default class N3Parser {
706
752
  token = null;
707
753
  }
708
754
 
709
- if (component === 'subject') {
710
- // A subject literal implies N3 mode, so the literal might start a path
711
- const reader = this._getPathEndReader(token, this._readPredicateOrNamedGraph);
712
- return reader || this._readPredicateOrNamedGraph(token);
755
+ if (component === 'subject' || component === 'predicate') {
756
+ // A subject or predicate literal implies N3 mode, so it might start a path
757
+ const next = component === 'subject' ? this._readPredicateOrNamedGraph : this._readObject;
758
+ const reader = this._getPathEndReader(token, next, component);
759
+ return reader || next.call(this, token);
713
760
  }
714
- if (component === 'predicate')
715
- return token === null ? this._readObject : this._readObject(token);
716
761
  return this._completeObjectLiteralPost(token, listItem);
717
762
  }
718
763
 
@@ -739,16 +784,13 @@ export default class N3Parser {
739
784
  return this._readDirCode;
740
785
  }
741
786
 
742
- // A subject literal implies N3 mode, so it might start a path.
743
- if (component === 'subject') {
744
- const reader = this._getPathEndReader(completed.token, next);
745
- if (reader)
746
- return reader;
747
- }
787
+ // A subject or predicate literal implies N3 mode, so it might start a path.
788
+ const reader = this._getPathEndReader(completed.token, next, component);
789
+ if (reader)
790
+ return reader;
748
791
 
749
- // If the token was consumed as a datatype, continue with the next component;
750
- // otherwise, consume the token now
751
- return completed.token === null ? next : next.call(this, completed.token);
792
+ // Consume the non-path token now
793
+ return next.call(this, completed.token);
752
794
  }
753
795
 
754
796
  // Completes a literal in subject position
@@ -823,13 +865,18 @@ export default class N3Parser {
823
865
  if (empty && this._emptyFormulaAsTrue) {
824
866
  if (this._subject === formula)
825
867
  this._subject = this.N3_TRUE;
868
+ else if (this._predicate === formula)
869
+ this._predicate = this.N3_TRUE;
826
870
  else
827
871
  this._object = this.N3_TRUE;
828
872
  }
829
873
 
830
- // If the formula was the subject, continue reading the predicate.
831
- // If the formula was the object, read punctuation.
832
- return this._object === null ? this._readPredicate : this._getContextEndReader();
874
+ // Continue according to the formula's position in the enclosing statement
875
+ if (this._object !== null)
876
+ return this._getPathReader(this._getContextEndReader(), 'object');
877
+ if (this._predicate !== null)
878
+ return this._getPathReader(this._readObject, 'predicate');
879
+ return this._getPathReader(this._readPredicate, 'subject');
833
880
  }
834
881
 
835
882
  // ### `_readPunctuation` reads punctuation between quads or quad parts
@@ -848,7 +895,7 @@ export default class N3Parser {
848
895
  case '.':
849
896
  this._subject = null;
850
897
  this._tripleTerm = null;
851
- next = this._contextStack.length ? this._readSubject : this._readInTopContext;
898
+ next = this._getStatementReader();
852
899
  if (inversePredicate) this._inversePredicate = false;
853
900
  break;
854
901
  // Semicolon means the subject is shared; predicate and object are different
@@ -1021,12 +1068,12 @@ export default class N3Parser {
1021
1068
  // SPARQL-style declarations don't have punctuation
1022
1069
  if (this._sparqlStyle) {
1023
1070
  this._sparqlStyle = false;
1024
- return this._readInTopContext(token);
1071
+ return this._getStatementReader().call(this, token);
1025
1072
  }
1026
1073
 
1027
1074
  if (token.type !== '.')
1028
1075
  return this._error('Expected declaration to end with a dot', token);
1029
- return this._readInTopContext;
1076
+ return this._getStatementReader();
1030
1077
  }
1031
1078
 
1032
1079
  // Reads a list of quantified symbols from a @forSome or @forAll statement
@@ -1078,18 +1125,19 @@ export default class N3Parser {
1078
1125
  }
1079
1126
 
1080
1127
  // ### `_getPathReader` reads a potential path and then resumes with the given function
1081
- _getPathReader(afterPath) {
1128
+ _getPathReader(afterPath, position) {
1082
1129
  this._afterPath = afterPath;
1130
+ this._pathPosition = position || (this._predicate === null ? 'subject' : 'object');
1083
1131
  return this._readPath;
1084
1132
  }
1085
1133
 
1086
1134
  // ### `_getPathEndReader` continues reading after a term that might start a path,
1087
1135
  // given the pending token that follows the term (or `null` if it was consumed)
1088
- _getPathEndReader(token, afterPath) {
1136
+ _getPathEndReader(token, afterPath, position) {
1089
1137
  // Other pending tokens are not handled here
1090
1138
  if (token !== null && token.type !== '!' && token.type !== '^')
1091
1139
  return null;
1092
- const reader = this._getPathReader(afterPath);
1140
+ const reader = this._getPathReader(afterPath, position);
1093
1141
  // If no token is pending, wait for the next one; otherwise, consume it now
1094
1142
  return token === null ? reader : reader.call(this, token);
1095
1143
  }
@@ -1103,6 +1151,7 @@ export default class N3Parser {
1103
1151
  case '^': return this._readBackwardPath;
1104
1152
  // Not a path; resume reading where we left off
1105
1153
  default:
1154
+ const afterPath = this._afterPath;
1106
1155
  const stack = this._contextStack, parent = stack.length && stack[stack.length - 1];
1107
1156
  // If we were reading a list item, we still need to output it
1108
1157
  if (parent && parent.type === 'item') {
@@ -1113,7 +1162,9 @@ export default class N3Parser {
1113
1162
  // Output the list item
1114
1163
  this._emit(this._subject, this.RDF_FIRST, item, this._graph);
1115
1164
  }
1116
- return this._afterPath(token);
1165
+ this._afterPath = null;
1166
+ this._pathPosition = null;
1167
+ return afterPath.call(this, token);
1117
1168
  }
1118
1169
  }
1119
1170
 
@@ -1124,10 +1175,11 @@ export default class N3Parser {
1124
1175
  // The next token is the predicate
1125
1176
  if ((predicate = this._readEntity(token)) === undefined)
1126
1177
  return;
1127
- // If we were reading a subject, replace the subject by the path's object
1128
- if (this._predicate === null)
1178
+ // Replace the path expression with the generated object in its current position
1179
+ if (this._pathPosition === 'subject')
1129
1180
  subject = this._subject, this._subject = object;
1130
- // If we were reading an object, replace the subject by the path's object
1181
+ else if (this._pathPosition === 'predicate')
1182
+ subject = this._predicate, this._predicate = object;
1131
1183
  else
1132
1184
  subject = this._object, this._object = object;
1133
1185
  // Emit the path's current quad and read its next section
@@ -1142,10 +1194,11 @@ export default class N3Parser {
1142
1194
  // The next token is the predicate
1143
1195
  if ((predicate = this._readEntity(token)) === undefined)
1144
1196
  return;
1145
- // If we were reading a subject, replace the subject by the path's subject
1146
- if (this._predicate === null)
1197
+ // Replace the path expression with the generated subject in its current position
1198
+ if (this._pathPosition === 'subject')
1147
1199
  object = this._subject, this._subject = subject;
1148
- // If we were reading an object, replace the subject by the path's subject
1200
+ else if (this._pathPosition === 'predicate')
1201
+ object = this._predicate, this._predicate = subject;
1149
1202
  else
1150
1203
  object = this._object, this._object = subject;
1151
1204
  // Emit the path's current quad and read its next section