n3 2.11.3 → 2.11.4

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/lib/N3Lexer.js CHANGED
@@ -89,6 +89,11 @@ function execAtEnd(regExp, input, pos) {
89
89
  return regExp.exec(`${input.slice(pos)} `);
90
90
  }
91
91
 
92
+ // Whitespace or the start of a comment
93
+ function isSeparatorCode(code) {
94
+ return code === SPACE || code === TAB || code === LF || code === CR || code === HASH;
95
+ }
96
+
92
97
  // ## Constructor
93
98
  class N3Lexer {
94
99
  constructor(options) {
@@ -194,6 +199,8 @@ class N3Lexer {
194
199
  }
195
200
  }
196
201
  if (pos >= input.length) {
202
+ // A datatype marker needs a type
203
+ if (inputFinished && this._previousMarker === '^^') return reportSyntaxError(this, input, pos);
197
204
  this._linePosition = currentLineLength;
198
205
  if (inputFinished) {
199
206
  emitToken('eof', '', '', this._line, currentLineLength, 0);
@@ -215,6 +222,8 @@ class N3Lexer {
215
222
  inconclusive = false;
216
223
  switch (firstChar) {
217
224
  case '^':
225
+ // A datatype marker separated from its type cannot be followed by another marker
226
+ if (this._previousMarker === '^^') return reportSyntaxError(this, input, pos);
218
227
  // We need at least 3 tokens lookahead to distinguish ^^<IRI> and ^^pre:fixed
219
228
  if (input.length - pos < 3) break;
220
229
  // Try to match a type
@@ -223,6 +232,8 @@ class N3Lexer {
223
232
  // Move to type IRI or prefixed name
224
233
  pos += 2;
225
234
  if (input[pos] !== '<') {
235
+ // Whitespace and comments may separate the marker from the type
236
+ if (isSeparatorCode(input.charCodeAt(pos))) continue; // eslint-disable-line no-continue
226
237
  inconclusive = true;
227
238
  break;
228
239
  }
package/lib/N3Writer.js CHANGED
@@ -19,9 +19,15 @@ const {
19
19
  xsd
20
20
  } = _IRIs.default;
21
21
 
22
- // Characters in literals that require escaping
22
+ // Characters that require escaping in Turtle, TriG, and N3,
23
+ // where characters outside the Basic Multilingual Plane are escaped as well
23
24
  const escape = /["\\\t\n\r\b\f\u0000-\u0019\ud800-\udbff]/,
24
25
  escapeAll = /["\\\t\n\r\b\f\u0000-\u0019]|[\ud800-\udbff][\udc00-\udfff]/g,
26
+ // Characters that require escaping in canonical N-Triples and N-Quads:
27
+ // U+0000–U+001F, `"`, `\`, U+007F, U+FFFE, and U+FFFF.
28
+ // A negated class is as fast to test as a shorter class listing characters.
29
+ canonicalEscape = /[^ !#-\[\]-~\u0080-\uFFFD]/,
30
+ canonicalEscapeAll = /[^ !#-\[\]-~\u0080-\uFFFD]/g,
25
31
  escapedCharacters = {
26
32
  '\\': '\\\\',
27
33
  '"': '\\"',
@@ -75,6 +81,7 @@ class N3Writer {
75
81
  this._subject = null;
76
82
  if (!/triple|quad/i.test(options.format)) {
77
83
  this._lineMode = false;
84
+ this._escape = escape, this._escapeAll = escapeAll, this._characterReplacer = characterReplacer;
78
85
  this._graph = DEFAULTGRAPH;
79
86
  this._prefixIRIs = Object.create(null);
80
87
  // Escaped prefix IRIs and names for the prefix matcher, computed once per prefix
@@ -87,6 +94,9 @@ class N3Writer {
87
94
  } else {
88
95
  this._lineMode = true;
89
96
  this._writeQuad = this._writeQuadLine;
97
+ // N-Triples and N-Quads are written in their canonical form
98
+ this._escape = canonicalEscape, this._escapeAll = canonicalEscapeAll;
99
+ this._characterReplacer = canonicalCharacterReplacer;
90
100
  }
91
101
  }
92
102
 
@@ -176,7 +186,7 @@ class N3Writer {
176
186
  iri = this._baseIri.toRelative(iri);
177
187
  }
178
188
  // Escape special characters
179
- if (escape.test(iri)) iri = iri.replace(escapeAll, characterReplacer);
189
+ if (this._escape.test(iri)) iri = iri.replace(this._escapeAll, this._characterReplacer);
180
190
  // Try to represent the IRI as prefixed name, unless no prefixes were added
181
191
  const prefixMatch = this._hasPrefixes ? this._prefixRegex.exec(iri) : null;
182
192
  return !prefixMatch ? `<${iri}>` : !prefixMatch[1] ? iri : this._prefixIRIs[prefixMatch[1]] + prefixMatch[2];
@@ -186,7 +196,7 @@ class N3Writer {
186
196
  _encodeLiteral(literal) {
187
197
  // Escape special characters
188
198
  let value = literal.value;
189
- if (escape.test(value)) value = value.replace(escapeAll, characterReplacer);
199
+ if (this._escape.test(value)) value = value.replace(this._escapeAll, this._characterReplacer);
190
200
 
191
201
  // Write a language-tagged literal
192
202
  const language = literal.language;
@@ -256,7 +266,9 @@ class N3Writer {
256
266
  object,
257
267
  graph
258
268
  }) {
259
- return `<<(${this._encodeSubject(subject)} ${this._encodePredicate(predicate)} ${this._encodeObject(object)}${(0, _N3Util.isDefaultGraph)(graph) ? '' : ` ${this._encodeIriOrBlank(graph)}`})>>`;
269
+ // Canonical N-Triples and N-Quads put spaces inside the delimiters
270
+ const space = this._lineMode ? ' ' : '';
271
+ return `<<(${space}${this._encodeSubject(subject)} ${this._encodePredicate(predicate)} ${this._encodeObject(object)}${(0, _N3Util.isDefaultGraph)(graph) ? '' : ` ${this._encodeIriOrBlank(graph)}`}${space})>>`;
260
272
  }
261
273
 
262
274
  // ### `_blockedWrite` replaces `_write` after the writer has been closed
@@ -412,4 +424,15 @@ function characterReplacer(character) {
412
424
  }
413
425
  }
414
426
  return result;
427
+ }
428
+
429
+ // Replaces a character by its canonical N-Triples escape:
430
+ // a short escape where one exists, and an upper-case `\uXXXX` escape otherwise
431
+ function canonicalCharacterReplacer(character) {
432
+ let result = escapedCharacters[character];
433
+ if (result === undefined) {
434
+ result = character.charCodeAt(0).toString(16).toUpperCase();
435
+ result = '\\u0000'.substr(0, 6 - result.length) + result;
436
+ }
437
+ return result;
415
438
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n3",
3
- "version": "2.11.3",
3
+ "version": "2.11.4",
4
4
  "description": "Lightning fast, asynchronous, streaming Turtle / N3 / RDF library.",
5
5
  "author": "Ruben Verborgh <ruben.verborgh@gmail.com>",
6
6
  "keywords": [
package/src/N3Lexer.js CHANGED
@@ -54,6 +54,11 @@ function execAtEnd(regExp, input, pos) {
54
54
  return regExp.exec(`${input.slice(pos)} `);
55
55
  }
56
56
 
57
+ // Whitespace or the start of a comment
58
+ function isSeparatorCode(code) {
59
+ return code === SPACE || code === TAB || code === LF || code === CR || code === HASH;
60
+ }
61
+
57
62
  // ## Constructor
58
63
  export default class N3Lexer {
59
64
  constructor(options) {
@@ -170,6 +175,9 @@ export default class N3Lexer {
170
175
  }
171
176
  }
172
177
  if (pos >= input.length) {
178
+ // A datatype marker needs a type
179
+ if (inputFinished && this._previousMarker === '^^')
180
+ return reportSyntaxError(this, input, pos);
173
181
  this._linePosition = currentLineLength;
174
182
  if (inputFinished) {
175
183
  emitToken('eof', '', '', this._line, currentLineLength, 0);
@@ -185,6 +193,9 @@ export default class N3Lexer {
185
193
  finalLineLength = 0, inconclusive = false;
186
194
  switch (firstChar) {
187
195
  case '^':
196
+ // A datatype marker separated from its type cannot be followed by another marker
197
+ if (this._previousMarker === '^^')
198
+ return reportSyntaxError(this, input, pos);
188
199
  // We need at least 3 tokens lookahead to distinguish ^^<IRI> and ^^pre:fixed
189
200
  if (input.length - pos < 3)
190
201
  break;
@@ -194,6 +205,9 @@ export default class N3Lexer {
194
205
  // Move to type IRI or prefixed name
195
206
  pos += 2;
196
207
  if (input[pos] !== '<') {
208
+ // Whitespace and comments may separate the marker from the type
209
+ if (isSeparatorCode(input.charCodeAt(pos)))
210
+ continue; // eslint-disable-line no-continue
197
211
  inconclusive = true;
198
212
  break;
199
213
  }
package/src/N3Writer.js CHANGED
@@ -9,9 +9,15 @@ const DEFAULTGRAPH = N3DataFactory.defaultGraph();
9
9
 
10
10
  const { rdf, xsd } = namespaces;
11
11
 
12
- // Characters in literals that require escaping
12
+ // Characters that require escaping in Turtle, TriG, and N3,
13
+ // where characters outside the Basic Multilingual Plane are escaped as well
13
14
  const escape = /["\\\t\n\r\b\f\u0000-\u0019\ud800-\udbff]/,
14
15
  escapeAll = /["\\\t\n\r\b\f\u0000-\u0019]|[\ud800-\udbff][\udc00-\udfff]/g,
16
+ // Characters that require escaping in canonical N-Triples and N-Quads:
17
+ // U+0000–U+001F, `"`, `\`, U+007F, U+FFFE, and U+FFFF.
18
+ // A negated class is as fast to test as a shorter class listing characters.
19
+ canonicalEscape = /[^ !#-\[\]-~\u0080-\uFFFD]/,
20
+ canonicalEscapeAll = /[^ !#-\[\]-~\u0080-\uFFFD]/g,
15
21
  escapedCharacters = {
16
22
  '\\': '\\\\', '"': '\\"', '\t': '\\t',
17
23
  '\n': '\\n', '\r': '\\r', '\b': '\\b', '\f': '\\f',
@@ -57,6 +63,7 @@ export default class N3Writer {
57
63
  this._subject = null;
58
64
  if (!(/triple|quad/i).test(options.format)) {
59
65
  this._lineMode = false;
66
+ this._escape = escape, this._escapeAll = escapeAll, this._characterReplacer = characterReplacer;
60
67
  this._graph = DEFAULTGRAPH;
61
68
  this._prefixIRIs = Object.create(null);
62
69
  // Escaped prefix IRIs and names for the prefix matcher, computed once per prefix
@@ -71,6 +78,9 @@ export default class N3Writer {
71
78
  else {
72
79
  this._lineMode = true;
73
80
  this._writeQuad = this._writeQuadLine;
81
+ // N-Triples and N-Quads are written in their canonical form
82
+ this._escape = canonicalEscape, this._escapeAll = canonicalEscapeAll;
83
+ this._characterReplacer = canonicalCharacterReplacer;
74
84
  }
75
85
  }
76
86
 
@@ -176,8 +186,8 @@ export default class N3Writer {
176
186
  iri = this._baseIri.toRelative(iri);
177
187
  }
178
188
  // Escape special characters
179
- if (escape.test(iri))
180
- iri = iri.replace(escapeAll, characterReplacer);
189
+ if (this._escape.test(iri))
190
+ iri = iri.replace(this._escapeAll, this._characterReplacer);
181
191
  // Try to represent the IRI as prefixed name, unless no prefixes were added
182
192
  const prefixMatch = this._hasPrefixes ? this._prefixRegex.exec(iri) : null;
183
193
  return !prefixMatch ? `<${iri}>` :
@@ -188,8 +198,8 @@ export default class N3Writer {
188
198
  _encodeLiteral(literal) {
189
199
  // Escape special characters
190
200
  let value = literal.value;
191
- if (escape.test(value))
192
- value = value.replace(escapeAll, characterReplacer);
201
+ if (this._escape.test(value))
202
+ value = value.replace(this._escapeAll, this._characterReplacer);
193
203
 
194
204
  // Write a language-tagged literal
195
205
  const language = literal.language;
@@ -260,11 +270,13 @@ export default class N3Writer {
260
270
 
261
271
  // ### `_encodeQuad` encodes an RDF-star quad
262
272
  _encodeQuad({ subject, predicate, object, graph }) {
263
- return `<<(${
273
+ // Canonical N-Triples and N-Quads put spaces inside the delimiters
274
+ const space = this._lineMode ? ' ' : '';
275
+ return `<<(${space}${
264
276
  this._encodeSubject(subject)} ${
265
277
  this._encodePredicate(predicate)} ${
266
278
  this._encodeObject(object)}${
267
- isDefaultGraph(graph) ? '' : ` ${this._encodeIriOrBlank(graph)}`})>>`;
279
+ isDefaultGraph(graph) ? '' : ` ${this._encodeIriOrBlank(graph)}`}${space})>>`;
268
280
  }
269
281
 
270
282
  // ### `_blockedWrite` replaces `_write` after the writer has been closed
@@ -428,3 +440,14 @@ function characterReplacer(character) {
428
440
  }
429
441
  return result;
430
442
  }
443
+
444
+ // Replaces a character by its canonical N-Triples escape:
445
+ // a short escape where one exists, and an upper-case `\uXXXX` escape otherwise
446
+ function canonicalCharacterReplacer(character) {
447
+ let result = escapedCharacters[character];
448
+ if (result === undefined) {
449
+ result = character.charCodeAt(0).toString(16).toUpperCase();
450
+ result = '\\u0000'.substr(0, 6 - result.length) + result;
451
+ }
452
+ return result;
453
+ }