n3 3.0.0-alpha.3 → 3.0.0-alpha.5
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 +32 -0
- package/browser/n3.esm.min.js +11 -11
- package/browser/n3.min.js +11 -11
- package/lib/N3Lexer.js +204 -149
- package/lib/N3Parser.js +54 -27
- package/lib/N3Reasoner.js +1 -1
- package/lib/N3Store.js +46 -5
- package/lib/N3StreamParser.js +4 -0
- package/lib/N3Writer.js +46 -16
- package/lib/Util.js +1 -1
- package/package.json +2 -29
- package/src/N3Lexer.js +228 -162
- package/src/N3Parser.js +61 -27
- package/src/N3Reasoner.js +1 -1
- package/src/N3Store.js +57 -8
- package/src/N3StreamParser.js +2 -0
- package/src/N3Writer.js +48 -20
- package/src/Util.js +1 -1
package/lib/N3Parser.js
CHANGED
|
@@ -54,10 +54,17 @@ class N3Parser {
|
|
|
54
54
|
return null;
|
|
55
55
|
};
|
|
56
56
|
this._blankNodePrefix = typeof options.blankNodePrefix !== 'string' ? '' : options.blankNodePrefix.replace(/^(?!_:)/, '_:');
|
|
57
|
+
// Map the token types of additional directives (@name and NAME) to their names
|
|
58
|
+
this._directives = null;
|
|
59
|
+
if (options.directives) {
|
|
60
|
+
this._directives = Object.create(null);
|
|
61
|
+
for (const name of options.directives) this._directives[`@${name.toLowerCase()}`] = this._directives[name.toUpperCase()] = name;
|
|
62
|
+
}
|
|
57
63
|
this._lexer = options.lexer || new _N3Lexer.default({
|
|
58
64
|
lineMode: isLineMode,
|
|
59
65
|
n3: isN3,
|
|
60
|
-
isImpliedBy: this._isImpliedBy
|
|
66
|
+
isImpliedBy: this._isImpliedBy,
|
|
67
|
+
directives: options.directives
|
|
61
68
|
});
|
|
62
69
|
// Disable explicit quantifiers by default
|
|
63
70
|
this._explicitQuantifiers = !!options.explicitQuantifiers;
|
|
@@ -66,6 +73,9 @@ class N3Parser {
|
|
|
66
73
|
// Disable parsing of unsupported versions by default
|
|
67
74
|
this._parseUnsupportedVersions = !!options.parseUnsupportedVersions;
|
|
68
75
|
this._version = options.version;
|
|
76
|
+
// Maximum nesting depth of triple terms (default 1024; Infinity disables the limit)
|
|
77
|
+
const maxTripleTermDepth = options.maxTripleTermDepth;
|
|
78
|
+
this._maxTripleTermDepth = maxTripleTermDepth === Infinity || Number.isInteger(maxTripleTermDepth) && maxTripleTermDepth >= 0 ? maxTripleTermDepth : 1024;
|
|
69
79
|
}
|
|
70
80
|
|
|
71
81
|
// ## Static class methods
|
|
@@ -91,12 +101,29 @@ class N3Parser {
|
|
|
91
101
|
const queryPos = baseIRI.indexOf('?');
|
|
92
102
|
const path = queryPos < 0 ? baseIRI : baseIRI.substr(0, queryPos);
|
|
93
103
|
this._basePath = baseIRI.indexOf('/') < 0 ? baseIRI : path.substr(0, path.lastIndexOf('/') + 1);
|
|
94
|
-
|
|
104
|
+
const base = baseIRI;
|
|
105
|
+
baseIRI = baseIRI.match(/^(?:([a-z][a-z0-9+.-]*:))?(\/\/[^\/?#]*)?/i);
|
|
95
106
|
this._baseRoot = baseIRI[0];
|
|
96
107
|
this._baseScheme = baseIRI[1];
|
|
108
|
+
// If the base has an authority but an empty path,
|
|
109
|
+
// relative IRIs merge under the path '/' (RFC 3986 §5.2.3)
|
|
110
|
+
if (baseIRI[2] !== undefined && (base.length === this._baseRoot.length || base[this._baseRoot.length] === '?')) this._basePath = `${this._baseRoot}/`;
|
|
97
111
|
}
|
|
98
112
|
}
|
|
99
113
|
|
|
114
|
+
// ### `_enterTripleTerm` stores the current parsing context when entering a triple term,
|
|
115
|
+
// failing if triple terms nest deeper than the maximum depth
|
|
116
|
+
_enterTripleTerm(type, subject, predicate, token) {
|
|
117
|
+
if (this._tripleTermDepth >= this._maxTripleTermDepth) {
|
|
118
|
+
this._error(`Triple terms nested deeper than ${this._maxTripleTermDepth} level${this._maxTripleTermDepth === 1 ? '' : 's'}`, token);
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
this._tripleTermDepth++;
|
|
122
|
+
this._saveContext(type, this._graph, subject, predicate, null);
|
|
123
|
+
this._graph = null;
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
|
|
100
127
|
// ### `_saveContext` stores the current parsing context
|
|
101
128
|
// when entering a new scope (list, blank node, formula)
|
|
102
129
|
_saveContext(type, graph, subject, predicate, object) {
|
|
@@ -219,8 +246,13 @@ class N3Parser {
|
|
|
219
246
|
}
|
|
220
247
|
case 'GRAPH':
|
|
221
248
|
if (this._supportsNamedGraphs) return this._readNamedGraphLabel;
|
|
222
|
-
// Otherwise, the next token must be a subject
|
|
249
|
+
// Otherwise, the next token must be a subject, unless it is an additional directive
|
|
223
250
|
default:
|
|
251
|
+
if (this._directives !== null && token.type in this._directives && this._graph === null) {
|
|
252
|
+
this._sparqlStyle = token.type[0] !== '@';
|
|
253
|
+
this._directiveCallback(this._directives[token.type]);
|
|
254
|
+
return this._readDeclarationPunctuation;
|
|
255
|
+
}
|
|
224
256
|
return this._readSubject(token);
|
|
225
257
|
}
|
|
226
258
|
}
|
|
@@ -339,13 +371,9 @@ class N3Parser {
|
|
|
339
371
|
}
|
|
340
372
|
case '<<(':
|
|
341
373
|
if (!this._n3Mode) return this._error('Disallowed triple term as subject', token);
|
|
342
|
-
this.
|
|
343
|
-
this._graph = null;
|
|
344
|
-
return this._readSubject;
|
|
374
|
+
return this._enterTripleTerm('<<(', null, null, token) && this._readSubject;
|
|
345
375
|
case '<<':
|
|
346
|
-
this.
|
|
347
|
-
this._graph = null;
|
|
348
|
-
return this._readSubject;
|
|
376
|
+
return this._enterTripleTerm('<<', null, null, token) && this._readSubject;
|
|
349
377
|
default:
|
|
350
378
|
// Read the subject entity
|
|
351
379
|
if ((this._subject = this._readEntity(token)) === undefined) return;
|
|
@@ -468,13 +496,9 @@ class N3Parser {
|
|
|
468
496
|
this._saveContext('formula', this._graph, this._subject, this._predicate, this._graph = this._factory.blankNode());
|
|
469
497
|
return this._readInFormulaContext;
|
|
470
498
|
case '<<(':
|
|
471
|
-
this.
|
|
472
|
-
this._graph = null;
|
|
473
|
-
return this._readSubject;
|
|
499
|
+
return this._enterTripleTerm('<<(', this._subject, this._predicate, token) && this._readSubject;
|
|
474
500
|
case '<<':
|
|
475
|
-
this.
|
|
476
|
-
this._graph = null;
|
|
477
|
-
return this._readSubject;
|
|
501
|
+
return this._enterTripleTerm('<<', this._subject, this._predicate, token) && this._readSubject;
|
|
478
502
|
default:
|
|
479
503
|
// Read the object entity
|
|
480
504
|
if ((this._object = this._readEntity(token)) === undefined) return;
|
|
@@ -669,13 +693,8 @@ class N3Parser {
|
|
|
669
693
|
this._subject = null;
|
|
670
694
|
return this._readInFormulaContext;
|
|
671
695
|
case '<<(':
|
|
672
|
-
this._saveContext('<<(', this._graph, null, null, null);
|
|
673
|
-
this._graph = null;
|
|
674
|
-
next = this._readSubject;
|
|
675
|
-
break;
|
|
676
696
|
case '<<':
|
|
677
|
-
this.
|
|
678
|
-
this._graph = null;
|
|
697
|
+
if (!this._enterTripleTerm(token.type, null, null, token)) return;
|
|
679
698
|
next = this._readSubject;
|
|
680
699
|
break;
|
|
681
700
|
default:
|
|
@@ -1167,6 +1186,7 @@ class N3Parser {
|
|
|
1167
1186
|
// Read the quad and restore the previous context
|
|
1168
1187
|
const quad = this._createQuad(this._subject, this._predicate, this._object, this._graph, this._inversePredicate);
|
|
1169
1188
|
this._restoreContext('<<(', token);
|
|
1189
|
+
this._tripleTermDepth--;
|
|
1170
1190
|
|
|
1171
1191
|
// If we're in a list, continue processing that list
|
|
1172
1192
|
const stack = this._contextStack,
|
|
@@ -1202,6 +1222,7 @@ class N3Parser {
|
|
|
1202
1222
|
this._tripleTerm = null;
|
|
1203
1223
|
const reifier = this._readTripleTerm();
|
|
1204
1224
|
this._restoreContext('<<', token);
|
|
1225
|
+
this._tripleTermDepth--;
|
|
1205
1226
|
|
|
1206
1227
|
// // If we're in a list, continue processing that list
|
|
1207
1228
|
const stack = this._contextStack,
|
|
@@ -1426,8 +1447,11 @@ class N3Parser {
|
|
|
1426
1447
|
}
|
|
1427
1448
|
// Resolve root-relative IRIs at the root of the base IRI
|
|
1428
1449
|
case '/':
|
|
1429
|
-
// Resolve scheme-relative IRIs to the scheme
|
|
1430
|
-
|
|
1450
|
+
// Resolve scheme-relative IRIs to the scheme,
|
|
1451
|
+
// keeping the reference's authority intact (RFC 3986 §5.2.4)
|
|
1452
|
+
if (iri[1] !== '/') return this._baseRoot + this._removeDotSegments(iri);
|
|
1453
|
+
// Without a base scheme, a leading ':' marks where the authority starts
|
|
1454
|
+
return this._baseScheme ? this._removeDotSegments(this._baseScheme + iri) : this._removeDotSegments(`:${iri}`).substr(1);
|
|
1431
1455
|
// Resolve all other IRIs at the base IRI's path
|
|
1432
1456
|
default:
|
|
1433
1457
|
// Relative IRIs cannot contain a colon in the first path segment
|
|
@@ -1454,8 +1478,8 @@ class N3Parser {
|
|
|
1454
1478
|
if (pathStart < 0) {
|
|
1455
1479
|
// Skip two slashes before the authority
|
|
1456
1480
|
if (iri[++i] === '/' && iri[++i] === '/')
|
|
1457
|
-
// Skip to
|
|
1458
|
-
while ((pathStart = i + 1) < length && iri[pathStart] !== '/') i = pathStart;
|
|
1481
|
+
// Skip to the end of the authority
|
|
1482
|
+
while ((pathStart = i + 1) < length && iri[pathStart] !== '/' && iri[pathStart] !== '?' && iri[pathStart] !== '#') i = pathStart;
|
|
1459
1483
|
}
|
|
1460
1484
|
break;
|
|
1461
1485
|
// Don't modify a query string or fragment
|
|
@@ -1503,12 +1527,13 @@ class N3Parser {
|
|
|
1503
1527
|
parse(input, quadCallback, prefixCallback, versionCallback) {
|
|
1504
1528
|
// The second parameter also accepts an object of named callbacks.
|
|
1505
1529
|
// As a second and third parameter it still accepts a separate quadCallback and prefixCallback for backward compatibility as well
|
|
1506
|
-
let onQuad, onPrefix, onComment, onVersion, onToken, onTokenEnd;
|
|
1507
|
-
if (quadCallback && (quadCallback.onQuad || quadCallback.onPrefix || quadCallback.onComment || quadCallback.onVersion || quadCallback.onToken || quadCallback.onTokenEnd)) {
|
|
1530
|
+
let onQuad, onPrefix, onComment, onVersion, onDirective, onToken, onTokenEnd;
|
|
1531
|
+
if (quadCallback && (quadCallback.onQuad || quadCallback.onPrefix || quadCallback.onComment || quadCallback.onVersion || quadCallback.onDirective || quadCallback.onToken || quadCallback.onTokenEnd)) {
|
|
1508
1532
|
onQuad = quadCallback.onQuad;
|
|
1509
1533
|
onPrefix = quadCallback.onPrefix;
|
|
1510
1534
|
onComment = quadCallback.onComment;
|
|
1511
1535
|
onVersion = quadCallback.onVersion;
|
|
1536
|
+
onDirective = quadCallback.onDirective;
|
|
1512
1537
|
onToken = quadCallback.onToken;
|
|
1513
1538
|
onTokenEnd = quadCallback.onTokenEnd;
|
|
1514
1539
|
} else {
|
|
@@ -1526,6 +1551,7 @@ class N3Parser {
|
|
|
1526
1551
|
if (this._n3Mode && this._implicitEmptyPrefix && this._base) this._prefixes[''] = this._resolveIRI('#');
|
|
1527
1552
|
this._prefixCallback = onPrefix || noop;
|
|
1528
1553
|
this._versionCallback = onVersion || noop;
|
|
1554
|
+
this._directiveCallback = onDirective || noop;
|
|
1529
1555
|
this._inversePredicate = false;
|
|
1530
1556
|
this._expectOf = false;
|
|
1531
1557
|
this._quantified = Object.create(null);
|
|
@@ -1533,6 +1559,7 @@ class N3Parser {
|
|
|
1533
1559
|
this._prefixChanges = null;
|
|
1534
1560
|
this._quantifiedChanges = null;
|
|
1535
1561
|
this._emptyFormula = false;
|
|
1562
|
+
this._tripleTermDepth = 0;
|
|
1536
1563
|
let readToken = token => {
|
|
1537
1564
|
return this._readCallback = this._readCallback(token);
|
|
1538
1565
|
};
|
package/lib/N3Reasoner.js
CHANGED
|
@@ -154,7 +154,7 @@ class N3Reasoner {
|
|
|
154
154
|
premise,
|
|
155
155
|
conclusion
|
|
156
156
|
}) {
|
|
157
|
-
const varMapping =
|
|
157
|
+
const varMapping = Object.create(null);
|
|
158
158
|
const toId = value => value.termType === 'Variable' ?
|
|
159
159
|
// If the term is a variable, then create an empty object that values can be placed into
|
|
160
160
|
varMapping[value.value] = varMapping[value.value] || {} :
|
package/lib/N3Store.js
CHANGED
|
@@ -116,6 +116,31 @@ function difference(s1, s2, depth = 4) {
|
|
|
116
116
|
return target;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
// Returns the key of a term in the entity index.
|
|
120
|
+
// Keys mark the term type by their first character, so the IRI of a named node
|
|
121
|
+
// that starts with such a marker (as relative IRIs can) is wrapped in < and >.
|
|
122
|
+
const markedIRI = /^[?_"[.<]/;
|
|
123
|
+
function entityKey(term) {
|
|
124
|
+
// Strings are term ids, which only need wrapping when they are IRIs starting with <
|
|
125
|
+
if (typeof term === 'string') return term.charCodeAt(0) !== 0x3C || term === '<>' ? term : `<${term}>`;
|
|
126
|
+
// IDs of IRIs usually start with a lowercase scheme letter, which never marks a term type
|
|
127
|
+
const id = (0, _N3DataFactory.termToId)(term);
|
|
128
|
+
if (id.charCodeAt(0) >= 0x61 || !term) return id;
|
|
129
|
+
// Keys of quoted triples are built from the keys of their components
|
|
130
|
+
if (term.termType === 'Quad') return JSON.stringify(quadKeyParts(term));
|
|
131
|
+
return term.termType !== 'NamedNode' || !markedIRI.test(term.value) ? id : `<${term.value}>`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Returns the keys of the components of a quad, nested like the parts of its internal id
|
|
135
|
+
function quadKeyParts(quad) {
|
|
136
|
+
const parts = [nestedKey(quad.subject), nestedKey(quad.predicate), nestedKey(quad.object)];
|
|
137
|
+
if (quad.graph && !(0, _N3Util.isDefaultGraph)(quad.graph)) parts.push(nestedKey(quad.graph));
|
|
138
|
+
return parts;
|
|
139
|
+
}
|
|
140
|
+
function nestedKey(term) {
|
|
141
|
+
return term.termType === 'Quad' ? quadKeyParts(term) : entityKey(term);
|
|
142
|
+
}
|
|
143
|
+
|
|
119
144
|
// ## Constructor
|
|
120
145
|
class N3EntityIndex {
|
|
121
146
|
constructor(options = {}) {
|
|
@@ -132,6 +157,9 @@ class N3EntityIndex {
|
|
|
132
157
|
this._factory = options.factory || _N3DataFactory.default;
|
|
133
158
|
}
|
|
134
159
|
_termFromId(id) {
|
|
160
|
+
// A key in < and > is a named node
|
|
161
|
+
if (id[0] === '<') return this._factory.namedNode(id.substring(1, id.length - 1));
|
|
162
|
+
// A key starting with . is a quoted triple
|
|
135
163
|
if (id[0] === '.') {
|
|
136
164
|
const entities = this._entities;
|
|
137
165
|
const terms = id.split('.');
|
|
@@ -148,12 +176,25 @@ class N3EntityIndex {
|
|
|
148
176
|
let g;
|
|
149
177
|
return s && p && o && ((0, _N3Util.isDefaultGraph)(term.graph) || (g = this._termToNumericId(term.graph))) && this._ids[g ? `.${s}.${p}.${o}.${g}` : `.${s}.${p}.${o}`];
|
|
150
178
|
}
|
|
151
|
-
return this.
|
|
179
|
+
return typeof term === 'string' ? this._stringToNumericId(term) : this._ids[entityKey(term)];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Returns the numeric id of a term given as a string id
|
|
183
|
+
_stringToNumericId(term) {
|
|
184
|
+
// The string is the key unless it is an IRI starting with <. Read the first character
|
|
185
|
+
// from the stored key after a match, which is cheaper than from a concatenated string.
|
|
186
|
+
const id = this._ids[term];
|
|
187
|
+
if (id ? this._entities[id].charCodeAt(0) !== 0x3C : term.charCodeAt(0) !== 0x3C) return id;
|
|
188
|
+
return term === '<>' ? id : this._ids[`<${term}>`];
|
|
152
189
|
}
|
|
153
190
|
_termToNewNumericId(term) {
|
|
191
|
+
if (typeof term === 'string') return this._stringToNumericId(term) || this._addEntity(entityKey(term));
|
|
154
192
|
// This assumes that no graph term is present - we may wish to error if there is one
|
|
155
|
-
const str = term && term.termType === 'Quad' ? `.${this._termToNewNumericId(term.subject)}.${this._termToNewNumericId(term.predicate)}.${this._termToNewNumericId(term.object)}${(0, _N3Util.isDefaultGraph)(term.graph) ? '' : `.${this._termToNewNumericId(term.graph)}`}` : (
|
|
156
|
-
return this._ids[str] ||
|
|
193
|
+
const str = term && term.termType === 'Quad' ? `.${this._termToNewNumericId(term.subject)}.${this._termToNewNumericId(term.predicate)}.${this._termToNewNumericId(term.object)}${(0, _N3Util.isDefaultGraph)(term.graph) ? '' : `.${this._termToNewNumericId(term.graph)}`}` : entityKey(term);
|
|
194
|
+
return this._ids[str] || this._addEntity(str);
|
|
195
|
+
}
|
|
196
|
+
_addEntity(key) {
|
|
197
|
+
return this._ids[this._entities[++this._id] = key] = this._id;
|
|
157
198
|
}
|
|
158
199
|
createBlankNode(suggestedName) {
|
|
159
200
|
let name, index;
|
|
@@ -1305,7 +1346,7 @@ function intersectMatchPatterns(left, right) {
|
|
|
1305
1346
|
for (let i = 0; i < 4; i++) {
|
|
1306
1347
|
const leftTerm = left[i],
|
|
1307
1348
|
rightTerm = right[i];
|
|
1308
|
-
if (leftTerm === null || leftTerm === undefined) result[i] = rightTerm;else if (rightTerm === null || rightTerm === undefined || (
|
|
1349
|
+
if (leftTerm === null || leftTerm === undefined) result[i] = rightTerm;else if (rightTerm === null || rightTerm === undefined || entityKey(leftTerm) === entityKey(rightTerm)) result[i] = leftTerm;else return false;
|
|
1309
1350
|
}
|
|
1310
1351
|
return result;
|
|
1311
1352
|
}
|
|
@@ -1368,7 +1409,7 @@ class DatasetCoreAndReadableStream extends _readableStream.Readable {
|
|
|
1368
1409
|
object,
|
|
1369
1410
|
graph
|
|
1370
1411
|
} = this;
|
|
1371
|
-
return !this._matchesNothing && (subject === null || subject === undefined || (
|
|
1412
|
+
return !this._matchesNothing && (subject === null || subject === undefined || entityKey(subject) === entityKey(quad.subject)) && (predicate === null || predicate === undefined || entityKey(predicate) === entityKey(quad.predicate)) && (object === null || object === undefined || entityKey(object) === entityKey(quad.object)) && (graph === null || graph === undefined || entityKey(graph) === entityKey(quad.graph));
|
|
1372
1413
|
}
|
|
1373
1414
|
|
|
1374
1415
|
// ### `_assertMatchesPattern` rejects a Quad outside this view.
|
package/lib/N3StreamParser.js
CHANGED
|
@@ -28,6 +28,10 @@ class N3StreamParser extends _readableStream.Transform {
|
|
|
28
28
|
// Emit prefixes through the `prefix` event
|
|
29
29
|
onPrefix: (prefix, uri) => {
|
|
30
30
|
this.emit('prefix', prefix, uri);
|
|
31
|
+
},
|
|
32
|
+
// Emit additional directives through the `directive` event
|
|
33
|
+
onDirective: name => {
|
|
34
|
+
this.emit('directive', name);
|
|
31
35
|
}
|
|
32
36
|
};
|
|
33
37
|
if (options && options.comments) callbacks.onComment = comment => {
|
package/lib/N3Writer.js
CHANGED
|
@@ -18,10 +18,19 @@ const {
|
|
|
18
18
|
rdf,
|
|
19
19
|
xsd
|
|
20
20
|
} = _IRIs.default;
|
|
21
|
+
const {
|
|
22
|
+
hasOwnProperty
|
|
23
|
+
} = Object.prototype;
|
|
21
24
|
|
|
22
|
-
// Characters in
|
|
25
|
+
// Characters that require escaping in Turtle, TriG, and N3,
|
|
26
|
+
// where characters outside the Basic Multilingual Plane are escaped as well
|
|
23
27
|
const escape = /["\\\t\n\r\b\f\u0000-\u0019\ud800-\udbff]/,
|
|
24
28
|
escapeAll = /["\\\t\n\r\b\f\u0000-\u0019]|[\ud800-\udbff][\udc00-\udfff]/g,
|
|
29
|
+
// Characters that require escaping in canonical N-Triples and N-Quads:
|
|
30
|
+
// U+0000–U+001F, `"`, `\`, U+007F, U+FFFE, and U+FFFF.
|
|
31
|
+
// A negated class is as fast to test as a shorter class listing characters.
|
|
32
|
+
canonicalEscape = /[^ !#-\[\]-~\u0080-\uFFFD]/,
|
|
33
|
+
canonicalEscapeAll = /[^ !#-\[\]-~\u0080-\uFFFD]/g,
|
|
25
34
|
escapedCharacters = {
|
|
26
35
|
'\\': '\\\\',
|
|
27
36
|
'"': '\\"',
|
|
@@ -75,8 +84,11 @@ class N3Writer {
|
|
|
75
84
|
this._subject = null;
|
|
76
85
|
if (!/triple|quad/i.test(options.format)) {
|
|
77
86
|
this._lineMode = false;
|
|
87
|
+
this._escape = escape, this._escapeAll = escapeAll, this._characterReplacer = characterReplacer;
|
|
78
88
|
this._graph = DEFAULTGRAPH;
|
|
79
89
|
this._prefixIRIs = Object.create(null);
|
|
90
|
+
// Escaped prefix IRIs and names for the prefix matcher, computed once per prefix
|
|
91
|
+
this._prefixPatterns = Object.create(null);
|
|
80
92
|
if (options.baseIRI) {
|
|
81
93
|
this._baseIri = new _BaseIRI.default(options.baseIRI);
|
|
82
94
|
if (options.writeBase !== false) this._write(`@base <${options.baseIRI}>.\n`);
|
|
@@ -85,6 +97,9 @@ class N3Writer {
|
|
|
85
97
|
} else {
|
|
86
98
|
this._lineMode = true;
|
|
87
99
|
this._writeQuad = this._writeQuadLine;
|
|
100
|
+
// N-Triples and N-Quads are written in their canonical form
|
|
101
|
+
this._escape = canonicalEscape, this._escapeAll = canonicalEscapeAll;
|
|
102
|
+
this._characterReplacer = canonicalCharacterReplacer;
|
|
88
103
|
}
|
|
89
104
|
}
|
|
90
105
|
|
|
@@ -163,7 +178,7 @@ class N3Writer {
|
|
|
163
178
|
// A blank node or list is represented as-is
|
|
164
179
|
if (entity.termType !== 'NamedNode') {
|
|
165
180
|
// If it is a list head, pretty-print it
|
|
166
|
-
if (this._lists &&
|
|
181
|
+
if (this._lists && hasOwnProperty.call(this._lists, entity.value)) entity = this.list(this._lists[entity.value]);
|
|
167
182
|
// Terms from this library already hold their serialization as id
|
|
168
183
|
if (entity instanceof _N3DataFactory.Term) return entity.id;
|
|
169
184
|
return entity.termType === 'Variable' ? `?${entity.value}` : `_:${entity.value}`;
|
|
@@ -174,17 +189,17 @@ class N3Writer {
|
|
|
174
189
|
iri = this._baseIri.toRelative(iri);
|
|
175
190
|
}
|
|
176
191
|
// Escape special characters
|
|
177
|
-
if (
|
|
192
|
+
if (this._escape.test(iri)) iri = iri.replace(this._escapeAll, this._characterReplacer);
|
|
178
193
|
// Try to represent the IRI as prefixed name, unless no prefixes were added
|
|
179
|
-
const prefixMatch = this._hasPrefixes ? this._prefixRegex.exec(iri) : null;
|
|
180
|
-
return !prefixMatch ? `<${iri}>` :
|
|
194
|
+
const prefixMatch = this._hasPrefixes ? (this._prefixRegex || this._createPrefixRegex()).exec(iri) : null;
|
|
195
|
+
return !prefixMatch ? `<${iri}>` : this._prefixIRIs[prefixMatch[1]] + prefixMatch[2];
|
|
181
196
|
}
|
|
182
197
|
|
|
183
198
|
// ### `_encodeLiteral` represents a literal
|
|
184
199
|
_encodeLiteral(literal) {
|
|
185
200
|
// Escape special characters
|
|
186
201
|
let value = literal.value;
|
|
187
|
-
if (
|
|
202
|
+
if (this._escape.test(value)) value = value.replace(this._escapeAll, this._characterReplacer);
|
|
188
203
|
|
|
189
204
|
// Write a language-tagged literal
|
|
190
205
|
const language = literal.language;
|
|
@@ -254,7 +269,9 @@ class N3Writer {
|
|
|
254
269
|
object,
|
|
255
270
|
graph
|
|
256
271
|
}) {
|
|
257
|
-
|
|
272
|
+
// Canonical N-Triples and N-Quads put spaces inside the delimiters
|
|
273
|
+
const space = this._lineMode ? ' ' : '';
|
|
274
|
+
return `<<(${space}${this._encodeSubject(subject)} ${this._encodePredicate(predicate)} ${this._encodeObject(object)}${(0, _N3Util.isDefaultGraph)(graph) ? '' : ` ${this._encodeIriOrBlank(graph)}`}${space})>>`;
|
|
258
275
|
}
|
|
259
276
|
|
|
260
277
|
// ### `_blockedWrite` replaces `_write` after the writer has been closed
|
|
@@ -302,24 +319,26 @@ class N3Writer {
|
|
|
302
319
|
}
|
|
303
320
|
// Store and write the prefix
|
|
304
321
|
this._prefixIRIs[iri] = prefix += ':';
|
|
322
|
+
this._prefixPatterns[iri] = (0, _Util.escapeRegex)(iri);
|
|
305
323
|
this._write(`@prefix ${prefix} <${iri}>.\n`);
|
|
306
324
|
}
|
|
307
|
-
// Recreate the prefix matcher
|
|
325
|
+
// Recreate the prefix matcher when it is next needed, so that adding
|
|
326
|
+
// prefixes one by one does not rebuild it for every prefix
|
|
308
327
|
if (hasPrefixes) {
|
|
309
328
|
this._hasPrefixes = true;
|
|
310
|
-
|
|
311
|
-
prefixList = '';
|
|
312
|
-
for (const prefixIRI in this._prefixIRIs) {
|
|
313
|
-
IRIlist += IRIlist ? `|${prefixIRI}` : prefixIRI;
|
|
314
|
-
prefixList += (prefixList ? '|' : '') + this._prefixIRIs[prefixIRI];
|
|
315
|
-
}
|
|
316
|
-
IRIlist = (0, _Util.escapeRegex)(IRIlist, /[\]\/\(\)\*\+\?\.\\\$]/g, '\\$&');
|
|
317
|
-
this._prefixRegex = new RegExp(`^(?:${prefixList})[^\/]*$|` + `^(${IRIlist})([_a-zA-Z0-9](?:\\.?[\\-_a-zA-Z0-9])*)$`);
|
|
329
|
+
this._prefixRegex = null;
|
|
318
330
|
}
|
|
319
331
|
// End a prefix block with a newline
|
|
320
332
|
this._write(hasPrefixes ? '\n' : '', done);
|
|
321
333
|
}
|
|
322
334
|
|
|
335
|
+
// ### `_createPrefixRegex` creates the matcher for the current prefixes
|
|
336
|
+
_createPrefixRegex() {
|
|
337
|
+
let IRIlist = '';
|
|
338
|
+
for (const prefixIRI in this._prefixPatterns) IRIlist += IRIlist ? `|${this._prefixPatterns[prefixIRI]}` : this._prefixPatterns[prefixIRI];
|
|
339
|
+
return this._prefixRegex = new RegExp(`^(${IRIlist})([_a-zA-Z0-9](?:\\.?[\\-_a-zA-Z0-9])*)$`);
|
|
340
|
+
}
|
|
341
|
+
|
|
323
342
|
// ### `blank` creates a blank node with the given content
|
|
324
343
|
blank(predicate, object) {
|
|
325
344
|
let children = predicate,
|
|
@@ -409,4 +428,15 @@ function characterReplacer(character) {
|
|
|
409
428
|
}
|
|
410
429
|
}
|
|
411
430
|
return result;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Replaces a character by its canonical N-Triples escape:
|
|
434
|
+
// a short escape where one exists, and an upper-case `\uXXXX` escape otherwise
|
|
435
|
+
function canonicalCharacterReplacer(character) {
|
|
436
|
+
let result = escapedCharacters[character];
|
|
437
|
+
if (result === undefined) {
|
|
438
|
+
result = character.charCodeAt(0).toString(16).toUpperCase();
|
|
439
|
+
result = '\\u0000'.substr(0, 6 - result.length) + result;
|
|
440
|
+
}
|
|
441
|
+
return result;
|
|
412
442
|
}
|
package/lib/Util.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n3",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.5",
|
|
4
4
|
"description": "Lightning fast, asynchronous, streaming Turtle / N3 / RDF library.",
|
|
5
5
|
"author": "Ruben Verborgh <ruben.verborgh@gmail.com>",
|
|
6
6
|
"keywords": [
|
|
@@ -60,44 +60,17 @@
|
|
|
60
60
|
"test": "jest",
|
|
61
61
|
"lint": "eslint src perf test spec",
|
|
62
62
|
"prepare": "npm run build",
|
|
63
|
-
"spec": "
|
|
64
|
-
"spec-1-1": "npm run spec-1-1-turtle && npm run spec-1-1-ntriples && npm run spec-1-1-nquads && npm run spec-1-1-trig",
|
|
63
|
+
"spec": "node spec/run.js",
|
|
65
64
|
"spec-1-1-earl": "npm run spec-1-1-earl-turtle && npm run spec-1-1-earl-ntriples && npm run spec-1-1-earl-nquads && npm run spec-1-1-earl-trig",
|
|
66
|
-
"spec-1-1-ntriples": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-n-triples/manifest.ttl -i '{ \"format\": \"n-triples\" }' -c .rdf-test-suite-cache/",
|
|
67
|
-
"spec-1-1-nquads": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-n-quads/manifest.ttl -i '{ \"format\": \"n-quads\" }' -c .rdf-test-suite-cache/",
|
|
68
|
-
"spec-1-1-turtle": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-turtle/manifest.ttl -i '{ \"format\": \"turtle\" }' -c .rdf-test-suite-cache/ --skip \"(bad-numeric-escape|bareword_decimal)\"",
|
|
69
|
-
"spec-1-1-trig": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-trig/manifest.ttl -i '{ \"format\": \"trig\" }' -c .rdf-test-suite-cache/ --skip \"(bad-numeric-escape|IRI-resolution-01)\"",
|
|
70
65
|
"spec-1-1-earl-ntriples": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-n-triples/manifest.ttl -i '{ \"format\": \"n-triples\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-ntriples.ttl",
|
|
71
66
|
"spec-1-1-earl-nquads": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-n-quads/manifest.ttl -i '{ \"format\": \"n-quads\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-nquads.ttl",
|
|
72
67
|
"spec-1-1-earl-turtle": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-turtle/manifest.ttl -i '{ \"format\": \"turtle\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-turtle.ttl",
|
|
73
68
|
"spec-1-1-earl-trig": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-trig/manifest.ttl -i '{ \"format\": \"trig\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-trig.ttl",
|
|
74
|
-
"spec-1-2": "npm run spec-1-2-turtle && npm run spec-1-2-ntriples && npm run spec-1-2-nquads && npm run spec-1-2-trig && npm run spec-1-2-turtle-eval && npm run spec-1-2-trig-eval",
|
|
75
69
|
"spec-1-2-earl": "npm run spec-1-2-earl-turtle && npm run spec-1-2-earl-ntriples && npm run spec-1-2-earl-nquads && npm run spec-1-2-earl-trig",
|
|
76
|
-
"spec-1-2-ntriples": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-n-triples/syntax/manifest.ttl -i '{ \"format\": \"n-triples\" }' -c .rdf-test-suite-cache/",
|
|
77
|
-
"spec-1-2-nquads": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-n-quads/syntax/manifest.ttl -i '{ \"format\": \"n-quads\" }' -c .rdf-test-suite-cache/",
|
|
78
|
-
"spec-1-2-turtle": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-turtle/syntax/manifest.ttl -i '{ \"format\": \"turtle\" }' -c .rdf-test-suite-cache/",
|
|
79
|
-
"spec-1-2-trig": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-trig/syntax/manifest.ttl -i '{ \"format\": \"trig\" }' -c .rdf-test-suite-cache/",
|
|
80
|
-
"spec-1-2-turtle-eval": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-turtle/eval/manifest.ttl -i '{ \"format\": \"turtle\" }' -c .rdf-test-suite-cache/",
|
|
81
|
-
"spec-1-2-trig-eval": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-trig/eval/manifest.ttl -i '{ \"format\": \"trig\" }' -c .rdf-test-suite-cache/",
|
|
82
70
|
"spec-1-2-earl-ntriples": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-n-triples/syntax/manifest.ttl -i '{ \"format\": \"n-triples\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-ntriples.ttl",
|
|
83
71
|
"spec-1-2-earl-nquads": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-n-quads/syntax/manifest.ttl -i '{ \"format\": \"n-quads\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-nquads.ttl",
|
|
84
72
|
"spec-1-2-earl-turtle": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-turtle/syntax/manifest.ttl -i '{ \"format\": \"turtle\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-turtle.ttl",
|
|
85
73
|
"spec-1-2-earl-trig": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-trig/syntax/manifest.ttl -i '{ \"format\": \"trig\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-trig.ttl",
|
|
86
|
-
"spec-writer": "npm run spec-writer-1-1-turtle && npm run spec-writer-1-1-ntriples && npm run spec-writer-1-1-nquads && npm run spec-writer-1-1-trig && npm run spec-writer-1-2-turtle && npm run spec-writer-1-2-ntriples && npm run spec-writer-1-2-nquads && npm run spec-writer-1-2-trig && npm run spec-writer-1-2-turtle-eval && npm run spec-writer-1-2-trig-eval",
|
|
87
|
-
"spec-writer-1-1-turtle": "rdf-test-suite spec/writer.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-turtle/manifest.ttl -i '{ \"format\": \"turtle\" }' -c .rdf-test-suite-cache/ --skip \"(bad-numeric-escape|bareword_decimal)\"",
|
|
88
|
-
"spec-writer-1-1-ntriples": "rdf-test-suite spec/writer.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-n-triples/manifest.ttl -i '{ \"format\": \"n-triples\" }' -c .rdf-test-suite-cache/",
|
|
89
|
-
"spec-writer-1-1-nquads": "rdf-test-suite spec/writer.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-n-quads/manifest.ttl -i '{ \"format\": \"n-quads\" }' -c .rdf-test-suite-cache/",
|
|
90
|
-
"spec-writer-1-1-trig": "rdf-test-suite spec/writer.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-trig/manifest.ttl -i '{ \"format\": \"trig\" }' -c .rdf-test-suite-cache/ --skip \"(bad-numeric-escape|IRI-resolution-01)\"",
|
|
91
|
-
"spec-writer-1-2-turtle": "rdf-test-suite spec/writer.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-turtle/syntax/manifest.ttl -i '{ \"format\": \"turtle\" }' -c .rdf-test-suite-cache/",
|
|
92
|
-
"spec-writer-1-2-ntriples": "rdf-test-suite spec/writer.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-n-triples/syntax/manifest.ttl -i '{ \"format\": \"n-triples\" }' -c .rdf-test-suite-cache/",
|
|
93
|
-
"spec-writer-1-2-nquads": "rdf-test-suite spec/writer.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-n-quads/syntax/manifest.ttl -i '{ \"format\": \"n-quads\" }' -c .rdf-test-suite-cache/",
|
|
94
|
-
"spec-writer-1-2-trig": "rdf-test-suite spec/writer.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-trig/syntax/manifest.ttl -i '{ \"format\": \"trig\" }' -c .rdf-test-suite-cache/",
|
|
95
|
-
"spec-writer-1-2-turtle-eval": "rdf-test-suite spec/writer.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-turtle/eval/manifest.ttl -i '{ \"format\": \"turtle\" }' -c .rdf-test-suite-cache/",
|
|
96
|
-
"spec-writer-1-2-trig-eval": "rdf-test-suite spec/writer.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-trig/eval/manifest.ttl -i '{ \"format\": \"trig\" }' -c .rdf-test-suite-cache/",
|
|
97
|
-
"spec-n3": "npm run spec-n3-parser && npm run spec-n3-extended",
|
|
98
|
-
"spec-n3-fetch": "sh scripts/fetch-n3-tests.sh",
|
|
99
|
-
"spec-n3-parser": "npm run spec-n3-fetch && rdf-test-suite spec/parser.js https://w3c-cg.github.io/N3/tests/N3Tests/manifest-parser.ttl -i '{ \"format\": \"text/n3\" }' -m 'https://w3c-cg.github.io/N3/tests/~.n3-tests/tests/'",
|
|
100
|
-
"spec-n3-extended": "npm run spec-n3-fetch && rdf-test-suite spec/parser.js https://w3c-cg.github.io/N3/tests/N3Tests/manifest-extended.ttl -i '{ \"format\": \"text/n3\" }' -m 'https://w3c-cg.github.io/N3/tests/~.n3-tests/tests/'",
|
|
101
74
|
"spec-clean": "rm -rf .rdf-test-suite-cache/ .n3-tests/",
|
|
102
75
|
"docs": "cd src && docco *.js -o ../docs && cd .."
|
|
103
76
|
},
|