n3 2.3.0 → 2.5.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/LICENSE.md +1 -1
- package/README.md +2 -3
- package/browser/n3.esm.min.js +5 -5
- package/browser/n3.min.js +1 -1
- package/lib/N3Lexer.js +5 -0
- package/lib/N3Parser.js +104 -41
- package/package.json +1 -1
- package/src/N3Lexer.js +9 -0
- package/src/N3Parser.js +123 -40
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
438
|
+
return this._readInFormulaContext;
|
|
393
439
|
case '<<(':
|
|
394
440
|
this._saveContext('<<(', this._graph, this._subject, this._predicate, null);
|
|
395
441
|
this._graph = null;
|
|
@@ -434,11 +480,41 @@ export default class N3Parser {
|
|
|
434
480
|
if (parentParent.type === '<<') {
|
|
435
481
|
return this._error('Unexpected compound blank node expression in reified triple', token);
|
|
436
482
|
}
|
|
483
|
+
if (token.type === 'id')
|
|
484
|
+
return this._readIriPropertyListId;
|
|
437
485
|
this._predicate = null;
|
|
438
486
|
return this._readPredicate(token);
|
|
439
487
|
}
|
|
440
488
|
}
|
|
441
489
|
|
|
490
|
+
// ### `_readIriPropertyListId` replaces a property list's blank node with its IRI
|
|
491
|
+
_readIriPropertyListId(token) {
|
|
492
|
+
const iri = this._readEntity(token);
|
|
493
|
+
if (iri === undefined)
|
|
494
|
+
return;
|
|
495
|
+
if (iri.termType !== 'NamedNode')
|
|
496
|
+
return this._error(`Expected IRI after id but got ${token.type}`, token);
|
|
497
|
+
|
|
498
|
+
const placeholder = this._subject;
|
|
499
|
+
this._subject = iri;
|
|
500
|
+
const context = this._contextStack[this._contextStack.length - 1];
|
|
501
|
+
if (context.subject === placeholder)
|
|
502
|
+
context.subject = iri;
|
|
503
|
+
if (context.predicate === placeholder)
|
|
504
|
+
context.predicate = iri;
|
|
505
|
+
if (context.object === placeholder)
|
|
506
|
+
context.object = iri;
|
|
507
|
+
this._predicate = null;
|
|
508
|
+
return this._readIriPropertyListPredicate;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// ### `_readIriPropertyListPredicate` requires properties after an IRI property list ID
|
|
512
|
+
_readIriPropertyListPredicate(token) {
|
|
513
|
+
if (token.type === ';' || token.type === ']' || token.type === '.' || token.type === '}')
|
|
514
|
+
return this._error(`Expected predicate but got ${token.type}`, token);
|
|
515
|
+
return this._readPredicate(token);
|
|
516
|
+
}
|
|
517
|
+
|
|
442
518
|
// ### `_readBlankNodeTail` reads the end of a blank node
|
|
443
519
|
_readBlankNodeTail(token) {
|
|
444
520
|
if (token.type !== ']')
|
|
@@ -456,7 +532,7 @@ export default class N3Parser {
|
|
|
456
532
|
return this._getContextEndReader();
|
|
457
533
|
// If the blank node was the predicate, continue reading the object
|
|
458
534
|
else if (this._predicate !== null)
|
|
459
|
-
return this._readObject;
|
|
535
|
+
return this._getPathReader(this._readObject, 'predicate');
|
|
460
536
|
// If the blank node was the subject, continue reading the predicate
|
|
461
537
|
else
|
|
462
538
|
// If the blank node was empty, it could be a named graph label
|
|
@@ -529,7 +605,7 @@ export default class N3Parser {
|
|
|
529
605
|
// Was this list the parent's predicate?
|
|
530
606
|
else if (this._object === null) {
|
|
531
607
|
// The next token is the object
|
|
532
|
-
next = this._readObject;
|
|
608
|
+
next = this._getPathReader(this._readObject, 'predicate');
|
|
533
609
|
// No list tail if this was an empty list
|
|
534
610
|
if (this._predicate === this.RDF_NIL)
|
|
535
611
|
return next;
|
|
@@ -585,7 +661,7 @@ export default class N3Parser {
|
|
|
585
661
|
this._saveContext('formula', this._graph, list, this.RDF_FIRST,
|
|
586
662
|
this._graph = item);
|
|
587
663
|
this._subject = null;
|
|
588
|
-
return this.
|
|
664
|
+
return this._readInFormulaContext;
|
|
589
665
|
case '<<(':
|
|
590
666
|
this._saveContext('<<(', this._graph, null, null, null);
|
|
591
667
|
this._graph = null;
|
|
@@ -706,13 +782,12 @@ export default class N3Parser {
|
|
|
706
782
|
token = null;
|
|
707
783
|
}
|
|
708
784
|
|
|
709
|
-
if (component === 'subject') {
|
|
710
|
-
// A subject literal implies N3 mode, so
|
|
711
|
-
const
|
|
712
|
-
|
|
785
|
+
if (component === 'subject' || component === 'predicate') {
|
|
786
|
+
// A subject or predicate literal implies N3 mode, so it might start a path
|
|
787
|
+
const next = component === 'subject' ? this._readPredicateOrNamedGraph : this._readObject;
|
|
788
|
+
const reader = this._getPathEndReader(token, next, component);
|
|
789
|
+
return reader || next.call(this, token);
|
|
713
790
|
}
|
|
714
|
-
if (component === 'predicate')
|
|
715
|
-
return token === null ? this._readObject : this._readObject(token);
|
|
716
791
|
return this._completeObjectLiteralPost(token, listItem);
|
|
717
792
|
}
|
|
718
793
|
|
|
@@ -739,16 +814,13 @@ export default class N3Parser {
|
|
|
739
814
|
return this._readDirCode;
|
|
740
815
|
}
|
|
741
816
|
|
|
742
|
-
// A subject literal implies N3 mode, so it might start a path.
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
return reader;
|
|
747
|
-
}
|
|
817
|
+
// A subject or predicate literal implies N3 mode, so it might start a path.
|
|
818
|
+
const reader = this._getPathEndReader(completed.token, next, component);
|
|
819
|
+
if (reader)
|
|
820
|
+
return reader;
|
|
748
821
|
|
|
749
|
-
//
|
|
750
|
-
|
|
751
|
-
return completed.token === null ? next : next.call(this, completed.token);
|
|
822
|
+
// Consume the non-path token now
|
|
823
|
+
return next.call(this, completed.token);
|
|
752
824
|
}
|
|
753
825
|
|
|
754
826
|
// Completes a literal in subject position
|
|
@@ -823,13 +895,18 @@ export default class N3Parser {
|
|
|
823
895
|
if (empty && this._emptyFormulaAsTrue) {
|
|
824
896
|
if (this._subject === formula)
|
|
825
897
|
this._subject = this.N3_TRUE;
|
|
898
|
+
else if (this._predicate === formula)
|
|
899
|
+
this._predicate = this.N3_TRUE;
|
|
826
900
|
else
|
|
827
901
|
this._object = this.N3_TRUE;
|
|
828
902
|
}
|
|
829
903
|
|
|
830
|
-
//
|
|
831
|
-
|
|
832
|
-
|
|
904
|
+
// Continue according to the formula's position in the enclosing statement
|
|
905
|
+
if (this._object !== null)
|
|
906
|
+
return this._getPathReader(this._getContextEndReader(), 'object');
|
|
907
|
+
if (this._predicate !== null)
|
|
908
|
+
return this._getPathReader(this._readObject, 'predicate');
|
|
909
|
+
return this._getPathReader(this._readPredicate, 'subject');
|
|
833
910
|
}
|
|
834
911
|
|
|
835
912
|
// ### `_readPunctuation` reads punctuation between quads or quad parts
|
|
@@ -848,7 +925,7 @@ export default class N3Parser {
|
|
|
848
925
|
case '.':
|
|
849
926
|
this._subject = null;
|
|
850
927
|
this._tripleTerm = null;
|
|
851
|
-
next = this.
|
|
928
|
+
next = this._getStatementReader();
|
|
852
929
|
if (inversePredicate) this._inversePredicate = false;
|
|
853
930
|
break;
|
|
854
931
|
// Semicolon means the subject is shared; predicate and object are different
|
|
@@ -1021,12 +1098,12 @@ export default class N3Parser {
|
|
|
1021
1098
|
// SPARQL-style declarations don't have punctuation
|
|
1022
1099
|
if (this._sparqlStyle) {
|
|
1023
1100
|
this._sparqlStyle = false;
|
|
1024
|
-
return this.
|
|
1101
|
+
return this._getStatementReader().call(this, token);
|
|
1025
1102
|
}
|
|
1026
1103
|
|
|
1027
1104
|
if (token.type !== '.')
|
|
1028
1105
|
return this._error('Expected declaration to end with a dot', token);
|
|
1029
|
-
return this.
|
|
1106
|
+
return this._getStatementReader();
|
|
1030
1107
|
}
|
|
1031
1108
|
|
|
1032
1109
|
// Reads a list of quantified symbols from a @forSome or @forAll statement
|
|
@@ -1078,18 +1155,19 @@ export default class N3Parser {
|
|
|
1078
1155
|
}
|
|
1079
1156
|
|
|
1080
1157
|
// ### `_getPathReader` reads a potential path and then resumes with the given function
|
|
1081
|
-
_getPathReader(afterPath) {
|
|
1158
|
+
_getPathReader(afterPath, position) {
|
|
1082
1159
|
this._afterPath = afterPath;
|
|
1160
|
+
this._pathPosition = position || (this._predicate === null ? 'subject' : 'object');
|
|
1083
1161
|
return this._readPath;
|
|
1084
1162
|
}
|
|
1085
1163
|
|
|
1086
1164
|
// ### `_getPathEndReader` continues reading after a term that might start a path,
|
|
1087
1165
|
// given the pending token that follows the term (or `null` if it was consumed)
|
|
1088
|
-
_getPathEndReader(token, afterPath) {
|
|
1166
|
+
_getPathEndReader(token, afterPath, position) {
|
|
1089
1167
|
// Other pending tokens are not handled here
|
|
1090
1168
|
if (token !== null && token.type !== '!' && token.type !== '^')
|
|
1091
1169
|
return null;
|
|
1092
|
-
const reader = this._getPathReader(afterPath);
|
|
1170
|
+
const reader = this._getPathReader(afterPath, position);
|
|
1093
1171
|
// If no token is pending, wait for the next one; otherwise, consume it now
|
|
1094
1172
|
return token === null ? reader : reader.call(this, token);
|
|
1095
1173
|
}
|
|
@@ -1103,6 +1181,7 @@ export default class N3Parser {
|
|
|
1103
1181
|
case '^': return this._readBackwardPath;
|
|
1104
1182
|
// Not a path; resume reading where we left off
|
|
1105
1183
|
default:
|
|
1184
|
+
const afterPath = this._afterPath;
|
|
1106
1185
|
const stack = this._contextStack, parent = stack.length && stack[stack.length - 1];
|
|
1107
1186
|
// If we were reading a list item, we still need to output it
|
|
1108
1187
|
if (parent && parent.type === 'item') {
|
|
@@ -1113,7 +1192,9 @@ export default class N3Parser {
|
|
|
1113
1192
|
// Output the list item
|
|
1114
1193
|
this._emit(this._subject, this.RDF_FIRST, item, this._graph);
|
|
1115
1194
|
}
|
|
1116
|
-
|
|
1195
|
+
this._afterPath = null;
|
|
1196
|
+
this._pathPosition = null;
|
|
1197
|
+
return afterPath.call(this, token);
|
|
1117
1198
|
}
|
|
1118
1199
|
}
|
|
1119
1200
|
|
|
@@ -1124,10 +1205,11 @@ export default class N3Parser {
|
|
|
1124
1205
|
// The next token is the predicate
|
|
1125
1206
|
if ((predicate = this._readEntity(token)) === undefined)
|
|
1126
1207
|
return;
|
|
1127
|
-
//
|
|
1128
|
-
if (this.
|
|
1208
|
+
// Replace the path expression with the generated object in its current position
|
|
1209
|
+
if (this._pathPosition === 'subject')
|
|
1129
1210
|
subject = this._subject, this._subject = object;
|
|
1130
|
-
|
|
1211
|
+
else if (this._pathPosition === 'predicate')
|
|
1212
|
+
subject = this._predicate, this._predicate = object;
|
|
1131
1213
|
else
|
|
1132
1214
|
subject = this._object, this._object = object;
|
|
1133
1215
|
// Emit the path's current quad and read its next section
|
|
@@ -1142,10 +1224,11 @@ export default class N3Parser {
|
|
|
1142
1224
|
// The next token is the predicate
|
|
1143
1225
|
if ((predicate = this._readEntity(token)) === undefined)
|
|
1144
1226
|
return;
|
|
1145
|
-
//
|
|
1146
|
-
if (this.
|
|
1227
|
+
// Replace the path expression with the generated subject in its current position
|
|
1228
|
+
if (this._pathPosition === 'subject')
|
|
1147
1229
|
object = this._subject, this._subject = subject;
|
|
1148
|
-
|
|
1230
|
+
else if (this._pathPosition === 'predicate')
|
|
1231
|
+
object = this._predicate, this._predicate = subject;
|
|
1149
1232
|
else
|
|
1150
1233
|
object = this._object, this._object = subject;
|
|
1151
1234
|
// Emit the path's current quad and read its next section
|