n3 1.20.4 → 1.21.1
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/README.md +34 -10
- package/browser/n3.min.js +1 -1
- package/lib/N3Lexer.js +3 -3
- package/lib/N3Parser.js +31 -7
- package/lib/N3Store.js +43 -1
- package/lib/N3StreamParser.js +14 -9
- package/package.json +3 -3
- package/src/N3Lexer.js +3 -3
- package/src/N3Parser.js +38 -7
- package/src/N3Store.js +40 -2
- package/src/N3StreamParser.js +12 -6
package/lib/N3Lexer.js
CHANGED
|
@@ -97,7 +97,7 @@ class N3Lexer {
|
|
|
97
97
|
this._n3Mode = options.n3 !== false;
|
|
98
98
|
}
|
|
99
99
|
// Don't output comment tokens by default
|
|
100
|
-
this.
|
|
100
|
+
this.comments = !!options.comments;
|
|
101
101
|
// Cache the last tested closing position of long literals
|
|
102
102
|
this._literalClosingPos = 0;
|
|
103
103
|
}
|
|
@@ -114,7 +114,7 @@ class N3Lexer {
|
|
|
114
114
|
let whiteSpaceMatch, comment;
|
|
115
115
|
while (whiteSpaceMatch = this._newline.exec(input)) {
|
|
116
116
|
// Try to find a comment
|
|
117
|
-
if (this.
|
|
117
|
+
if (this.comments && (comment = this._comment.exec(whiteSpaceMatch[0]))) emitToken('comment', comment[1], '', this._line, whiteSpaceMatch[0].length);
|
|
118
118
|
// Advance the input
|
|
119
119
|
input = input.substr(whiteSpaceMatch[0].length, input.length);
|
|
120
120
|
currentLineLength = input.length;
|
|
@@ -128,7 +128,7 @@ class N3Lexer {
|
|
|
128
128
|
// If the input is finished, emit EOF
|
|
129
129
|
if (inputFinished) {
|
|
130
130
|
// Try to find a final comment
|
|
131
|
-
if (this.
|
|
131
|
+
if (this.comments && (comment = this._comment.exec(input))) emitToken('comment', comment[1], '', this._line, input.length);
|
|
132
132
|
input = null;
|
|
133
133
|
emitToken('eof', '', '', this._line, 0);
|
|
134
134
|
}
|
package/lib/N3Parser.js
CHANGED
|
@@ -943,20 +943,31 @@ class N3Parser {
|
|
|
943
943
|
|
|
944
944
|
// ## Public methods
|
|
945
945
|
|
|
946
|
-
// ### `parse` parses the N3 input and emits each parsed quad through the callback
|
|
946
|
+
// ### `parse` parses the N3 input and emits each parsed quad through the onQuad callback.
|
|
947
947
|
parse(input, quadCallback, prefixCallback) {
|
|
948
|
+
// The second parameter accepts an object { onQuad: ..., onPrefix: ..., onComment: ...}
|
|
949
|
+
// As a second and third parameter it still accepts a separate quadCallback and prefixCallback for backward compatibility as well
|
|
950
|
+
let onQuad, onPrefix, onComment;
|
|
951
|
+
if (quadCallback && (quadCallback.onQuad || quadCallback.onPrefix || quadCallback.onComment)) {
|
|
952
|
+
onQuad = quadCallback.onQuad;
|
|
953
|
+
onPrefix = quadCallback.onPrefix;
|
|
954
|
+
onComment = quadCallback.onComment;
|
|
955
|
+
} else {
|
|
956
|
+
onQuad = quadCallback;
|
|
957
|
+
onPrefix = prefixCallback;
|
|
958
|
+
}
|
|
948
959
|
// The read callback is the next function to be executed when a token arrives.
|
|
949
960
|
// We start reading in the top context.
|
|
950
961
|
this._readCallback = this._readInTopContext;
|
|
951
962
|
this._sparqlStyle = false;
|
|
952
963
|
this._prefixes = Object.create(null);
|
|
953
964
|
this._prefixes._ = this._blankNodePrefix ? this._blankNodePrefix.substr(2) : `b${blankNodePrefix++}_`;
|
|
954
|
-
this._prefixCallback =
|
|
965
|
+
this._prefixCallback = onPrefix || noop;
|
|
955
966
|
this._inversePredicate = false;
|
|
956
967
|
this._quantified = Object.create(null);
|
|
957
968
|
|
|
958
969
|
// Parse synchronously if no quad callback is given
|
|
959
|
-
if (!
|
|
970
|
+
if (!onQuad) {
|
|
960
971
|
const quads = [];
|
|
961
972
|
let error;
|
|
962
973
|
this._callback = (e, t) => {
|
|
@@ -968,12 +979,25 @@ class N3Parser {
|
|
|
968
979
|
if (error) throw error;
|
|
969
980
|
return quads;
|
|
970
981
|
}
|
|
982
|
+
let processNextToken = (error, token) => {
|
|
983
|
+
if (error !== null) this._callback(error), this._callback = noop;else if (this._readCallback) this._readCallback = this._readCallback(token);
|
|
984
|
+
};
|
|
985
|
+
|
|
986
|
+
// Enable checking for comments on every token when a commentCallback has been set
|
|
987
|
+
if (onComment) {
|
|
988
|
+
// Enable the lexer to return comments as tokens first (disabled by default)
|
|
989
|
+
this._lexer.comments = true;
|
|
990
|
+
// Patch the processNextToken function
|
|
991
|
+
processNextToken = (error, token) => {
|
|
992
|
+
if (error !== null) this._callback(error), this._callback = noop;else if (this._readCallback) {
|
|
993
|
+
if (token.type === 'comment') onComment(token.value);else this._readCallback = this._readCallback(token);
|
|
994
|
+
}
|
|
995
|
+
};
|
|
996
|
+
}
|
|
971
997
|
|
|
972
998
|
// Parse asynchronously otherwise, executing the read callback when a token arrives
|
|
973
|
-
this._callback =
|
|
974
|
-
this._lexer.tokenize(input,
|
|
975
|
-
if (error !== null) this._callback(error), this._callback = noop;else if (this._readCallback) this._readCallback = this._readCallback(token);
|
|
976
|
-
});
|
|
999
|
+
this._callback = onQuad;
|
|
1000
|
+
this._lexer.tokenize(input, processNextToken);
|
|
977
1001
|
}
|
|
978
1002
|
}
|
|
979
1003
|
|
package/lib/N3Store.js
CHANGED
|
@@ -21,6 +21,32 @@ function merge(target, source, depth = 4) {
|
|
|
21
21
|
return target;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Determines the intersection of the `_graphs` index s1 and s2.
|
|
26
|
+
* s1 and s2 *must* belong to Stores that share an `_entityIndex`.
|
|
27
|
+
*
|
|
28
|
+
* False is returned when there is no intersection; this should
|
|
29
|
+
* *not* be set as the value for an index.
|
|
30
|
+
*/
|
|
31
|
+
function intersect(s1, s2, depth = 4) {
|
|
32
|
+
let target = false;
|
|
33
|
+
for (const key in s1) {
|
|
34
|
+
if (key in s2) {
|
|
35
|
+
const intersection = depth === 0 ? null : intersect(s1[key], s2[key], depth - 1);
|
|
36
|
+
if (intersection !== false) {
|
|
37
|
+
target = target || Object.create(null);
|
|
38
|
+
target[key] = intersection;
|
|
39
|
+
}
|
|
40
|
+
// Depth 3 is the 'subjects', 'predicates' and 'objects' keys.
|
|
41
|
+
// If the 'subjects' index is empty, so will the 'predicates' and 'objects' index.
|
|
42
|
+
else if (depth === 3) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return target;
|
|
48
|
+
}
|
|
49
|
+
|
|
24
50
|
// ## Constructor
|
|
25
51
|
class N3EntityIndex {
|
|
26
52
|
constructor(options = {}) {
|
|
@@ -350,7 +376,12 @@ class N3Store {
|
|
|
350
376
|
// ### `removeQuad` removes a quad from the store if it exists
|
|
351
377
|
removeQuad(subject, predicate, object, graph) {
|
|
352
378
|
// Shift arguments if a quad object is given instead of components
|
|
353
|
-
if (!predicate)
|
|
379
|
+
if (!predicate) ({
|
|
380
|
+
subject,
|
|
381
|
+
predicate,
|
|
382
|
+
object,
|
|
383
|
+
graph
|
|
384
|
+
} = subject);
|
|
354
385
|
// Convert terms to internal string representation
|
|
355
386
|
graph = graph ? this._termToNumericId(graph) : 1;
|
|
356
387
|
|
|
@@ -846,6 +877,17 @@ class N3Store {
|
|
|
846
877
|
});
|
|
847
878
|
store._graphs = merge(Object.create(null), this._graphs);
|
|
848
879
|
store._size = this._size;
|
|
880
|
+
return store;
|
|
881
|
+
} else if (other instanceof N3Store && this._entityIndex === other._entityIndex) {
|
|
882
|
+
const store = new N3Store({
|
|
883
|
+
entityIndex: this._entityIndex
|
|
884
|
+
});
|
|
885
|
+
const graphs = intersect(other._graphs, this._graphs);
|
|
886
|
+
if (graphs) {
|
|
887
|
+
store._graphs = graphs;
|
|
888
|
+
store._size = null;
|
|
889
|
+
}
|
|
890
|
+
return store;
|
|
849
891
|
}
|
|
850
892
|
return this.filter(quad => other.has(quad));
|
|
851
893
|
}
|
package/lib/N3StreamParser.js
CHANGED
|
@@ -20,6 +20,19 @@ class N3StreamParser extends _readableStream.Transform {
|
|
|
20
20
|
// Set up parser with dummy stream to obtain `data` and `end` callbacks
|
|
21
21
|
const parser = new _N3Parser.default(options);
|
|
22
22
|
let onData, onEnd;
|
|
23
|
+
const callbacks = {
|
|
24
|
+
// Handle quads by pushing them down the pipeline
|
|
25
|
+
onQuad: (error, quad) => {
|
|
26
|
+
error && this.emit('error', error) || quad && this.push(quad);
|
|
27
|
+
},
|
|
28
|
+
// Emit prefixes through the `prefix` event
|
|
29
|
+
onPrefix: (prefix, uri) => {
|
|
30
|
+
this.emit('prefix', prefix, uri);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
if (options && options.comments) callbacks.onComment = comment => {
|
|
34
|
+
this.emit('comment', comment);
|
|
35
|
+
};
|
|
23
36
|
parser.parse({
|
|
24
37
|
on: (event, callback) => {
|
|
25
38
|
switch (event) {
|
|
@@ -31,15 +44,7 @@ class N3StreamParser extends _readableStream.Transform {
|
|
|
31
44
|
break;
|
|
32
45
|
}
|
|
33
46
|
}
|
|
34
|
-
},
|
|
35
|
-
// Handle quads by pushing them down the pipeline
|
|
36
|
-
(error, quad) => {
|
|
37
|
-
error && this.emit('error', error) || quad && this.push(quad);
|
|
38
|
-
},
|
|
39
|
-
// Emit prefixes through the `prefix` event
|
|
40
|
-
(prefix, uri) => {
|
|
41
|
-
this.emit('prefix', prefix, uri);
|
|
42
|
-
});
|
|
47
|
+
}, callbacks);
|
|
43
48
|
|
|
44
49
|
// Implement Transform methods through parser callbacks
|
|
45
50
|
this._transform = (chunk, encoding, done) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n3",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.1",
|
|
4
4
|
"description": "Lightning fast, asynchronous, streaming Turtle / N3 / RDF library.",
|
|
5
5
|
"author": "Ruben Verborgh <ruben.verborgh@gmail.com>",
|
|
6
6
|
"keywords": [
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"@babel/preset-env": "^7.16.0",
|
|
35
35
|
"@babel/register": "^7.16.0",
|
|
36
36
|
"@rdfjs/data-model": "^1",
|
|
37
|
-
"arrayify-stream": "^
|
|
37
|
+
"arrayify-stream": "^3.0.0",
|
|
38
38
|
"browserify": "^17.0.0",
|
|
39
|
-
"deep-taxonomy-benchmark": "^
|
|
39
|
+
"deep-taxonomy-benchmark": "^2.0.0",
|
|
40
40
|
"docco": "^0.9.1",
|
|
41
41
|
"eslint": "^8.57.0",
|
|
42
42
|
"eslint-plugin-import": "^2.29.1",
|
package/src/N3Lexer.js
CHANGED
|
@@ -68,7 +68,7 @@ export default class N3Lexer {
|
|
|
68
68
|
this._n3Mode = options.n3 !== false;
|
|
69
69
|
}
|
|
70
70
|
// Don't output comment tokens by default
|
|
71
|
-
this.
|
|
71
|
+
this.comments = !!options.comments;
|
|
72
72
|
// Cache the last tested closing position of long literals
|
|
73
73
|
this._literalClosingPos = 0;
|
|
74
74
|
}
|
|
@@ -85,7 +85,7 @@ export default class N3Lexer {
|
|
|
85
85
|
let whiteSpaceMatch, comment;
|
|
86
86
|
while (whiteSpaceMatch = this._newline.exec(input)) {
|
|
87
87
|
// Try to find a comment
|
|
88
|
-
if (this.
|
|
88
|
+
if (this.comments && (comment = this._comment.exec(whiteSpaceMatch[0])))
|
|
89
89
|
emitToken('comment', comment[1], '', this._line, whiteSpaceMatch[0].length);
|
|
90
90
|
// Advance the input
|
|
91
91
|
input = input.substr(whiteSpaceMatch[0].length, input.length);
|
|
@@ -101,7 +101,7 @@ export default class N3Lexer {
|
|
|
101
101
|
// If the input is finished, emit EOF
|
|
102
102
|
if (inputFinished) {
|
|
103
103
|
// Try to find a final comment
|
|
104
|
-
if (this.
|
|
104
|
+
if (this.comments && (comment = this._comment.exec(input)))
|
|
105
105
|
emitToken('comment', comment[1], '', this._line, input.length);
|
|
106
106
|
input = null;
|
|
107
107
|
emitToken('eof', '', '', this._line, 0);
|
package/src/N3Parser.js
CHANGED
|
@@ -1010,8 +1010,20 @@ export default class N3Parser {
|
|
|
1010
1010
|
|
|
1011
1011
|
// ## Public methods
|
|
1012
1012
|
|
|
1013
|
-
// ### `parse` parses the N3 input and emits each parsed quad through the callback
|
|
1013
|
+
// ### `parse` parses the N3 input and emits each parsed quad through the onQuad callback.
|
|
1014
1014
|
parse(input, quadCallback, prefixCallback) {
|
|
1015
|
+
// The second parameter accepts an object { onQuad: ..., onPrefix: ..., onComment: ...}
|
|
1016
|
+
// As a second and third parameter it still accepts a separate quadCallback and prefixCallback for backward compatibility as well
|
|
1017
|
+
let onQuad, onPrefix, onComment;
|
|
1018
|
+
if (quadCallback && (quadCallback.onQuad || quadCallback.onPrefix || quadCallback.onComment)) {
|
|
1019
|
+
onQuad = quadCallback.onQuad;
|
|
1020
|
+
onPrefix = quadCallback.onPrefix;
|
|
1021
|
+
onComment = quadCallback.onComment;
|
|
1022
|
+
}
|
|
1023
|
+
else {
|
|
1024
|
+
onQuad = quadCallback;
|
|
1025
|
+
onPrefix = prefixCallback;
|
|
1026
|
+
}
|
|
1015
1027
|
// The read callback is the next function to be executed when a token arrives.
|
|
1016
1028
|
// We start reading in the top context.
|
|
1017
1029
|
this._readCallback = this._readInTopContext;
|
|
@@ -1019,12 +1031,12 @@ export default class N3Parser {
|
|
|
1019
1031
|
this._prefixes = Object.create(null);
|
|
1020
1032
|
this._prefixes._ = this._blankNodePrefix ? this._blankNodePrefix.substr(2)
|
|
1021
1033
|
: `b${blankNodePrefix++}_`;
|
|
1022
|
-
this._prefixCallback =
|
|
1034
|
+
this._prefixCallback = onPrefix || noop;
|
|
1023
1035
|
this._inversePredicate = false;
|
|
1024
1036
|
this._quantified = Object.create(null);
|
|
1025
1037
|
|
|
1026
1038
|
// Parse synchronously if no quad callback is given
|
|
1027
|
-
if (!
|
|
1039
|
+
if (!onQuad) {
|
|
1028
1040
|
const quads = [];
|
|
1029
1041
|
let error;
|
|
1030
1042
|
this._callback = (e, t) => { e ? (error = e) : t && quads.push(t); };
|
|
@@ -1035,14 +1047,33 @@ export default class N3Parser {
|
|
|
1035
1047
|
return quads;
|
|
1036
1048
|
}
|
|
1037
1049
|
|
|
1038
|
-
|
|
1039
|
-
this._callback = quadCallback;
|
|
1040
|
-
this._lexer.tokenize(input, (error, token) => {
|
|
1050
|
+
let processNextToken = (error, token) => {
|
|
1041
1051
|
if (error !== null)
|
|
1042
1052
|
this._callback(error), this._callback = noop;
|
|
1043
1053
|
else if (this._readCallback)
|
|
1044
1054
|
this._readCallback = this._readCallback(token);
|
|
1045
|
-
}
|
|
1055
|
+
};
|
|
1056
|
+
|
|
1057
|
+
// Enable checking for comments on every token when a commentCallback has been set
|
|
1058
|
+
if (onComment) {
|
|
1059
|
+
// Enable the lexer to return comments as tokens first (disabled by default)
|
|
1060
|
+
this._lexer.comments = true;
|
|
1061
|
+
// Patch the processNextToken function
|
|
1062
|
+
processNextToken = (error, token) => {
|
|
1063
|
+
if (error !== null)
|
|
1064
|
+
this._callback(error), this._callback = noop;
|
|
1065
|
+
else if (this._readCallback) {
|
|
1066
|
+
if (token.type === 'comment')
|
|
1067
|
+
onComment(token.value);
|
|
1068
|
+
else
|
|
1069
|
+
this._readCallback = this._readCallback(token);
|
|
1070
|
+
}
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// Parse asynchronously otherwise, executing the read callback when a token arrives
|
|
1075
|
+
this._callback = onQuad;
|
|
1076
|
+
this._lexer.tokenize(input, processNextToken);
|
|
1046
1077
|
}
|
|
1047
1078
|
}
|
|
1048
1079
|
|
package/src/N3Store.js
CHANGED
|
@@ -17,6 +17,34 @@ function merge(target, source, depth = 4) {
|
|
|
17
17
|
return target;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Determines the intersection of the `_graphs` index s1 and s2.
|
|
22
|
+
* s1 and s2 *must* belong to Stores that share an `_entityIndex`.
|
|
23
|
+
*
|
|
24
|
+
* False is returned when there is no intersection; this should
|
|
25
|
+
* *not* be set as the value for an index.
|
|
26
|
+
*/
|
|
27
|
+
function intersect(s1, s2, depth = 4) {
|
|
28
|
+
let target = false;
|
|
29
|
+
|
|
30
|
+
for (const key in s1) {
|
|
31
|
+
if (key in s2) {
|
|
32
|
+
const intersection = depth === 0 ? null : intersect(s1[key], s2[key], depth - 1);
|
|
33
|
+
if (intersection !== false) {
|
|
34
|
+
target = target || Object.create(null);
|
|
35
|
+
target[key] = intersection;
|
|
36
|
+
}
|
|
37
|
+
// Depth 3 is the 'subjects', 'predicates' and 'objects' keys.
|
|
38
|
+
// If the 'subjects' index is empty, so will the 'predicates' and 'objects' index.
|
|
39
|
+
else if (depth === 3) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return target;
|
|
46
|
+
}
|
|
47
|
+
|
|
20
48
|
// ## Constructor
|
|
21
49
|
export class N3EntityIndex {
|
|
22
50
|
constructor(options = {}) {
|
|
@@ -356,8 +384,7 @@ export default class N3Store {
|
|
|
356
384
|
removeQuad(subject, predicate, object, graph) {
|
|
357
385
|
// Shift arguments if a quad object is given instead of components
|
|
358
386
|
if (!predicate)
|
|
359
|
-
|
|
360
|
-
predicate = subject.predicate, subject = subject.subject;
|
|
387
|
+
({ subject, predicate, object, graph } = subject);
|
|
361
388
|
// Convert terms to internal string representation
|
|
362
389
|
graph = graph ? this._termToNumericId(graph) : 1;
|
|
363
390
|
|
|
@@ -902,7 +929,18 @@ export default class N3Store {
|
|
|
902
929
|
const store = new N3Store({ entityIndex: this._entityIndex });
|
|
903
930
|
store._graphs = merge(Object.create(null), this._graphs);
|
|
904
931
|
store._size = this._size;
|
|
932
|
+
return store;
|
|
905
933
|
}
|
|
934
|
+
else if ((other instanceof N3Store) && this._entityIndex === other._entityIndex) {
|
|
935
|
+
const store = new N3Store({ entityIndex: this._entityIndex });
|
|
936
|
+
const graphs = intersect(other._graphs, this._graphs);
|
|
937
|
+
if (graphs) {
|
|
938
|
+
store._graphs = graphs;
|
|
939
|
+
store._size = null;
|
|
940
|
+
}
|
|
941
|
+
return store;
|
|
942
|
+
}
|
|
943
|
+
|
|
906
944
|
return this.filter(quad => other.has(quad));
|
|
907
945
|
}
|
|
908
946
|
|
package/src/N3StreamParser.js
CHANGED
|
@@ -11,6 +11,17 @@ export default class N3StreamParser extends Transform {
|
|
|
11
11
|
// Set up parser with dummy stream to obtain `data` and `end` callbacks
|
|
12
12
|
const parser = new N3Parser(options);
|
|
13
13
|
let onData, onEnd;
|
|
14
|
+
|
|
15
|
+
const callbacks = {
|
|
16
|
+
// Handle quads by pushing them down the pipeline
|
|
17
|
+
onQuad: (error, quad) => { error && this.emit('error', error) || quad && this.push(quad); },
|
|
18
|
+
// Emit prefixes through the `prefix` event
|
|
19
|
+
onPrefix: (prefix, uri) => { this.emit('prefix', prefix, uri); },
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
if (options && options.comments)
|
|
23
|
+
callbacks.onComment = comment => { this.emit('comment', comment); };
|
|
24
|
+
|
|
14
25
|
parser.parse({
|
|
15
26
|
on: (event, callback) => {
|
|
16
27
|
switch (event) {
|
|
@@ -18,12 +29,7 @@ export default class N3StreamParser extends Transform {
|
|
|
18
29
|
case 'end': onEnd = callback; break;
|
|
19
30
|
}
|
|
20
31
|
},
|
|
21
|
-
},
|
|
22
|
-
// Handle quads by pushing them down the pipeline
|
|
23
|
-
(error, quad) => { error && this.emit('error', error) || quad && this.push(quad); },
|
|
24
|
-
// Emit prefixes through the `prefix` event
|
|
25
|
-
(prefix, uri) => { this.emit('prefix', prefix, uri); },
|
|
26
|
-
);
|
|
32
|
+
}, callbacks);
|
|
27
33
|
|
|
28
34
|
// Implement Transform methods through parser callbacks
|
|
29
35
|
this._transform = (chunk, encoding, done) => { onData(chunk); done(); };
|