eyeling 1.28.8 → 1.28.9
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/dist/browser/eyeling.browser.js +80 -49
- package/examples/complex-matrix-stability.n3 +23 -23
- package/examples/deep-taxonomy-10.n3 +2 -2
- package/examples/deep-taxonomy-100.n3 +2 -2
- package/examples/deep-taxonomy-1000.n3 +2 -2
- package/examples/deep-taxonomy-10000.n3 +2 -2
- package/eyeling.js +80 -49
- package/lib/lexer.js +80 -49
- package/package.json +1 -1
- package/test/api.test.js +127 -1
- package/test/playground.test.js +17 -3
|
@@ -13298,7 +13298,76 @@ function lex(inputText, opts = {}) {
|
|
|
13298
13298
|
continue;
|
|
13299
13299
|
}
|
|
13300
13300
|
|
|
13301
|
-
// 5)
|
|
13301
|
+
// 5) Numeric literal (integer, decimal, or double). Turtle/N3 numeric
|
|
13302
|
+
// literals may have a leading sign and decimals may start with '.', e.g.
|
|
13303
|
+
// '+42' and '.5'. Reject repeated decimal points so '1.2.3'
|
|
13304
|
+
// cannot be misread as one impossible numeric-like token.
|
|
13305
|
+
if (
|
|
13306
|
+
isAsciiDigit(c) ||
|
|
13307
|
+
((c === '+' || c === '-') && (isAsciiDigit(peek(1)) || (peek(1) === '.' && isAsciiDigit(peek(2))))) ||
|
|
13308
|
+
(c === '.' && isAsciiDigit(peek(1)) && !isAsciiDigit(peek(-1)))
|
|
13309
|
+
) {
|
|
13310
|
+
const start = i;
|
|
13311
|
+
const numChars = [];
|
|
13312
|
+
|
|
13313
|
+
if (chars[i] === '+' || chars[i] === '-') {
|
|
13314
|
+
numChars.push(chars[i]);
|
|
13315
|
+
i++;
|
|
13316
|
+
}
|
|
13317
|
+
|
|
13318
|
+
if (chars[i] === '.') {
|
|
13319
|
+
numChars.push('.');
|
|
13320
|
+
i++;
|
|
13321
|
+
while (i < n && isAsciiDigit(chars[i])) {
|
|
13322
|
+
numChars.push(chars[i]);
|
|
13323
|
+
i++;
|
|
13324
|
+
}
|
|
13325
|
+
} else {
|
|
13326
|
+
while (i < n && isAsciiDigit(chars[i])) {
|
|
13327
|
+
numChars.push(chars[i]);
|
|
13328
|
+
i++;
|
|
13329
|
+
}
|
|
13330
|
+
if (i < n && chars[i] === '.' && i + 1 < n && isAsciiDigit(chars[i + 1])) {
|
|
13331
|
+
numChars.push('.');
|
|
13332
|
+
i++;
|
|
13333
|
+
while (i < n && isAsciiDigit(chars[i])) {
|
|
13334
|
+
numChars.push(chars[i]);
|
|
13335
|
+
i++;
|
|
13336
|
+
}
|
|
13337
|
+
}
|
|
13338
|
+
}
|
|
13339
|
+
|
|
13340
|
+
if (i < n && chars[i] === '.' && i + 1 < n && isAsciiDigit(chars[i + 1])) {
|
|
13341
|
+
throw new N3SyntaxError('Malformed numeric literal: multiple decimal points', start);
|
|
13342
|
+
}
|
|
13343
|
+
|
|
13344
|
+
// Optional exponent part: e.g., 1e0, 1.1e-3, .5E+0.
|
|
13345
|
+
if (i < n && (chars[i] === 'e' || chars[i] === 'E')) {
|
|
13346
|
+
let j = i + 1;
|
|
13347
|
+
if (j < n && (chars[j] === '+' || chars[j] === '-')) j++;
|
|
13348
|
+
if (j < n && isAsciiDigit(chars[j])) {
|
|
13349
|
+
numChars.push(chars[i]); // e/E
|
|
13350
|
+
i++;
|
|
13351
|
+
if (i < n && (chars[i] === '+' || chars[i] === '-')) {
|
|
13352
|
+
numChars.push(chars[i]);
|
|
13353
|
+
i++;
|
|
13354
|
+
}
|
|
13355
|
+
while (i < n && isAsciiDigit(chars[i])) {
|
|
13356
|
+
numChars.push(chars[i]);
|
|
13357
|
+
i++;
|
|
13358
|
+
}
|
|
13359
|
+
}
|
|
13360
|
+
}
|
|
13361
|
+
|
|
13362
|
+
if (i < n && chars[i] === '.' && i + 1 < n && isAsciiDigit(chars[i + 1])) {
|
|
13363
|
+
throw new N3SyntaxError('Malformed numeric literal: multiple decimal points', start);
|
|
13364
|
+
}
|
|
13365
|
+
|
|
13366
|
+
tokens.push(new Token('Literal', numChars.join(''), start));
|
|
13367
|
+
continue;
|
|
13368
|
+
}
|
|
13369
|
+
|
|
13370
|
+
// 6) Single-character punctuation. Use a switch rather than allocating a
|
|
13302
13371
|
// mapping object for every punctuation token in large inputs.
|
|
13303
13372
|
switch (c) {
|
|
13304
13373
|
case '{':
|
|
@@ -13419,13 +13488,17 @@ function lex(inputText, opts = {}) {
|
|
|
13419
13488
|
}
|
|
13420
13489
|
continue;
|
|
13421
13490
|
}
|
|
13491
|
+
if (cc === '\n' || cc === '\r') {
|
|
13492
|
+
throw new N3SyntaxError('Unescaped newline in short string literal', start);
|
|
13493
|
+
}
|
|
13422
13494
|
if (cc === '"') {
|
|
13423
13495
|
closed = true;
|
|
13424
13496
|
break;
|
|
13425
13497
|
}
|
|
13426
13498
|
if (sChars !== null) sChars.push(cc);
|
|
13427
13499
|
}
|
|
13428
|
-
|
|
13500
|
+
if (!closed) throw new N3SyntaxError('Unterminated short string literal "..."', start);
|
|
13501
|
+
const rawContent = sChars === null ? sliceChars(contentStart, i - 1) : sChars.join('');
|
|
13429
13502
|
const decoded = sChars === null ? rawContent : decodeN3StringEscapes(rawContent, start);
|
|
13430
13503
|
if (sChars !== null || inputMayContainInvalidStringChar) assertValidStringLiteralValue(decoded, start);
|
|
13431
13504
|
const s = JSON.stringify(decoded); // canonical short quoted form
|
|
@@ -13509,13 +13582,17 @@ function lex(inputText, opts = {}) {
|
|
|
13509
13582
|
}
|
|
13510
13583
|
continue;
|
|
13511
13584
|
}
|
|
13585
|
+
if (cc === '\n' || cc === '\r') {
|
|
13586
|
+
throw new N3SyntaxError('Unescaped newline in short string literal', start);
|
|
13587
|
+
}
|
|
13512
13588
|
if (cc === "'") {
|
|
13513
13589
|
closed = true;
|
|
13514
13590
|
break;
|
|
13515
13591
|
}
|
|
13516
13592
|
if (sChars !== null) sChars.push(cc);
|
|
13517
13593
|
}
|
|
13518
|
-
|
|
13594
|
+
if (!closed) throw new N3SyntaxError("Unterminated short string literal '...'", start);
|
|
13595
|
+
const rawContent = sChars === null ? sliceChars(contentStart, i - 1) : sChars.join('');
|
|
13519
13596
|
const decoded = sChars === null ? rawContent : decodeN3StringEscapes(rawContent, start);
|
|
13520
13597
|
if (sChars !== null || inputMayContainInvalidStringChar) assertValidStringLiteralValue(decoded, start);
|
|
13521
13598
|
const s = JSON.stringify(decoded); // canonical short quoted form
|
|
@@ -13598,52 +13675,6 @@ function lex(inputText, opts = {}) {
|
|
|
13598
13675
|
continue;
|
|
13599
13676
|
}
|
|
13600
13677
|
|
|
13601
|
-
// 6) Numeric literal (integer or float)
|
|
13602
|
-
if (isAsciiDigit(c) || (c === '-' && peek(1) !== null && isAsciiDigit(peek(1)))) {
|
|
13603
|
-
const start = i;
|
|
13604
|
-
const numChars = [c];
|
|
13605
|
-
i++;
|
|
13606
|
-
while (i < n) {
|
|
13607
|
-
const cc = chars[i];
|
|
13608
|
-
if (isAsciiDigit(cc)) {
|
|
13609
|
-
numChars.push(cc);
|
|
13610
|
-
i++;
|
|
13611
|
-
continue;
|
|
13612
|
-
}
|
|
13613
|
-
if (cc === '.') {
|
|
13614
|
-
if (i + 1 < n && isAsciiDigit(chars[i + 1])) {
|
|
13615
|
-
numChars.push('.');
|
|
13616
|
-
i++;
|
|
13617
|
-
continue;
|
|
13618
|
-
} else {
|
|
13619
|
-
break;
|
|
13620
|
-
}
|
|
13621
|
-
}
|
|
13622
|
-
break;
|
|
13623
|
-
}
|
|
13624
|
-
|
|
13625
|
-
// Optional exponent part: e.g., 1e0, 1.1e-3, 1.1E+0
|
|
13626
|
-
if (i < n && (chars[i] === 'e' || chars[i] === 'E')) {
|
|
13627
|
-
let j = i + 1;
|
|
13628
|
-
if (j < n && (chars[j] === '+' || chars[j] === '-')) j++;
|
|
13629
|
-
if (j < n && isAsciiDigit(chars[j])) {
|
|
13630
|
-
numChars.push(chars[i]); // e/E
|
|
13631
|
-
i++;
|
|
13632
|
-
if (i < n && (chars[i] === '+' || chars[i] === '-')) {
|
|
13633
|
-
numChars.push(chars[i]);
|
|
13634
|
-
i++;
|
|
13635
|
-
}
|
|
13636
|
-
while (i < n && isAsciiDigit(chars[i])) {
|
|
13637
|
-
numChars.push(chars[i]);
|
|
13638
|
-
i++;
|
|
13639
|
-
}
|
|
13640
|
-
}
|
|
13641
|
-
}
|
|
13642
|
-
|
|
13643
|
-
tokens.push(new Token('Literal', numChars.join(''), start));
|
|
13644
|
-
continue;
|
|
13645
|
-
}
|
|
13646
|
-
|
|
13647
13678
|
// 7) Identifiers / keywords / QNames
|
|
13648
13679
|
const start = i;
|
|
13649
13680
|
const word = readIdentText(start);
|
|
@@ -253,38 +253,38 @@
|
|
|
253
253
|
:Check :c4 ?C4.
|
|
254
254
|
:Check :c5 ?C5.
|
|
255
255
|
(
|
|
256
|
-
"Complex Matrix Stability — ARC-style
|
|
256
|
+
"""Complex Matrix Stability — ARC-style
|
|
257
257
|
|
|
258
|
-
"
|
|
259
|
-
"## Answer
|
|
260
|
-
"
|
|
258
|
+
"""
|
|
259
|
+
"""## Answer
|
|
260
|
+
"""
|
|
261
261
|
"We compare three diagonal 2x2 complex matrices for discrete-time stability: "
|
|
262
262
|
"A_unstable = " ?MuS ", A_stable = " ?MsS ", and A_damped = " ?MdS ". "
|
|
263
263
|
"Their spectral radii are ρ(A_unstable) = " ?Ru ", ρ(A_stable) = " ?Rs ", and ρ(A_damped) = " ?Rd ". "
|
|
264
|
-
"So A_unstable is unstable, A_stable is marginally stable, and A_damped is damped.
|
|
264
|
+
"""So A_unstable is unstable, A_stable is marginally stable, and A_damped is damped.
|
|
265
265
|
|
|
266
|
-
"
|
|
267
|
-
"## Reason Why
|
|
268
|
-
"
|
|
266
|
+
"""
|
|
267
|
+
"""## Reason Why
|
|
268
|
+
"""
|
|
269
269
|
"For a discrete-time linear system x_{k+1} = A x_k, the eigenvalues of A govern the behaviour of the modes. "
|
|
270
270
|
"Because these matrices are diagonal, the eigenvalues are just the diagonal entries. "
|
|
271
271
|
"The spectral radius is the maximum modulus of the eigenvalues: if it is greater than 1 a mode grows, "
|
|
272
272
|
"if it equals 1 the modes remain bounded without decaying, and if it is less than 1 all modes decay to zero. "
|
|
273
|
-
"Here the diagonal entries give radii 2, 1, and 0 respectively, which explains the three classifications.
|
|
274
|
-
|
|
275
|
-
"
|
|
276
|
-
"## Check
|
|
277
|
-
"
|
|
278
|
-
"C1 " ?C1 "
|
|
279
|
-
"
|
|
280
|
-
"C2 " ?C2 "
|
|
281
|
-
"
|
|
282
|
-
"C3 " ?C3 "
|
|
283
|
-
"
|
|
284
|
-
"C4 " ?C4 "
|
|
285
|
-
"
|
|
286
|
-
"C5 " ?C5 "
|
|
287
|
-
"
|
|
273
|
+
"""Here the diagonal entries give radii 2, 1, and 0 respectively, which explains the three classifications.
|
|
274
|
+
|
|
275
|
+
"""
|
|
276
|
+
"""## Check
|
|
277
|
+
"""
|
|
278
|
+
"C1 " ?C1 """
|
|
279
|
+
"""
|
|
280
|
+
"C2 " ?C2 """
|
|
281
|
+
"""
|
|
282
|
+
"C3 " ?C3 """
|
|
283
|
+
"""
|
|
284
|
+
"C4 " ?C4 """
|
|
285
|
+
"""
|
|
286
|
+
"C5 " ?C5 """
|
|
287
|
+
"""
|
|
288
288
|
) string:concatenation ?Block. }
|
|
289
289
|
=>
|
|
290
290
|
{ :report log:outputString ?Block. } .
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
}
|
|
60
60
|
=>
|
|
61
61
|
{
|
|
62
|
-
:report log:outputString "Deep Taxonomy - deep classification benchmark
|
|
62
|
+
:report log:outputString """Deep Taxonomy - deep classification benchmark
|
|
63
63
|
|
|
64
64
|
## Answer
|
|
65
65
|
The test succeeds: starting from one individual classified as N0, the rules eventually classify it as N10 and then as A2.
|
|
@@ -74,5 +74,5 @@ C3 OK - the chain reaches the midpoint N5 and still carries both side-label bran
|
|
|
74
74
|
C4 OK - the final taxonomy step from N9 to N10 was completed.
|
|
75
75
|
C5 OK - once N10 is reached, the terminal class A2 is derived.
|
|
76
76
|
C6 OK - the success flag is raised only after the terminal class A2 is present.
|
|
77
|
-
" .
|
|
77
|
+
""" .
|
|
78
78
|
} .
|
|
@@ -149,7 +149,7 @@
|
|
|
149
149
|
}
|
|
150
150
|
=>
|
|
151
151
|
{
|
|
152
|
-
:report log:outputString "Deep Taxonomy - deep classification benchmark
|
|
152
|
+
:report log:outputString """Deep Taxonomy - deep classification benchmark
|
|
153
153
|
|
|
154
154
|
## Answer
|
|
155
155
|
The test succeeds: starting from one individual classified as N0, the rules eventually classify it as N100 and then as A2.
|
|
@@ -164,5 +164,5 @@ C3 OK - the chain reaches the midpoint N50 and still carries both side-label bra
|
|
|
164
164
|
C4 OK - the final taxonomy step from N99 to N100 was completed.
|
|
165
165
|
C5 OK - once N100 is reached, the terminal class A2 is derived.
|
|
166
166
|
C6 OK - the success flag is raised only after the terminal class A2 is present.
|
|
167
|
-
" .
|
|
167
|
+
""" .
|
|
168
168
|
} .
|
|
@@ -1049,7 +1049,7 @@
|
|
|
1049
1049
|
}
|
|
1050
1050
|
=>
|
|
1051
1051
|
{
|
|
1052
|
-
:report log:outputString "Deep Taxonomy - deep classification benchmark
|
|
1052
|
+
:report log:outputString """Deep Taxonomy - deep classification benchmark
|
|
1053
1053
|
|
|
1054
1054
|
## Answer
|
|
1055
1055
|
The test succeeds: starting from one individual classified as N0, the rules eventually classify it as N1000 and then as A2.
|
|
@@ -1064,5 +1064,5 @@ C3 OK - the chain reaches the midpoint N500 and still carries both side-label br
|
|
|
1064
1064
|
C4 OK - the final taxonomy step from N999 to N1000 was completed.
|
|
1065
1065
|
C5 OK - once N1000 is reached, the terminal class A2 is derived.
|
|
1066
1066
|
C6 OK - the success flag is raised only after the terminal class A2 is present.
|
|
1067
|
-
" .
|
|
1067
|
+
""" .
|
|
1068
1068
|
} .
|
|
@@ -10049,7 +10049,7 @@
|
|
|
10049
10049
|
}
|
|
10050
10050
|
=>
|
|
10051
10051
|
{
|
|
10052
|
-
:report log:outputString "Deep Taxonomy - deep classification benchmark
|
|
10052
|
+
:report log:outputString """Deep Taxonomy - deep classification benchmark
|
|
10053
10053
|
|
|
10054
10054
|
## Answer
|
|
10055
10055
|
The test succeeds: starting from one individual classified as N0, the rules eventually classify it as N10000 and then as A2.
|
|
@@ -10064,5 +10064,5 @@ C3 OK - the chain reaches the midpoint N5000 and still carries both side-label b
|
|
|
10064
10064
|
C4 OK - the final taxonomy step from N9999 to N10000 was completed.
|
|
10065
10065
|
C5 OK - once N10000 is reached, the terminal class A2 is derived.
|
|
10066
10066
|
C6 OK - the success flag is raised only after the terminal class A2 is present.
|
|
10067
|
-
" .
|
|
10067
|
+
""" .
|
|
10068
10068
|
} .
|
package/eyeling.js
CHANGED
|
@@ -13298,7 +13298,76 @@ function lex(inputText, opts = {}) {
|
|
|
13298
13298
|
continue;
|
|
13299
13299
|
}
|
|
13300
13300
|
|
|
13301
|
-
// 5)
|
|
13301
|
+
// 5) Numeric literal (integer, decimal, or double). Turtle/N3 numeric
|
|
13302
|
+
// literals may have a leading sign and decimals may start with '.', e.g.
|
|
13303
|
+
// '+42' and '.5'. Reject repeated decimal points so '1.2.3'
|
|
13304
|
+
// cannot be misread as one impossible numeric-like token.
|
|
13305
|
+
if (
|
|
13306
|
+
isAsciiDigit(c) ||
|
|
13307
|
+
((c === '+' || c === '-') && (isAsciiDigit(peek(1)) || (peek(1) === '.' && isAsciiDigit(peek(2))))) ||
|
|
13308
|
+
(c === '.' && isAsciiDigit(peek(1)) && !isAsciiDigit(peek(-1)))
|
|
13309
|
+
) {
|
|
13310
|
+
const start = i;
|
|
13311
|
+
const numChars = [];
|
|
13312
|
+
|
|
13313
|
+
if (chars[i] === '+' || chars[i] === '-') {
|
|
13314
|
+
numChars.push(chars[i]);
|
|
13315
|
+
i++;
|
|
13316
|
+
}
|
|
13317
|
+
|
|
13318
|
+
if (chars[i] === '.') {
|
|
13319
|
+
numChars.push('.');
|
|
13320
|
+
i++;
|
|
13321
|
+
while (i < n && isAsciiDigit(chars[i])) {
|
|
13322
|
+
numChars.push(chars[i]);
|
|
13323
|
+
i++;
|
|
13324
|
+
}
|
|
13325
|
+
} else {
|
|
13326
|
+
while (i < n && isAsciiDigit(chars[i])) {
|
|
13327
|
+
numChars.push(chars[i]);
|
|
13328
|
+
i++;
|
|
13329
|
+
}
|
|
13330
|
+
if (i < n && chars[i] === '.' && i + 1 < n && isAsciiDigit(chars[i + 1])) {
|
|
13331
|
+
numChars.push('.');
|
|
13332
|
+
i++;
|
|
13333
|
+
while (i < n && isAsciiDigit(chars[i])) {
|
|
13334
|
+
numChars.push(chars[i]);
|
|
13335
|
+
i++;
|
|
13336
|
+
}
|
|
13337
|
+
}
|
|
13338
|
+
}
|
|
13339
|
+
|
|
13340
|
+
if (i < n && chars[i] === '.' && i + 1 < n && isAsciiDigit(chars[i + 1])) {
|
|
13341
|
+
throw new N3SyntaxError('Malformed numeric literal: multiple decimal points', start);
|
|
13342
|
+
}
|
|
13343
|
+
|
|
13344
|
+
// Optional exponent part: e.g., 1e0, 1.1e-3, .5E+0.
|
|
13345
|
+
if (i < n && (chars[i] === 'e' || chars[i] === 'E')) {
|
|
13346
|
+
let j = i + 1;
|
|
13347
|
+
if (j < n && (chars[j] === '+' || chars[j] === '-')) j++;
|
|
13348
|
+
if (j < n && isAsciiDigit(chars[j])) {
|
|
13349
|
+
numChars.push(chars[i]); // e/E
|
|
13350
|
+
i++;
|
|
13351
|
+
if (i < n && (chars[i] === '+' || chars[i] === '-')) {
|
|
13352
|
+
numChars.push(chars[i]);
|
|
13353
|
+
i++;
|
|
13354
|
+
}
|
|
13355
|
+
while (i < n && isAsciiDigit(chars[i])) {
|
|
13356
|
+
numChars.push(chars[i]);
|
|
13357
|
+
i++;
|
|
13358
|
+
}
|
|
13359
|
+
}
|
|
13360
|
+
}
|
|
13361
|
+
|
|
13362
|
+
if (i < n && chars[i] === '.' && i + 1 < n && isAsciiDigit(chars[i + 1])) {
|
|
13363
|
+
throw new N3SyntaxError('Malformed numeric literal: multiple decimal points', start);
|
|
13364
|
+
}
|
|
13365
|
+
|
|
13366
|
+
tokens.push(new Token('Literal', numChars.join(''), start));
|
|
13367
|
+
continue;
|
|
13368
|
+
}
|
|
13369
|
+
|
|
13370
|
+
// 6) Single-character punctuation. Use a switch rather than allocating a
|
|
13302
13371
|
// mapping object for every punctuation token in large inputs.
|
|
13303
13372
|
switch (c) {
|
|
13304
13373
|
case '{':
|
|
@@ -13419,13 +13488,17 @@ function lex(inputText, opts = {}) {
|
|
|
13419
13488
|
}
|
|
13420
13489
|
continue;
|
|
13421
13490
|
}
|
|
13491
|
+
if (cc === '\n' || cc === '\r') {
|
|
13492
|
+
throw new N3SyntaxError('Unescaped newline in short string literal', start);
|
|
13493
|
+
}
|
|
13422
13494
|
if (cc === '"') {
|
|
13423
13495
|
closed = true;
|
|
13424
13496
|
break;
|
|
13425
13497
|
}
|
|
13426
13498
|
if (sChars !== null) sChars.push(cc);
|
|
13427
13499
|
}
|
|
13428
|
-
|
|
13500
|
+
if (!closed) throw new N3SyntaxError('Unterminated short string literal "..."', start);
|
|
13501
|
+
const rawContent = sChars === null ? sliceChars(contentStart, i - 1) : sChars.join('');
|
|
13429
13502
|
const decoded = sChars === null ? rawContent : decodeN3StringEscapes(rawContent, start);
|
|
13430
13503
|
if (sChars !== null || inputMayContainInvalidStringChar) assertValidStringLiteralValue(decoded, start);
|
|
13431
13504
|
const s = JSON.stringify(decoded); // canonical short quoted form
|
|
@@ -13509,13 +13582,17 @@ function lex(inputText, opts = {}) {
|
|
|
13509
13582
|
}
|
|
13510
13583
|
continue;
|
|
13511
13584
|
}
|
|
13585
|
+
if (cc === '\n' || cc === '\r') {
|
|
13586
|
+
throw new N3SyntaxError('Unescaped newline in short string literal', start);
|
|
13587
|
+
}
|
|
13512
13588
|
if (cc === "'") {
|
|
13513
13589
|
closed = true;
|
|
13514
13590
|
break;
|
|
13515
13591
|
}
|
|
13516
13592
|
if (sChars !== null) sChars.push(cc);
|
|
13517
13593
|
}
|
|
13518
|
-
|
|
13594
|
+
if (!closed) throw new N3SyntaxError("Unterminated short string literal '...'", start);
|
|
13595
|
+
const rawContent = sChars === null ? sliceChars(contentStart, i - 1) : sChars.join('');
|
|
13519
13596
|
const decoded = sChars === null ? rawContent : decodeN3StringEscapes(rawContent, start);
|
|
13520
13597
|
if (sChars !== null || inputMayContainInvalidStringChar) assertValidStringLiteralValue(decoded, start);
|
|
13521
13598
|
const s = JSON.stringify(decoded); // canonical short quoted form
|
|
@@ -13598,52 +13675,6 @@ function lex(inputText, opts = {}) {
|
|
|
13598
13675
|
continue;
|
|
13599
13676
|
}
|
|
13600
13677
|
|
|
13601
|
-
// 6) Numeric literal (integer or float)
|
|
13602
|
-
if (isAsciiDigit(c) || (c === '-' && peek(1) !== null && isAsciiDigit(peek(1)))) {
|
|
13603
|
-
const start = i;
|
|
13604
|
-
const numChars = [c];
|
|
13605
|
-
i++;
|
|
13606
|
-
while (i < n) {
|
|
13607
|
-
const cc = chars[i];
|
|
13608
|
-
if (isAsciiDigit(cc)) {
|
|
13609
|
-
numChars.push(cc);
|
|
13610
|
-
i++;
|
|
13611
|
-
continue;
|
|
13612
|
-
}
|
|
13613
|
-
if (cc === '.') {
|
|
13614
|
-
if (i + 1 < n && isAsciiDigit(chars[i + 1])) {
|
|
13615
|
-
numChars.push('.');
|
|
13616
|
-
i++;
|
|
13617
|
-
continue;
|
|
13618
|
-
} else {
|
|
13619
|
-
break;
|
|
13620
|
-
}
|
|
13621
|
-
}
|
|
13622
|
-
break;
|
|
13623
|
-
}
|
|
13624
|
-
|
|
13625
|
-
// Optional exponent part: e.g., 1e0, 1.1e-3, 1.1E+0
|
|
13626
|
-
if (i < n && (chars[i] === 'e' || chars[i] === 'E')) {
|
|
13627
|
-
let j = i + 1;
|
|
13628
|
-
if (j < n && (chars[j] === '+' || chars[j] === '-')) j++;
|
|
13629
|
-
if (j < n && isAsciiDigit(chars[j])) {
|
|
13630
|
-
numChars.push(chars[i]); // e/E
|
|
13631
|
-
i++;
|
|
13632
|
-
if (i < n && (chars[i] === '+' || chars[i] === '-')) {
|
|
13633
|
-
numChars.push(chars[i]);
|
|
13634
|
-
i++;
|
|
13635
|
-
}
|
|
13636
|
-
while (i < n && isAsciiDigit(chars[i])) {
|
|
13637
|
-
numChars.push(chars[i]);
|
|
13638
|
-
i++;
|
|
13639
|
-
}
|
|
13640
|
-
}
|
|
13641
|
-
}
|
|
13642
|
-
|
|
13643
|
-
tokens.push(new Token('Literal', numChars.join(''), start));
|
|
13644
|
-
continue;
|
|
13645
|
-
}
|
|
13646
|
-
|
|
13647
13678
|
// 7) Identifiers / keywords / QNames
|
|
13648
13679
|
const start = i;
|
|
13649
13680
|
const word = readIdentText(start);
|
package/lib/lexer.js
CHANGED
|
@@ -1547,7 +1547,76 @@ function lex(inputText, opts = {}) {
|
|
|
1547
1547
|
continue;
|
|
1548
1548
|
}
|
|
1549
1549
|
|
|
1550
|
-
// 5)
|
|
1550
|
+
// 5) Numeric literal (integer, decimal, or double). Turtle/N3 numeric
|
|
1551
|
+
// literals may have a leading sign and decimals may start with '.', e.g.
|
|
1552
|
+
// '+42' and '.5'. Reject repeated decimal points so '1.2.3'
|
|
1553
|
+
// cannot be misread as one impossible numeric-like token.
|
|
1554
|
+
if (
|
|
1555
|
+
isAsciiDigit(c) ||
|
|
1556
|
+
((c === '+' || c === '-') && (isAsciiDigit(peek(1)) || (peek(1) === '.' && isAsciiDigit(peek(2))))) ||
|
|
1557
|
+
(c === '.' && isAsciiDigit(peek(1)) && !isAsciiDigit(peek(-1)))
|
|
1558
|
+
) {
|
|
1559
|
+
const start = i;
|
|
1560
|
+
const numChars = [];
|
|
1561
|
+
|
|
1562
|
+
if (chars[i] === '+' || chars[i] === '-') {
|
|
1563
|
+
numChars.push(chars[i]);
|
|
1564
|
+
i++;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
if (chars[i] === '.') {
|
|
1568
|
+
numChars.push('.');
|
|
1569
|
+
i++;
|
|
1570
|
+
while (i < n && isAsciiDigit(chars[i])) {
|
|
1571
|
+
numChars.push(chars[i]);
|
|
1572
|
+
i++;
|
|
1573
|
+
}
|
|
1574
|
+
} else {
|
|
1575
|
+
while (i < n && isAsciiDigit(chars[i])) {
|
|
1576
|
+
numChars.push(chars[i]);
|
|
1577
|
+
i++;
|
|
1578
|
+
}
|
|
1579
|
+
if (i < n && chars[i] === '.' && i + 1 < n && isAsciiDigit(chars[i + 1])) {
|
|
1580
|
+
numChars.push('.');
|
|
1581
|
+
i++;
|
|
1582
|
+
while (i < n && isAsciiDigit(chars[i])) {
|
|
1583
|
+
numChars.push(chars[i]);
|
|
1584
|
+
i++;
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
if (i < n && chars[i] === '.' && i + 1 < n && isAsciiDigit(chars[i + 1])) {
|
|
1590
|
+
throw new N3SyntaxError('Malformed numeric literal: multiple decimal points', start);
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
// Optional exponent part: e.g., 1e0, 1.1e-3, .5E+0.
|
|
1594
|
+
if (i < n && (chars[i] === 'e' || chars[i] === 'E')) {
|
|
1595
|
+
let j = i + 1;
|
|
1596
|
+
if (j < n && (chars[j] === '+' || chars[j] === '-')) j++;
|
|
1597
|
+
if (j < n && isAsciiDigit(chars[j])) {
|
|
1598
|
+
numChars.push(chars[i]); // e/E
|
|
1599
|
+
i++;
|
|
1600
|
+
if (i < n && (chars[i] === '+' || chars[i] === '-')) {
|
|
1601
|
+
numChars.push(chars[i]);
|
|
1602
|
+
i++;
|
|
1603
|
+
}
|
|
1604
|
+
while (i < n && isAsciiDigit(chars[i])) {
|
|
1605
|
+
numChars.push(chars[i]);
|
|
1606
|
+
i++;
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
if (i < n && chars[i] === '.' && i + 1 < n && isAsciiDigit(chars[i + 1])) {
|
|
1612
|
+
throw new N3SyntaxError('Malformed numeric literal: multiple decimal points', start);
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
tokens.push(new Token('Literal', numChars.join(''), start));
|
|
1616
|
+
continue;
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
// 6) Single-character punctuation. Use a switch rather than allocating a
|
|
1551
1620
|
// mapping object for every punctuation token in large inputs.
|
|
1552
1621
|
switch (c) {
|
|
1553
1622
|
case '{':
|
|
@@ -1668,13 +1737,17 @@ function lex(inputText, opts = {}) {
|
|
|
1668
1737
|
}
|
|
1669
1738
|
continue;
|
|
1670
1739
|
}
|
|
1740
|
+
if (cc === '\n' || cc === '\r') {
|
|
1741
|
+
throw new N3SyntaxError('Unescaped newline in short string literal', start);
|
|
1742
|
+
}
|
|
1671
1743
|
if (cc === '"') {
|
|
1672
1744
|
closed = true;
|
|
1673
1745
|
break;
|
|
1674
1746
|
}
|
|
1675
1747
|
if (sChars !== null) sChars.push(cc);
|
|
1676
1748
|
}
|
|
1677
|
-
|
|
1749
|
+
if (!closed) throw new N3SyntaxError('Unterminated short string literal "..."', start);
|
|
1750
|
+
const rawContent = sChars === null ? sliceChars(contentStart, i - 1) : sChars.join('');
|
|
1678
1751
|
const decoded = sChars === null ? rawContent : decodeN3StringEscapes(rawContent, start);
|
|
1679
1752
|
if (sChars !== null || inputMayContainInvalidStringChar) assertValidStringLiteralValue(decoded, start);
|
|
1680
1753
|
const s = JSON.stringify(decoded); // canonical short quoted form
|
|
@@ -1758,13 +1831,17 @@ function lex(inputText, opts = {}) {
|
|
|
1758
1831
|
}
|
|
1759
1832
|
continue;
|
|
1760
1833
|
}
|
|
1834
|
+
if (cc === '\n' || cc === '\r') {
|
|
1835
|
+
throw new N3SyntaxError('Unescaped newline in short string literal', start);
|
|
1836
|
+
}
|
|
1761
1837
|
if (cc === "'") {
|
|
1762
1838
|
closed = true;
|
|
1763
1839
|
break;
|
|
1764
1840
|
}
|
|
1765
1841
|
if (sChars !== null) sChars.push(cc);
|
|
1766
1842
|
}
|
|
1767
|
-
|
|
1843
|
+
if (!closed) throw new N3SyntaxError("Unterminated short string literal '...'", start);
|
|
1844
|
+
const rawContent = sChars === null ? sliceChars(contentStart, i - 1) : sChars.join('');
|
|
1768
1845
|
const decoded = sChars === null ? rawContent : decodeN3StringEscapes(rawContent, start);
|
|
1769
1846
|
if (sChars !== null || inputMayContainInvalidStringChar) assertValidStringLiteralValue(decoded, start);
|
|
1770
1847
|
const s = JSON.stringify(decoded); // canonical short quoted form
|
|
@@ -1847,52 +1924,6 @@ function lex(inputText, opts = {}) {
|
|
|
1847
1924
|
continue;
|
|
1848
1925
|
}
|
|
1849
1926
|
|
|
1850
|
-
// 6) Numeric literal (integer or float)
|
|
1851
|
-
if (isAsciiDigit(c) || (c === '-' && peek(1) !== null && isAsciiDigit(peek(1)))) {
|
|
1852
|
-
const start = i;
|
|
1853
|
-
const numChars = [c];
|
|
1854
|
-
i++;
|
|
1855
|
-
while (i < n) {
|
|
1856
|
-
const cc = chars[i];
|
|
1857
|
-
if (isAsciiDigit(cc)) {
|
|
1858
|
-
numChars.push(cc);
|
|
1859
|
-
i++;
|
|
1860
|
-
continue;
|
|
1861
|
-
}
|
|
1862
|
-
if (cc === '.') {
|
|
1863
|
-
if (i + 1 < n && isAsciiDigit(chars[i + 1])) {
|
|
1864
|
-
numChars.push('.');
|
|
1865
|
-
i++;
|
|
1866
|
-
continue;
|
|
1867
|
-
} else {
|
|
1868
|
-
break;
|
|
1869
|
-
}
|
|
1870
|
-
}
|
|
1871
|
-
break;
|
|
1872
|
-
}
|
|
1873
|
-
|
|
1874
|
-
// Optional exponent part: e.g., 1e0, 1.1e-3, 1.1E+0
|
|
1875
|
-
if (i < n && (chars[i] === 'e' || chars[i] === 'E')) {
|
|
1876
|
-
let j = i + 1;
|
|
1877
|
-
if (j < n && (chars[j] === '+' || chars[j] === '-')) j++;
|
|
1878
|
-
if (j < n && isAsciiDigit(chars[j])) {
|
|
1879
|
-
numChars.push(chars[i]); // e/E
|
|
1880
|
-
i++;
|
|
1881
|
-
if (i < n && (chars[i] === '+' || chars[i] === '-')) {
|
|
1882
|
-
numChars.push(chars[i]);
|
|
1883
|
-
i++;
|
|
1884
|
-
}
|
|
1885
|
-
while (i < n && isAsciiDigit(chars[i])) {
|
|
1886
|
-
numChars.push(chars[i]);
|
|
1887
|
-
i++;
|
|
1888
|
-
}
|
|
1889
|
-
}
|
|
1890
|
-
}
|
|
1891
|
-
|
|
1892
|
-
tokens.push(new Token('Literal', numChars.join(''), start));
|
|
1893
|
-
continue;
|
|
1894
|
-
}
|
|
1895
|
-
|
|
1896
1927
|
// 7) Identifiers / keywords / QNames
|
|
1897
1928
|
const start = i;
|
|
1898
1929
|
const word = readIdentText(start);
|
package/package.json
CHANGED
package/test/api.test.js
CHANGED
|
@@ -769,6 +769,132 @@ bad.:example a bad.:Person.
|
|
|
769
769
|
`,
|
|
770
770
|
expectError: true,
|
|
771
771
|
},
|
|
772
|
+
{
|
|
773
|
+
name: '12k2 invalid syntax: raw newline in short string literal should throw',
|
|
774
|
+
opt: { proofComments: false },
|
|
775
|
+
input: `
|
|
776
|
+
@prefix : <http://example.org/> .
|
|
777
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
778
|
+
|
|
779
|
+
:my :value "
|
|
780
|
+
123".
|
|
781
|
+
|
|
782
|
+
{
|
|
783
|
+
:my :value "\n123".
|
|
784
|
+
}
|
|
785
|
+
=>
|
|
786
|
+
{
|
|
787
|
+
:result :has :crash-syntax-11.
|
|
788
|
+
}.
|
|
789
|
+
|
|
790
|
+
{} => {
|
|
791
|
+
:test :contains :crash-syntax-11.
|
|
792
|
+
}.
|
|
793
|
+
|
|
794
|
+
{
|
|
795
|
+
:result :has :crash-syntax-11.
|
|
796
|
+
}
|
|
797
|
+
=>
|
|
798
|
+
{
|
|
799
|
+
:test :is false.
|
|
800
|
+
}.
|
|
801
|
+
`,
|
|
802
|
+
expectError: true,
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
name: '12k3 invalid syntax: repeated dots in numeric-looking literal should throw',
|
|
806
|
+
opt: { proofComments: false },
|
|
807
|
+
input: `
|
|
808
|
+
@prefix : <http://example.org/> .
|
|
809
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
810
|
+
|
|
811
|
+
:my :value 1.2.3.4.5.
|
|
812
|
+
|
|
813
|
+
{
|
|
814
|
+
:my :value 1.2.3.4.5.
|
|
815
|
+
}
|
|
816
|
+
=>
|
|
817
|
+
{
|
|
818
|
+
:result :has :crash-syntax-12.
|
|
819
|
+
}.
|
|
820
|
+
|
|
821
|
+
{} => {
|
|
822
|
+
:test :contains :crash-syntax-12.
|
|
823
|
+
}.
|
|
824
|
+
|
|
825
|
+
{
|
|
826
|
+
:result :has :crash-syntax-12.
|
|
827
|
+
}
|
|
828
|
+
=>
|
|
829
|
+
{
|
|
830
|
+
:test :is false.
|
|
831
|
+
}.
|
|
832
|
+
`,
|
|
833
|
+
expectError: true,
|
|
834
|
+
},
|
|
835
|
+
{
|
|
836
|
+
name: '12k4 success literal: signed integer shorthand parses',
|
|
837
|
+
opt: { proofComments: false },
|
|
838
|
+
input: `
|
|
839
|
+
@prefix : <http://example.org/> .
|
|
840
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
841
|
+
|
|
842
|
+
:my :value +42.
|
|
843
|
+
|
|
844
|
+
{
|
|
845
|
+
:my :value +42.
|
|
846
|
+
}
|
|
847
|
+
=>
|
|
848
|
+
{
|
|
849
|
+
:result :has :success-literal-32.
|
|
850
|
+
}.
|
|
851
|
+
|
|
852
|
+
{} => {
|
|
853
|
+
:test :contains :success-literal-32.
|
|
854
|
+
}.
|
|
855
|
+
|
|
856
|
+
{
|
|
857
|
+
:result :has :success-literal-32.
|
|
858
|
+
}
|
|
859
|
+
=>
|
|
860
|
+
{
|
|
861
|
+
:test :is true.
|
|
862
|
+
}.
|
|
863
|
+
`,
|
|
864
|
+
expect: [/:result\s+:has\s+:success-literal-32\s*\./, /:test\s+:is\s+true\s*\./],
|
|
865
|
+
},
|
|
866
|
+
{
|
|
867
|
+
name: '12k5 success literal: leading-dot decimal shorthand parses',
|
|
868
|
+
opt: { proofComments: false },
|
|
869
|
+
input: `
|
|
870
|
+
@prefix : <http://example.org/> .
|
|
871
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
872
|
+
|
|
873
|
+
:my :value .5.
|
|
874
|
+
|
|
875
|
+
{
|
|
876
|
+
:my :value .5.
|
|
877
|
+
}
|
|
878
|
+
=>
|
|
879
|
+
{
|
|
880
|
+
:result :has :success-literal-33.
|
|
881
|
+
}.
|
|
882
|
+
|
|
883
|
+
{} => {
|
|
884
|
+
:test :contains :success-literal-33.
|
|
885
|
+
}.
|
|
886
|
+
|
|
887
|
+
{
|
|
888
|
+
:result :has :success-literal-33.
|
|
889
|
+
}
|
|
890
|
+
=>
|
|
891
|
+
{
|
|
892
|
+
:test :is true.
|
|
893
|
+
}.
|
|
894
|
+
`,
|
|
895
|
+
expect: [/:result\s+:has\s+:success-literal-33\s*\./, /:test\s+:is\s+true\s*\./],
|
|
896
|
+
},
|
|
897
|
+
|
|
772
898
|
{
|
|
773
899
|
name: '12l regression: IRIREF \\u escape decodes before log:uri comparison (mismatch stays falsey)',
|
|
774
900
|
opt: { proofComments: false },
|
|
@@ -1360,7 +1486,7 @@ _:l2 rdf:rest rdf:nil.
|
|
|
1360
1486
|
odrl:action ?Ignore ;
|
|
1361
1487
|
odrl:duty [ odrl:action ?A ]
|
|
1362
1488
|
].
|
|
1363
|
-
( "%% Duty_(a,t)(action:%s)
|
|
1489
|
+
( "%% Duty_(a,t)(action:%s)\\n%% => ~Possible_(a,t)(~action:%s)\\n" ?A ?A ) string:format ?Str.
|
|
1364
1490
|
}
|
|
1365
1491
|
=>
|
|
1366
1492
|
{
|
package/test/playground.test.js
CHANGED
|
@@ -906,16 +906,30 @@ ${JSON.stringify(last, null, 2)}`);
|
|
|
906
906
|
const fuseProgram = fs.readFileSync(path.join(ROOT, 'examples', 'fuse.n3'), 'utf8');
|
|
907
907
|
const outputStringProgram = `@prefix : <#> .
|
|
908
908
|
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
909
|
-
:report log:outputString "## Hello from output string
|
|
909
|
+
:report log:outputString """## Hello from output string
|
|
910
|
+
|
|
911
|
+
Line 2 with **bold** and [Eyeling](https://example.org/eyeling)
|
|
912
|
+
""" .
|
|
910
913
|
`;
|
|
911
914
|
const riskMarkdownOutputStringProgram = `@prefix : <#> .
|
|
912
915
|
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
913
|
-
:report log:outputString "# Risk report
|
|
916
|
+
:report log:outputString """# Risk report
|
|
917
|
+
|
|
918
|
+
### Clause H1 — score 100
|
|
919
|
+
|
|
920
|
+
Risk: secondary use is permitted without a safeguard. Clause H1: Hospital may provide electronic health data for secondary use.
|
|
921
|
+
|
|
922
|
+
- **Mitigation for clause H1:** Require a permit before secondary use.
|
|
923
|
+
""" .
|
|
914
924
|
`;
|
|
915
925
|
const baseOnlyMarkdownProgram = `@base <https://raw.githubusercontent.com/eyereasoner/eyeling/refs/heads/main/examples/smoke-arithmetic.n3> .
|
|
916
926
|
@prefix : <#> .
|
|
917
927
|
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
918
|
-
:report log:outputString "# stateurl link base
|
|
928
|
+
:report log:outputString """# stateurl link base
|
|
929
|
+
|
|
930
|
+
[N3 rules](../smoke-arithmetic.n3)
|
|
931
|
+
[Input TriG](../input/smoke-arithmetic.trig)
|
|
932
|
+
""" .
|
|
919
933
|
`;
|
|
920
934
|
const logQueryTurtleProgram = `@prefix : <#> .
|
|
921
935
|
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|