links-notation 0.27.1 → 0.30.0
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 +47 -0
- package/README.ru.md +38 -0
- package/dist/index.js +492 -404
- package/index.d.ts +84 -0
- package/package.json +7 -4
- package/src/Cst.js +246 -0
- package/src/Link.js +41 -1
- package/src/Parser.js +49 -10
- package/src/ReferenceLiteral.js +22 -0
- package/src/StreamParser.js +33 -13
- package/src/comments.js +50 -11
- package/src/grammar.pegjs +19 -6
- package/src/index.js +6 -1
- package/src/parser-generated.js +183 -327
- package/src/quotes.js +13 -7
- package/src/references.js +18 -0
- package/tests/Comments.test.js +31 -0
- package/tests/Cst.test.js +109 -0
- package/tests/SharedSyntaxParity.test.js +47 -0
- package/tests/StreamParser.test.js +13 -0
- package/tests/SyntaxMetadata.test.js +102 -0
package/src/comments.js
CHANGED
|
@@ -13,23 +13,35 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { DelimitedReferences, QUOTES, readReference } from './quotes.js';
|
|
16
|
+
import { referenceLiteralEnd } from './ReferenceLiteral.js';
|
|
16
17
|
|
|
17
18
|
/** The character that starts a comment. */
|
|
18
19
|
export const COMMENT = '#';
|
|
19
20
|
|
|
20
|
-
/**
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* What ends a bare reference. A token starts after one of these, at the start
|
|
23
|
+
* of the document, and right after a delimited reference or a versioned
|
|
24
|
+
* literal: the places the grammar reads a reference from.
|
|
25
|
+
*/
|
|
26
|
+
const SEPARATORS = [' ', '\t', '\n', '\r', '(', ':', ')'];
|
|
22
27
|
|
|
23
28
|
/** What a comment may start after. */
|
|
24
29
|
const BEFORE_COMMENT = [' ', '\t', '\n', '\r'];
|
|
25
30
|
|
|
26
31
|
/**
|
|
27
32
|
* The document with every comment blanked out.
|
|
33
|
+
*
|
|
34
|
+
* The document is read token by token, the way the grammar reads it, so a `#`
|
|
35
|
+
* inside a delimited reference that starts right after a group, another
|
|
36
|
+
* reference or a literal is content, as it is in one that starts after a
|
|
37
|
+
* space. References are read in the document as written, as the grammar
|
|
38
|
+
* reads them.
|
|
28
39
|
* @param {string} document - The document to read
|
|
29
40
|
* @returns {string} The document, of the same length, without its comments
|
|
30
41
|
* @example
|
|
31
42
|
* stripComments('a: b # why\n'); // 'a: b \n'
|
|
32
43
|
* stripComments('"# kept"\n'); // '"# kept"\n'
|
|
44
|
+
* stripComments('(a)" #kept"\n'); // '(a)" #kept"\n'
|
|
33
45
|
* stripComments('issue#1047\n'); // 'issue#1047\n'
|
|
34
46
|
*/
|
|
35
47
|
export function stripComments(document) {
|
|
@@ -37,20 +49,13 @@ export function stripComments(document) {
|
|
|
37
49
|
// parse error reports are both counted in.
|
|
38
50
|
let blanked = null;
|
|
39
51
|
let position = 0;
|
|
52
|
+
// Whether a token may start at `position`.
|
|
53
|
+
let tokenStart = true;
|
|
40
54
|
const references = new DelimitedReferences(document);
|
|
41
55
|
|
|
42
56
|
while (position < document.length) {
|
|
43
57
|
const character = document[position];
|
|
44
58
|
|
|
45
|
-
if (
|
|
46
|
-
QUOTES.includes(character) &&
|
|
47
|
-
follows(document, position, BEFORE_REFERENCE)
|
|
48
|
-
) {
|
|
49
|
-
const end = references.endAt(position);
|
|
50
|
-
position = end === null ? position + 1 : end;
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
59
|
if (character === COMMENT && follows(document, position, BEFORE_COMMENT)) {
|
|
55
60
|
blanked = blanked ?? document.split('');
|
|
56
61
|
while (
|
|
@@ -64,12 +69,46 @@ export function stripComments(document) {
|
|
|
64
69
|
continue;
|
|
65
70
|
}
|
|
66
71
|
|
|
72
|
+
if (SEPARATORS.includes(character)) {
|
|
73
|
+
tokenStart = true;
|
|
74
|
+
position++;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (tokenStart) {
|
|
79
|
+
const end = delimitedTokenEnd(document, position, references);
|
|
80
|
+
if (end !== null) {
|
|
81
|
+
position = end;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Anything else is part of a bare reference, which runs to a separator.
|
|
87
|
+
tokenStart = false;
|
|
67
88
|
position++;
|
|
68
89
|
}
|
|
69
90
|
|
|
70
91
|
return blanked === null ? document : blanked.join('');
|
|
71
92
|
}
|
|
72
93
|
|
|
94
|
+
/**
|
|
95
|
+
* The position just past the delimited reference or versioned literal that
|
|
96
|
+
* starts the token at `start`, or `null` when the token there is a bare
|
|
97
|
+
* reference: a delimiter run that never closes is read as one, as the grammar
|
|
98
|
+
* reads it.
|
|
99
|
+
* @param {string} document - The document being read
|
|
100
|
+
* @param {number} start - Position where a token starts
|
|
101
|
+
* @param {DelimitedReferences} references - The references of the document
|
|
102
|
+
* @returns {number|null} The position just past the token
|
|
103
|
+
*/
|
|
104
|
+
export function delimitedTokenEnd(document, start, references) {
|
|
105
|
+
const literal = referenceLiteralEnd(document, start);
|
|
106
|
+
if (literal !== -1) {
|
|
107
|
+
return literal;
|
|
108
|
+
}
|
|
109
|
+
return QUOTES.includes(document[start]) ? references.endAt(start) : null;
|
|
110
|
+
}
|
|
111
|
+
|
|
73
112
|
/**
|
|
74
113
|
* Whether the character before the given position is one the caller allows,
|
|
75
114
|
* the start of the document counting as allowed.
|
package/src/grammar.pegjs
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
// until the stack overflows.
|
|
17
17
|
let contextDepth = 0;
|
|
18
18
|
const maxDepth = options.maxDepth ?? Infinity;
|
|
19
|
+
// The document as the caller wrote it, comments included.
|
|
20
|
+
const source = options.source ?? input;
|
|
19
21
|
// Lines already found unreadable, keyed by where they start and whether they
|
|
20
22
|
// are inside a parenthesised group. Reading a line depends on nothing else: a
|
|
21
23
|
// group starts a fresh indentation context, and indentation only decides
|
|
@@ -115,8 +117,10 @@
|
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
// Delimited references of this document, each read once however many
|
|
118
|
-
// alternatives ask for it (see quotes.js for how a reference is read)
|
|
119
|
-
|
|
120
|
+
// alternatives ask for it (see quotes.js for how a reference is read). They
|
|
121
|
+
// are read in the document as written, as the comment reader reads them, so
|
|
122
|
+
// blanking a comment never changes how far a reference reaches (issue #377).
|
|
123
|
+
const delimitedReferences = new DelimitedReferences(source);
|
|
120
124
|
|
|
121
125
|
// Universal procedural parser for N-quote strings (any N >= 1)
|
|
122
126
|
// Parses from the given position in the input string
|
|
@@ -164,7 +168,7 @@ element = &{ return !unreadableLines.has(lineKey(offset())); }
|
|
|
164
168
|
// succeed without consuming input.
|
|
165
169
|
/ &{ unreadableLines.add(lineKey(offset())); return false; } .
|
|
166
170
|
|
|
167
|
-
referenceOrLink = l:multiLineAnyLink { return l; } /
|
|
171
|
+
referenceOrLink = l:multiLineAnyLink { return l; } / r:reference { return { id: r.value, rawId: r.raw }; }
|
|
168
172
|
|
|
169
173
|
// A line that starts with a parenthesised group reads that group once and then
|
|
170
174
|
// branches on what follows it: the end of the line makes the group the whole
|
|
@@ -210,15 +214,24 @@ singleLineValueAndWhitespace = __ value:referenceOrLink { return value; }
|
|
|
210
214
|
|
|
211
215
|
singleLineValues = list:singleLineValueAndWhitespace+ { return list; }
|
|
212
216
|
|
|
213
|
-
singleLineLink = __ id:reference __ ":" v:singleLineValues { return { id: id, values: v }; }
|
|
217
|
+
singleLineLink = __ id:reference __ ":" v:singleLineValues { return { id: id.value, rawId: id.raw, values: v }; }
|
|
214
218
|
|
|
215
219
|
singleLineValueLink = v:singleLineValues { return { values: v }; }
|
|
216
220
|
|
|
217
|
-
indentedIdLink = id:reference __ ":" eol { return { id: id, values: [] }; }
|
|
221
|
+
indentedIdLink = id:reference __ ":" eol { return { id: id.value, rawId: id.raw, values: [] }; }
|
|
222
|
+
|
|
223
|
+
// A reference is read as its value together with the text it was written as,
|
|
224
|
+
// so a parsed link can say whether its ID was bare, quoted or a literal. The
|
|
225
|
+
// text is taken from the document as the caller wrote it, which keeps the same
|
|
226
|
+
// offsets as the document with its comments blanked out.
|
|
227
|
+
reference = value:referenceValue {
|
|
228
|
+
const { start, end } = range();
|
|
229
|
+
return { value: value, raw: source.slice(start, end) };
|
|
230
|
+
}
|
|
218
231
|
|
|
219
232
|
// Reference can be quoted (with any number of quotes N >= 1) or simple unquoted
|
|
220
233
|
// Universal approach: use procedural parsing for all quote types and counts
|
|
221
|
-
|
|
234
|
+
referenceValue = referenceLiteral / quotedReference / simpleReference
|
|
222
235
|
|
|
223
236
|
// Versioned literals reserve ~<version>{<UTF-8 hex>} at a reference boundary.
|
|
224
237
|
// A malformed reserved token must fail rather than fall back to a bare name.
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
export { Link, formatLinks } from './Link.js';
|
|
2
|
-
export {
|
|
2
|
+
export {
|
|
3
|
+
escapeReference,
|
|
4
|
+
unescapeReference,
|
|
5
|
+
referenceQuote,
|
|
6
|
+
} from './references.js';
|
|
7
|
+
export { CstKind, CstNode } from './Cst.js';
|
|
3
8
|
export {
|
|
4
9
|
parseIndentedDocument,
|
|
5
10
|
formatIndentedDocument,
|