n3 2.12.2 → 2.13.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 +24 -0
- package/browser/n3.esm.min.js +12 -13
- package/browser/n3.min.js +12 -13
- package/lib/N3Lexer.js +2 -0
- package/lib/N3Parser.js +14 -4
- package/lib/N3Writer.js +20 -8
- package/package.json +1 -1
- package/src/N3Lexer.js +2 -0
- package/src/N3Parser.js +18 -4
- package/src/N3Writer.js +23 -8
package/lib/N3Lexer.js
CHANGED
|
@@ -135,6 +135,8 @@ class N3Lexer {
|
|
|
135
135
|
for (const key in this) {
|
|
136
136
|
if (!(key in lineModeRegExps) && this[key] instanceof RegExp) this[key] = invalidRegExp;
|
|
137
137
|
}
|
|
138
|
+
// The only keyword in N-Triples and N-Quads is VERSION, which is case-sensitive
|
|
139
|
+
this._keyword = /VERSION(?=[\s#<])/y;
|
|
138
140
|
}
|
|
139
141
|
// When not in line mode, enable N3 functionality by default
|
|
140
142
|
else {
|
package/lib/N3Parser.js
CHANGED
|
@@ -11,6 +11,10 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
11
11
|
// **N3Parser** parses N3 documents.
|
|
12
12
|
|
|
13
13
|
let blankNodePrefix = 0;
|
|
14
|
+
// Detects `.` and `..` path segments in an IRI
|
|
15
|
+
const dotSegments = /(^|\/)\.\.?($|[/#?])/;
|
|
16
|
+
// Detects `.` and `..` segments in the path of an IRI, ignoring its query and fragment
|
|
17
|
+
const pathDotSegments = /^[^?#]*(?:^|\/)\.\.?(?:$|[/#?])/;
|
|
14
18
|
|
|
15
19
|
// ## Constructor
|
|
16
20
|
class N3Parser {
|
|
@@ -91,6 +95,7 @@ class N3Parser {
|
|
|
91
95
|
if (!baseIRI) {
|
|
92
96
|
this._base = '';
|
|
93
97
|
this._basePath = '';
|
|
98
|
+
this._basePathHasDotSegments = false;
|
|
94
99
|
} else {
|
|
95
100
|
// Remove fragment if present
|
|
96
101
|
const fragmentPos = baseIRI.indexOf('#');
|
|
@@ -107,6 +112,8 @@ class N3Parser {
|
|
|
107
112
|
// If the base has an authority but an empty path,
|
|
108
113
|
// relative IRIs merge under the path '/' (RFC 3986 §5.2.3)
|
|
109
114
|
if (baseIRI[2] !== undefined && (base.length === this._baseRoot.length || base[this._baseRoot.length] === '?')) this._basePath = `${this._baseRoot}/`;
|
|
115
|
+
// Check once whether resolving against the base path needs to remove dot segments
|
|
116
|
+
this._basePathHasDotSegments = dotSegments.test(this._basePath);
|
|
110
117
|
}
|
|
111
118
|
}
|
|
112
119
|
|
|
@@ -153,7 +160,7 @@ class N3Parser {
|
|
|
153
160
|
// Prefix and base declarations are scoped to their formula,
|
|
154
161
|
// so record prefix changes to undo them when the formula ends
|
|
155
162
|
if (type === 'formula') {
|
|
156
|
-
context.base = [this._base, this._basePath, this._baseRoot, this._baseScheme];
|
|
163
|
+
context.base = [this._base, this._basePath, this._baseRoot, this._baseScheme, this._basePathHasDotSegments];
|
|
157
164
|
this._prefixChanges = [];
|
|
158
165
|
}
|
|
159
166
|
this._contextStack.push(context);
|
|
@@ -193,7 +200,7 @@ class N3Parser {
|
|
|
193
200
|
if (this._n3Mode) {
|
|
194
201
|
this._inversePredicate = context.inverse;
|
|
195
202
|
this._expectOf = context.expectOf;
|
|
196
|
-
if (type === 'formula') [this._base, this._basePath, this._baseRoot, this._baseScheme] = context.base;
|
|
203
|
+
if (type === 'formula') [this._base, this._basePath, this._baseRoot, this._baseScheme, this._basePathHasDotSegments] = context.base;
|
|
197
204
|
if (this._prefixChanges !== context.prefixChanges) {
|
|
198
205
|
undoChanges(this._prefixes, this._prefixChanges);
|
|
199
206
|
this._prefixChanges = context.prefixChanges;
|
|
@@ -1455,14 +1462,17 @@ class N3Parser {
|
|
|
1455
1462
|
// Resolve all other IRIs at the base IRI's path
|
|
1456
1463
|
default:
|
|
1457
1464
|
// Relative IRIs cannot contain a colon in the first path segment
|
|
1458
|
-
|
|
1465
|
+
if (/^[^/:]*:/.test(iri)) return null;
|
|
1466
|
+
// Only scan the joined IRI for dot segments if either part can contain them,
|
|
1467
|
+
// as the base path can be long and the joined IRI would need to be copied
|
|
1468
|
+
return this._basePathHasDotSegments || pathDotSegments.test(iri) ? this._removeDotSegments(this._basePath + iri) : this._basePath + iri;
|
|
1459
1469
|
}
|
|
1460
1470
|
}
|
|
1461
1471
|
|
|
1462
1472
|
// ### `_removeDotSegments` resolves './' and '../' path segments in an IRI as per RFC3986
|
|
1463
1473
|
_removeDotSegments(iri) {
|
|
1464
1474
|
// Don't modify the IRI if it does not contain any dot segments
|
|
1465
|
-
if (
|
|
1475
|
+
if (!dotSegments.test(iri)) return iri;
|
|
1466
1476
|
|
|
1467
1477
|
// Start with an imaginary slash before the IRI in order to resolve trailing './' and '../'
|
|
1468
1478
|
const length = iri.length;
|
package/lib/N3Writer.js
CHANGED
|
@@ -41,6 +41,9 @@ const escape = /["\\\t\n\r\b\f\u0000-\u0019\ud800-\udbff]/,
|
|
|
41
41
|
'\f': '\\f'
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
+
// Characters that a version label cannot contain
|
|
45
|
+
const invalidVersionLabel = /["\\\u0000-\u001f\u007f]|[\ud800-\udbff](?![\udc00-\udfff])|(?:^|[^\ud800-\udbff])[\udc00-\udfff]/;
|
|
46
|
+
|
|
44
47
|
// A local name (PN_LOCAL) as written in a prefixed name, without backslash escapes,
|
|
45
48
|
// so that it denotes the same characters as the IRI it is taken from.
|
|
46
49
|
// Characters outside the Basic Multilingual Plane are escaped before matching, so they never occur.
|
|
@@ -88,12 +91,17 @@ class N3Writer {
|
|
|
88
91
|
this._endStream = options.end === undefined ? true : !!options.end;
|
|
89
92
|
}
|
|
90
93
|
|
|
94
|
+
// A version label is written as-is, so it cannot contain characters that need escaping,
|
|
95
|
+
// nor unpaired surrogates (which cannot be encoded)
|
|
96
|
+
if (options.version && invalidVersionLabel.test(options.version)) throw new Error(`Invalid version label: ${JSON.stringify(options.version)}`);
|
|
97
|
+
|
|
91
98
|
// Initialize writer, depending on the format
|
|
92
99
|
this._subject = null;
|
|
93
100
|
if (!/triple|quad/i.test(options.format)) {
|
|
94
101
|
this._lineMode = false;
|
|
95
102
|
this._escape = escape, this._escapeAll = escapeAll, this._characterReplacer = characterReplacer;
|
|
96
103
|
this._graph = DEFAULTGRAPH;
|
|
104
|
+
if (options.version) this._write(`@version "${options.version}".\n`);
|
|
97
105
|
this._prefixIRIs = Object.create(null);
|
|
98
106
|
// Escaped prefix IRIs and names for the prefix matcher, computed once per prefix
|
|
99
107
|
this._prefixPatterns = Object.create(null);
|
|
@@ -108,6 +116,7 @@ class N3Writer {
|
|
|
108
116
|
// N-Triples and N-Quads are written in their canonical form
|
|
109
117
|
this._escape = canonicalEscape, this._escapeAll = canonicalEscapeAll;
|
|
110
118
|
this._characterReplacer = canonicalCharacterReplacer;
|
|
119
|
+
if (options.version) this._write(`VERSION "${options.version}"\n`);
|
|
111
120
|
}
|
|
112
121
|
}
|
|
113
122
|
|
|
@@ -123,6 +132,15 @@ class N3Writer {
|
|
|
123
132
|
this._outputStream.write(string, 'utf8', callback);
|
|
124
133
|
}
|
|
125
134
|
|
|
135
|
+
// ### `_endStatement` finishes a pending statement and closes an open graph block
|
|
136
|
+
_endStatement() {
|
|
137
|
+
if (this._subject !== null) {
|
|
138
|
+
this._write(this._inDefaultGraph ? '.\n' : '\n}\n');
|
|
139
|
+
this._subject = null;
|
|
140
|
+
this._graph = DEFAULTGRAPH;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
126
144
|
// ### `_writeQuad` writes the quad to the output stream
|
|
127
145
|
_writeQuad(subject, predicate, object, graph, done) {
|
|
128
146
|
try {
|
|
@@ -325,10 +343,7 @@ class N3Writer {
|
|
|
325
343
|
if (typeof iri !== 'string') iri = iri.value;
|
|
326
344
|
hasPrefixes = true;
|
|
327
345
|
// Finish a possible pending quad
|
|
328
|
-
|
|
329
|
-
this._write(this._inDefaultGraph ? '.\n' : '\n}\n');
|
|
330
|
-
this._subject = null, this._graph = '';
|
|
331
|
-
}
|
|
346
|
+
this._endStatement();
|
|
332
347
|
// Store and write the prefix
|
|
333
348
|
this._prefixIRIs[iri] = prefix += ':';
|
|
334
349
|
this._prefixPatterns[iri] = [(0, _Util.escapeRegex)(iri), (0, _Util.escapeRegex)(prefix)];
|
|
@@ -407,10 +422,7 @@ class N3Writer {
|
|
|
407
422
|
// ### `end` signals the end of the output stream
|
|
408
423
|
end(done) {
|
|
409
424
|
// Finish a possible pending quad
|
|
410
|
-
|
|
411
|
-
this._write(this._inDefaultGraph ? '.\n' : '\n}\n');
|
|
412
|
-
this._subject = null;
|
|
413
|
-
}
|
|
425
|
+
this._endStatement();
|
|
414
426
|
// Disallow further writing
|
|
415
427
|
this._write = this._blockedWrite;
|
|
416
428
|
|
package/package.json
CHANGED
package/src/N3Lexer.js
CHANGED
|
@@ -101,6 +101,8 @@ export default class N3Lexer {
|
|
|
101
101
|
if (!(key in lineModeRegExps) && this[key] instanceof RegExp)
|
|
102
102
|
this[key] = invalidRegExp;
|
|
103
103
|
}
|
|
104
|
+
// The only keyword in N-Triples and N-Quads is VERSION, which is case-sensitive
|
|
105
|
+
this._keyword = /VERSION(?=[\s#<])/y;
|
|
104
106
|
}
|
|
105
107
|
// When not in line mode, enable N3 functionality by default
|
|
106
108
|
else {
|
package/src/N3Parser.js
CHANGED
|
@@ -4,6 +4,10 @@ import N3DataFactory from './N3DataFactory';
|
|
|
4
4
|
import namespaces from './IRIs';
|
|
5
5
|
|
|
6
6
|
let blankNodePrefix = 0;
|
|
7
|
+
// Detects `.` and `..` path segments in an IRI
|
|
8
|
+
const dotSegments = /(^|\/)\.\.?($|[/#?])/;
|
|
9
|
+
// Detects `.` and `..` segments in the path of an IRI, ignoring its query and fragment
|
|
10
|
+
const pathDotSegments = /^[^?#]*(?:^|\/)\.\.?(?:$|[/#?])/;
|
|
7
11
|
|
|
8
12
|
// ## Constructor
|
|
9
13
|
export default class N3Parser {
|
|
@@ -83,6 +87,7 @@ export default class N3Parser {
|
|
|
83
87
|
if (!baseIRI) {
|
|
84
88
|
this._base = '';
|
|
85
89
|
this._basePath = '';
|
|
90
|
+
this._basePathHasDotSegments = false;
|
|
86
91
|
}
|
|
87
92
|
else {
|
|
88
93
|
// Remove fragment if present
|
|
@@ -103,6 +108,8 @@ export default class N3Parser {
|
|
|
103
108
|
// relative IRIs merge under the path '/' (RFC 3986 §5.2.3)
|
|
104
109
|
if (baseIRI[2] !== undefined && (base.length === this._baseRoot.length || base[this._baseRoot.length] === '?'))
|
|
105
110
|
this._basePath = `${this._baseRoot}/`;
|
|
111
|
+
// Check once whether resolving against the base path needs to remove dot segments
|
|
112
|
+
this._basePathHasDotSegments = dotSegments.test(this._basePath);
|
|
106
113
|
}
|
|
107
114
|
}
|
|
108
115
|
|
|
@@ -140,7 +147,8 @@ export default class N3Parser {
|
|
|
140
147
|
// Prefix and base declarations are scoped to their formula,
|
|
141
148
|
// so record prefix changes to undo them when the formula ends
|
|
142
149
|
if (type === 'formula') {
|
|
143
|
-
context.base = [this._base, this._basePath, this._baseRoot, this._baseScheme
|
|
150
|
+
context.base = [this._base, this._basePath, this._baseRoot, this._baseScheme,
|
|
151
|
+
this._basePathHasDotSegments];
|
|
144
152
|
this._prefixChanges = [];
|
|
145
153
|
}
|
|
146
154
|
this._contextStack.push(context);
|
|
@@ -182,7 +190,8 @@ export default class N3Parser {
|
|
|
182
190
|
this._inversePredicate = context.inverse;
|
|
183
191
|
this._expectOf = context.expectOf;
|
|
184
192
|
if (type === 'formula')
|
|
185
|
-
[this._base, this._basePath, this._baseRoot, this._baseScheme
|
|
193
|
+
[this._base, this._basePath, this._baseRoot, this._baseScheme,
|
|
194
|
+
this._basePathHasDotSegments] = context.base;
|
|
186
195
|
if (this._prefixChanges !== context.prefixChanges) {
|
|
187
196
|
undoChanges(this._prefixes, this._prefixChanges);
|
|
188
197
|
this._prefixChanges = context.prefixChanges;
|
|
@@ -1576,14 +1585,19 @@ export default class N3Parser {
|
|
|
1576
1585
|
// Resolve all other IRIs at the base IRI's path
|
|
1577
1586
|
default:
|
|
1578
1587
|
// Relative IRIs cannot contain a colon in the first path segment
|
|
1579
|
-
|
|
1588
|
+
if (/^[^/:]*:/.test(iri))
|
|
1589
|
+
return null;
|
|
1590
|
+
// Only scan the joined IRI for dot segments if either part can contain them,
|
|
1591
|
+
// as the base path can be long and the joined IRI would need to be copied
|
|
1592
|
+
return this._basePathHasDotSegments || pathDotSegments.test(iri) ?
|
|
1593
|
+
this._removeDotSegments(this._basePath + iri) : this._basePath + iri;
|
|
1580
1594
|
}
|
|
1581
1595
|
}
|
|
1582
1596
|
|
|
1583
1597
|
// ### `_removeDotSegments` resolves './' and '../' path segments in an IRI as per RFC3986
|
|
1584
1598
|
_removeDotSegments(iri) {
|
|
1585
1599
|
// Don't modify the IRI if it does not contain any dot segments
|
|
1586
|
-
if (
|
|
1600
|
+
if (!dotSegments.test(iri))
|
|
1587
1601
|
return iri;
|
|
1588
1602
|
|
|
1589
1603
|
// Start with an imaginary slash before the IRI in order to resolve trailing './' and '../'
|
package/src/N3Writer.js
CHANGED
|
@@ -24,6 +24,9 @@ const escape = /["\\\t\n\r\b\f\u0000-\u0019\ud800-\udbff]/,
|
|
|
24
24
|
'\n': '\\n', '\r': '\\r', '\b': '\\b', '\f': '\\f',
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
+
// Characters that a version label cannot contain
|
|
28
|
+
const invalidVersionLabel = /["\\\u0000-\u001f\u007f]|[\ud800-\udbff](?![\udc00-\udfff])|(?:^|[^\ud800-\udbff])[\udc00-\udfff]/;
|
|
29
|
+
|
|
27
30
|
// A local name (PN_LOCAL) as written in a prefixed name, without backslash escapes,
|
|
28
31
|
// so that it denotes the same characters as the IRI it is taken from.
|
|
29
32
|
// Characters outside the Basic Multilingual Plane are escaped before matching, so they never occur.
|
|
@@ -70,12 +73,19 @@ export default class N3Writer {
|
|
|
70
73
|
this._endStream = options.end === undefined ? true : !!options.end;
|
|
71
74
|
}
|
|
72
75
|
|
|
76
|
+
// A version label is written as-is, so it cannot contain characters that need escaping,
|
|
77
|
+
// nor unpaired surrogates (which cannot be encoded)
|
|
78
|
+
if (options.version && invalidVersionLabel.test(options.version))
|
|
79
|
+
throw new Error(`Invalid version label: ${JSON.stringify(options.version)}`);
|
|
80
|
+
|
|
73
81
|
// Initialize writer, depending on the format
|
|
74
82
|
this._subject = null;
|
|
75
83
|
if (!(/triple|quad/i).test(options.format)) {
|
|
76
84
|
this._lineMode = false;
|
|
77
85
|
this._escape = escape, this._escapeAll = escapeAll, this._characterReplacer = characterReplacer;
|
|
78
86
|
this._graph = DEFAULTGRAPH;
|
|
87
|
+
if (options.version)
|
|
88
|
+
this._write(`@version "${options.version}".\n`);
|
|
79
89
|
this._prefixIRIs = Object.create(null);
|
|
80
90
|
// Escaped prefix IRIs and names for the prefix matcher, computed once per prefix
|
|
81
91
|
this._prefixPatterns = Object.create(null);
|
|
@@ -92,6 +102,8 @@ export default class N3Writer {
|
|
|
92
102
|
// N-Triples and N-Quads are written in their canonical form
|
|
93
103
|
this._escape = canonicalEscape, this._escapeAll = canonicalEscapeAll;
|
|
94
104
|
this._characterReplacer = canonicalCharacterReplacer;
|
|
105
|
+
if (options.version)
|
|
106
|
+
this._write(`VERSION "${options.version}"\n`);
|
|
95
107
|
}
|
|
96
108
|
}
|
|
97
109
|
|
|
@@ -107,6 +119,15 @@ export default class N3Writer {
|
|
|
107
119
|
this._outputStream.write(string, 'utf8', callback);
|
|
108
120
|
}
|
|
109
121
|
|
|
122
|
+
// ### `_endStatement` finishes a pending statement and closes an open graph block
|
|
123
|
+
_endStatement() {
|
|
124
|
+
if (this._subject !== null) {
|
|
125
|
+
this._write(this._inDefaultGraph ? '.\n' : '\n}\n');
|
|
126
|
+
this._subject = null;
|
|
127
|
+
this._graph = DEFAULTGRAPH;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
110
131
|
// ### `_writeQuad` writes the quad to the output stream
|
|
111
132
|
_writeQuad(subject, predicate, object, graph, done) {
|
|
112
133
|
try {
|
|
@@ -340,10 +361,7 @@ export default class N3Writer {
|
|
|
340
361
|
iri = iri.value;
|
|
341
362
|
hasPrefixes = true;
|
|
342
363
|
// Finish a possible pending quad
|
|
343
|
-
|
|
344
|
-
this._write(this._inDefaultGraph ? '.\n' : '\n}\n');
|
|
345
|
-
this._subject = null, this._graph = '';
|
|
346
|
-
}
|
|
364
|
+
this._endStatement();
|
|
347
365
|
// Store and write the prefix
|
|
348
366
|
this._prefixIRIs[iri] = (prefix += ':');
|
|
349
367
|
this._prefixPatterns[iri] = [escapeRegex(iri), escapeRegex(prefix)];
|
|
@@ -426,10 +444,7 @@ export default class N3Writer {
|
|
|
426
444
|
// ### `end` signals the end of the output stream
|
|
427
445
|
end(done) {
|
|
428
446
|
// Finish a possible pending quad
|
|
429
|
-
|
|
430
|
-
this._write(this._inDefaultGraph ? '.\n' : '\n}\n');
|
|
431
|
-
this._subject = null;
|
|
432
|
-
}
|
|
447
|
+
this._endStatement();
|
|
433
448
|
// Disallow further writing
|
|
434
449
|
this._write = this._blockedWrite;
|
|
435
450
|
|