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/LICENSE.md +1 -1
- package/README.md +12 -3
- package/browser/n3.esm.min.js +6 -6
- package/browser/n3.min.js +1 -1
- package/lib/IRIs.js +1 -0
- package/lib/N3DataFactory.js +6 -1
- package/lib/N3Parser.js +82 -41
- package/package.json +1 -1
- package/src/IRIs.js +1 -0
- package/src/N3DataFactory.js +6 -1
- package/src/N3Parser.js +93 -40
package/lib/IRIs.js
CHANGED
package/lib/N3DataFactory.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
667
|
-
const
|
|
668
|
-
|
|
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
|
-
|
|
696
|
-
|
|
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
|
-
//
|
|
701
|
-
|
|
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
|
-
//
|
|
771
|
-
|
|
772
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
//
|
|
1046
|
-
if (this.
|
|
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
|
-
//
|
|
1061
|
-
if (this.
|
|
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
package/src/IRIs.js
CHANGED
package/src/N3DataFactory.js
CHANGED
|
@@ -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
|
|
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
|