n3 2.8.0 → 2.9.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 +13 -2
- package/browser/n3.esm.min.js +7 -7
- package/browser/n3.min.js +7 -7
- package/lib/N3Lexer.js +34 -28
- package/lib/N3Parser.js +42 -21
- package/package.json +1 -1
- package/src/N3Lexer.js +38 -29
- package/src/N3Parser.js +47 -26
package/lib/N3Lexer.js
CHANGED
|
@@ -18,8 +18,6 @@ const SPACE = 0x20,
|
|
|
18
18
|
CR = 0x0D,
|
|
19
19
|
HASH = 0x23;
|
|
20
20
|
|
|
21
|
-
// Regular expression and replacement strings to unescape N3 strings
|
|
22
|
-
const escapeSequence = /\\u([a-fA-F0-9]{4})|\\U([a-fA-F0-9]{8})|\\([^])/g;
|
|
23
21
|
// Fixed escape sequences allowed in string literals (ECHAR)
|
|
24
22
|
const stringEscapeReplacements = {
|
|
25
23
|
'\\': '\\',
|
|
@@ -549,34 +547,42 @@ class N3Lexer {
|
|
|
549
547
|
// ### `_unescape` replaces N3 escape codes by their corresponding characters,
|
|
550
548
|
// allowing only the fixed escape sequences from the given replacement table
|
|
551
549
|
_unescape(item, replacements) {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
550
|
+
let backslash = item.indexOf('\\');
|
|
551
|
+
if (backslash < 0) return item;
|
|
552
|
+
let result = '',
|
|
553
|
+
start = 0;
|
|
554
|
+
// A trailing lone backslash is not an escape sequence.
|
|
555
|
+
while (backslash >= 0 && backslash + 1 < item.length) {
|
|
556
|
+
const escapedChar = item[backslash + 1];
|
|
557
|
+
let end = backslash + 2,
|
|
558
|
+
replacement;
|
|
559
|
+
if (escapedChar === 'u' || escapedChar === 'U') {
|
|
560
|
+
end += escapedChar === 'u' ? 4 : 8;
|
|
561
|
+
if (end > item.length) return null;
|
|
562
|
+
let charCode = 0;
|
|
563
|
+
for (let i = backslash + 2; i < end; i++) {
|
|
564
|
+
let digit = item.charCodeAt(i);
|
|
565
|
+
if (digit >= 0x30 && digit <= 0x39)
|
|
566
|
+
// 0–9
|
|
567
|
+
digit -= 0x30;else if (digit >= 0x41 && digit <= 0x46)
|
|
568
|
+
// A–F
|
|
569
|
+
digit -= 0x41 - 10;else if (digit >= 0x61 && digit <= 0x66)
|
|
570
|
+
// a–f
|
|
571
|
+
digit -= 0x61 - 10;else return null;
|
|
572
|
+
// Eight digits can exceed a signed 32-bit integer; avoid bitwise shifts.
|
|
573
|
+
charCode = charCode * 16 + digit;
|
|
561
574
|
}
|
|
562
|
-
|
|
575
|
+
if (!isValidCodePoint(charCode)) return null;
|
|
576
|
+
replacement = String.fromCodePoint(charCode);
|
|
577
|
+
} else {
|
|
578
|
+
if (!(escapedChar in replacements)) return null;
|
|
579
|
+
replacement = replacements[escapedChar];
|
|
563
580
|
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
return '';
|
|
570
|
-
}
|
|
571
|
-
return charCode <= 0xFFFF ? String.fromCharCode(Number.parseInt(unicode8, 16)) : String.fromCharCode(0xD800 + ((charCode -= 0x10000) >> 10), 0xDC00 + (charCode & 0x3FF));
|
|
572
|
-
}
|
|
573
|
-
// fixed escape sequence
|
|
574
|
-
if (escapedChar in replacements) return replacements[escapedChar];
|
|
575
|
-
// invalid escape sequence
|
|
576
|
-
invalid = true;
|
|
577
|
-
return '';
|
|
578
|
-
});
|
|
579
|
-
return invalid ? null : replaced;
|
|
581
|
+
result += item.slice(start, backslash) + replacement;
|
|
582
|
+
start = end;
|
|
583
|
+
backslash = item.indexOf('\\', start);
|
|
584
|
+
}
|
|
585
|
+
return result + item.slice(start);
|
|
580
586
|
}
|
|
581
587
|
|
|
582
588
|
// ### `_parseLiteral` parses a literal into an unescaped value
|
package/lib/N3Parser.js
CHANGED
|
@@ -1446,14 +1446,16 @@ class N3Parser {
|
|
|
1446
1446
|
|
|
1447
1447
|
// ### `parse` parses the N3 input and emits each parsed quad through the onQuad callback.
|
|
1448
1448
|
parse(input, quadCallback, prefixCallback, versionCallback) {
|
|
1449
|
-
// The second parameter accepts an object
|
|
1449
|
+
// The second parameter also accepts an object of named callbacks.
|
|
1450
1450
|
// As a second and third parameter it still accepts a separate quadCallback and prefixCallback for backward compatibility as well
|
|
1451
|
-
let onQuad, onPrefix, onComment, onVersion;
|
|
1452
|
-
if (quadCallback && (quadCallback.onQuad || quadCallback.onPrefix || quadCallback.onComment || quadCallback.onVersion)) {
|
|
1451
|
+
let onQuad, onPrefix, onComment, onVersion, onToken, onTokenEnd;
|
|
1452
|
+
if (quadCallback && (quadCallback.onQuad || quadCallback.onPrefix || quadCallback.onComment || quadCallback.onVersion || quadCallback.onToken || quadCallback.onTokenEnd)) {
|
|
1453
1453
|
onQuad = quadCallback.onQuad;
|
|
1454
1454
|
onPrefix = quadCallback.onPrefix;
|
|
1455
1455
|
onComment = quadCallback.onComment;
|
|
1456
1456
|
onVersion = quadCallback.onVersion;
|
|
1457
|
+
onToken = quadCallback.onToken;
|
|
1458
|
+
onTokenEnd = quadCallback.onTokenEnd;
|
|
1457
1459
|
} else {
|
|
1458
1460
|
onQuad = quadCallback;
|
|
1459
1461
|
onPrefix = prefixCallback;
|
|
@@ -1473,36 +1475,55 @@ class N3Parser {
|
|
|
1473
1475
|
this._expectOf = false;
|
|
1474
1476
|
this._quantified = Object.create(null);
|
|
1475
1477
|
this._emptyFormula = false;
|
|
1478
|
+
let readToken = token => {
|
|
1479
|
+
return this._readCallback = this._readCallback(token);
|
|
1480
|
+
};
|
|
1481
|
+
|
|
1482
|
+
// Comments bypass the grammar, but participate in the token lifecycle.
|
|
1483
|
+
if (onComment || this._lexer.comments) {
|
|
1484
|
+
this._lexer.comments = true;
|
|
1485
|
+
const readGrammarToken = readToken;
|
|
1486
|
+
readToken = token => {
|
|
1487
|
+
if (token.type !== 'comment') return readGrammarToken(token);
|
|
1488
|
+
if (onComment) onComment(token.value);
|
|
1489
|
+
return this._readCallback;
|
|
1490
|
+
};
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
// Install observers once per parse, leaving the ordinary token path intact.
|
|
1494
|
+
if (onToken || onTokenEnd) {
|
|
1495
|
+
const consumeToken = readToken;
|
|
1496
|
+
readToken = token => {
|
|
1497
|
+
try {
|
|
1498
|
+
try {
|
|
1499
|
+
if (onToken) onToken(token);
|
|
1500
|
+
return consumeToken(token);
|
|
1501
|
+
} finally {
|
|
1502
|
+
if (onTokenEnd) onTokenEnd(token);
|
|
1503
|
+
}
|
|
1504
|
+
} catch (error) {
|
|
1505
|
+
// A consumer exception aborts this parse, including later chunks.
|
|
1506
|
+
this._readCallback = null;
|
|
1507
|
+
throw error;
|
|
1508
|
+
}
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1476
1511
|
|
|
1477
|
-
// Parse synchronously if no quad callback is given
|
|
1512
|
+
// Parse synchronously if no quad callback is given.
|
|
1478
1513
|
if (!onQuad) {
|
|
1479
1514
|
const quads = [];
|
|
1480
1515
|
let error;
|
|
1481
1516
|
this._callback = (e, t) => {
|
|
1482
1517
|
e ? error = e : t && quads.push(t);
|
|
1483
1518
|
};
|
|
1484
|
-
this._lexer.tokenize(input).every(
|
|
1485
|
-
return this._readCallback = this._readCallback(token);
|
|
1486
|
-
});
|
|
1519
|
+
this._lexer.tokenize(input).every(readToken);
|
|
1487
1520
|
if (error) throw error;
|
|
1488
1521
|
return quads;
|
|
1489
1522
|
}
|
|
1490
|
-
|
|
1491
|
-
if (error !== null) this._callback(error), this._callback = noop;else if (this._readCallback)
|
|
1523
|
+
const processNextToken = (error, token) => {
|
|
1524
|
+
if (error !== null) this._callback(error), this._callback = noop;else if (this._readCallback) readToken(token);
|
|
1492
1525
|
};
|
|
1493
1526
|
|
|
1494
|
-
// Enable checking for comments on every token when a commentCallback has been set
|
|
1495
|
-
if (onComment) {
|
|
1496
|
-
// Enable the lexer to return comments as tokens first (disabled by default)
|
|
1497
|
-
this._lexer.comments = true;
|
|
1498
|
-
// Patch the processNextToken function
|
|
1499
|
-
processNextToken = (error, token) => {
|
|
1500
|
-
if (error !== null) this._callback(error), this._callback = noop;else if (this._readCallback) {
|
|
1501
|
-
if (token.type === 'comment') onComment(token.value);else this._readCallback = this._readCallback(token);
|
|
1502
|
-
}
|
|
1503
|
-
};
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
1527
|
// Parse asynchronously otherwise, executing the read callback when a token arrives
|
|
1507
1528
|
this._callback = onQuad;
|
|
1508
1529
|
this._lexer.tokenize(input, processNextToken);
|
package/package.json
CHANGED
package/src/N3Lexer.js
CHANGED
|
@@ -5,8 +5,6 @@ import namespaces from './IRIs';
|
|
|
5
5
|
const { xsd } = namespaces;
|
|
6
6
|
const SPACE = 0x20, TAB = 0x09, LF = 0x0A, CR = 0x0D, HASH = 0x23;
|
|
7
7
|
|
|
8
|
-
// Regular expression and replacement strings to unescape N3 strings
|
|
9
|
-
const escapeSequence = /\\u([a-fA-F0-9]{4})|\\U([a-fA-F0-9]{8})|\\([^])/g;
|
|
10
8
|
// Fixed escape sequences allowed in string literals (ECHAR)
|
|
11
9
|
const stringEscapeReplacements = {
|
|
12
10
|
'\\': '\\', "'": "'", '"': '"',
|
|
@@ -561,37 +559,48 @@ export default class N3Lexer {
|
|
|
561
559
|
// ### `_unescape` replaces N3 escape codes by their corresponding characters,
|
|
562
560
|
// allowing only the fixed escape sequences from the given replacement table
|
|
563
561
|
_unescape(item, replacements) {
|
|
564
|
-
|
|
562
|
+
let backslash = item.indexOf('\\');
|
|
563
|
+
if (backslash < 0)
|
|
565
564
|
return item;
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
565
|
+
|
|
566
|
+
let result = '', start = 0;
|
|
567
|
+
// A trailing lone backslash is not an escape sequence.
|
|
568
|
+
while (backslash >= 0 && backslash + 1 < item.length) {
|
|
569
|
+
const escapedChar = item[backslash + 1];
|
|
570
|
+
let end = backslash + 2, replacement;
|
|
571
|
+
if (escapedChar === 'u' || escapedChar === 'U') {
|
|
572
|
+
end += escapedChar === 'u' ? 4 : 8;
|
|
573
|
+
if (end > item.length)
|
|
574
|
+
return null;
|
|
575
|
+
|
|
576
|
+
let charCode = 0;
|
|
577
|
+
for (let i = backslash + 2; i < end; i++) {
|
|
578
|
+
let digit = item.charCodeAt(i);
|
|
579
|
+
if (digit >= 0x30 && digit <= 0x39) // 0–9
|
|
580
|
+
digit -= 0x30;
|
|
581
|
+
else if (digit >= 0x41 && digit <= 0x46) // A–F
|
|
582
|
+
digit -= 0x41 - 10;
|
|
583
|
+
else if (digit >= 0x61 && digit <= 0x66) // a–f
|
|
584
|
+
digit -= 0x61 - 10;
|
|
585
|
+
else
|
|
586
|
+
return null;
|
|
587
|
+
// Eight digits can exceed a signed 32-bit integer; avoid bitwise shifts.
|
|
588
|
+
charCode = charCode * 16 + digit;
|
|
574
589
|
}
|
|
575
|
-
|
|
590
|
+
if (!isValidCodePoint(charCode))
|
|
591
|
+
return null;
|
|
592
|
+
replacement = String.fromCodePoint(charCode);
|
|
576
593
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
invalid = true;
|
|
582
|
-
return '';
|
|
583
|
-
}
|
|
584
|
-
return charCode <= 0xFFFF ? String.fromCharCode(Number.parseInt(unicode8, 16)) :
|
|
585
|
-
String.fromCharCode(0xD800 + ((charCode -= 0x10000) >> 10), 0xDC00 + (charCode & 0x3FF));
|
|
594
|
+
else {
|
|
595
|
+
if (!(escapedChar in replacements))
|
|
596
|
+
return null;
|
|
597
|
+
replacement = replacements[escapedChar];
|
|
586
598
|
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
return '';
|
|
593
|
-
});
|
|
594
|
-
return invalid ? null : replaced;
|
|
599
|
+
result += item.slice(start, backslash) + replacement;
|
|
600
|
+
start = end;
|
|
601
|
+
backslash = item.indexOf('\\', start);
|
|
602
|
+
}
|
|
603
|
+
return result + item.slice(start);
|
|
595
604
|
}
|
|
596
605
|
|
|
597
606
|
// ### `_parseLiteral` parses a literal into an unescaped value
|
package/src/N3Parser.js
CHANGED
|
@@ -1564,14 +1564,17 @@ export default class N3Parser {
|
|
|
1564
1564
|
|
|
1565
1565
|
// ### `parse` parses the N3 input and emits each parsed quad through the onQuad callback.
|
|
1566
1566
|
parse(input, quadCallback, prefixCallback, versionCallback) {
|
|
1567
|
-
// The second parameter accepts an object
|
|
1567
|
+
// The second parameter also accepts an object of named callbacks.
|
|
1568
1568
|
// As a second and third parameter it still accepts a separate quadCallback and prefixCallback for backward compatibility as well
|
|
1569
|
-
let onQuad, onPrefix, onComment, onVersion;
|
|
1570
|
-
if (quadCallback && (quadCallback.onQuad || quadCallback.onPrefix || quadCallback.onComment || quadCallback.onVersion
|
|
1569
|
+
let onQuad, onPrefix, onComment, onVersion, onToken, onTokenEnd;
|
|
1570
|
+
if (quadCallback && (quadCallback.onQuad || quadCallback.onPrefix || quadCallback.onComment || quadCallback.onVersion ||
|
|
1571
|
+
quadCallback.onToken || quadCallback.onTokenEnd)) {
|
|
1571
1572
|
onQuad = quadCallback.onQuad;
|
|
1572
1573
|
onPrefix = quadCallback.onPrefix;
|
|
1573
1574
|
onComment = quadCallback.onComment;
|
|
1574
1575
|
onVersion = quadCallback.onVersion;
|
|
1576
|
+
onToken = quadCallback.onToken;
|
|
1577
|
+
onTokenEnd = quadCallback.onTokenEnd;
|
|
1575
1578
|
}
|
|
1576
1579
|
else {
|
|
1577
1580
|
onQuad = quadCallback;
|
|
@@ -1595,42 +1598,60 @@ export default class N3Parser {
|
|
|
1595
1598
|
this._quantified = Object.create(null);
|
|
1596
1599
|
this._emptyFormula = false;
|
|
1597
1600
|
|
|
1598
|
-
|
|
1601
|
+
let readToken = token => {
|
|
1602
|
+
return this._readCallback = this._readCallback(token);
|
|
1603
|
+
};
|
|
1604
|
+
|
|
1605
|
+
// Comments bypass the grammar, but participate in the token lifecycle.
|
|
1606
|
+
if (onComment || this._lexer.comments) {
|
|
1607
|
+
this._lexer.comments = true;
|
|
1608
|
+
const readGrammarToken = readToken;
|
|
1609
|
+
readToken = token => {
|
|
1610
|
+
if (token.type !== 'comment')
|
|
1611
|
+
return readGrammarToken(token);
|
|
1612
|
+
if (onComment) onComment(token.value);
|
|
1613
|
+
return this._readCallback;
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
// Install observers once per parse, leaving the ordinary token path intact.
|
|
1618
|
+
if (onToken || onTokenEnd) {
|
|
1619
|
+
const consumeToken = readToken;
|
|
1620
|
+
readToken = token => {
|
|
1621
|
+
try {
|
|
1622
|
+
try {
|
|
1623
|
+
if (onToken) onToken(token);
|
|
1624
|
+
return consumeToken(token);
|
|
1625
|
+
}
|
|
1626
|
+
finally {
|
|
1627
|
+
if (onTokenEnd) onTokenEnd(token);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
catch (error) {
|
|
1631
|
+
// A consumer exception aborts this parse, including later chunks.
|
|
1632
|
+
this._readCallback = null;
|
|
1633
|
+
throw error;
|
|
1634
|
+
}
|
|
1635
|
+
};
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
// Parse synchronously if no quad callback is given.
|
|
1599
1639
|
if (!onQuad) {
|
|
1600
1640
|
const quads = [];
|
|
1601
1641
|
let error;
|
|
1602
1642
|
this._callback = (e, t) => { e ? (error = e) : t && quads.push(t); };
|
|
1603
|
-
this._lexer.tokenize(input).every(
|
|
1604
|
-
return this._readCallback = this._readCallback(token);
|
|
1605
|
-
});
|
|
1643
|
+
this._lexer.tokenize(input).every(readToken);
|
|
1606
1644
|
if (error) throw error;
|
|
1607
1645
|
return quads;
|
|
1608
1646
|
}
|
|
1609
1647
|
|
|
1610
|
-
|
|
1648
|
+
const processNextToken = (error, token) => {
|
|
1611
1649
|
if (error !== null)
|
|
1612
1650
|
this._callback(error), this._callback = noop;
|
|
1613
1651
|
else if (this._readCallback)
|
|
1614
|
-
|
|
1652
|
+
readToken(token);
|
|
1615
1653
|
};
|
|
1616
1654
|
|
|
1617
|
-
// Enable checking for comments on every token when a commentCallback has been set
|
|
1618
|
-
if (onComment) {
|
|
1619
|
-
// Enable the lexer to return comments as tokens first (disabled by default)
|
|
1620
|
-
this._lexer.comments = true;
|
|
1621
|
-
// Patch the processNextToken function
|
|
1622
|
-
processNextToken = (error, token) => {
|
|
1623
|
-
if (error !== null)
|
|
1624
|
-
this._callback(error), this._callback = noop;
|
|
1625
|
-
else if (this._readCallback) {
|
|
1626
|
-
if (token.type === 'comment')
|
|
1627
|
-
onComment(token.value);
|
|
1628
|
-
else
|
|
1629
|
-
this._readCallback = this._readCallback(token);
|
|
1630
|
-
}
|
|
1631
|
-
};
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
1655
|
// Parse asynchronously otherwise, executing the read callback when a token arrives
|
|
1635
1656
|
this._callback = onQuad;
|
|
1636
1657
|
this._lexer.tokenize(input, processNextToken);
|