@noldova/context.parsers 0.0.0-stage → 0.0.2
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/LICENSE +21 -0
- package/README.md +94 -2
- package/api/index.d.ts +1378 -0
- package/api/index.js +30 -0
- package/api/index.js.map +1 -0
- package/exceptions/invalid-offset.exception.js +31 -0
- package/exceptions/invalid-offset.exception.js.map +1 -0
- package/exceptions/invalid-span.exception.js +33 -0
- package/exceptions/invalid-span.exception.js.map +1 -0
- package/exceptions/line-out-of-bounds.exception.js +23 -0
- package/exceptions/line-out-of-bounds.exception.js.map +1 -0
- package/exceptions/parser.exception.js +14 -0
- package/exceptions/parser.exception.js.map +1 -0
- package/models/diagnostic-collector.js +31 -0
- package/models/diagnostic-collector.js.map +1 -0
- package/models/nodes/comment.node.js +14 -0
- package/models/nodes/comment.node.js.map +1 -0
- package/models/nodes/composite.node.js +16 -0
- package/models/nodes/composite.node.js.map +1 -0
- package/models/nodes/end-of-file.node.js +19 -0
- package/models/nodes/end-of-file.node.js.map +1 -0
- package/models/nodes/literal.node.js +14 -0
- package/models/nodes/literal.node.js.map +1 -0
- package/models/nodes/node.js +18 -0
- package/models/nodes/node.js.map +1 -0
- package/models/nodes/value.node.js +16 -0
- package/models/nodes/value.node.js.map +1 -0
- package/models/nodes/whitespace.node.js +14 -0
- package/models/nodes/whitespace.node.js.map +1 -0
- package/models/parser-context.js +17 -0
- package/models/parser-context.js.map +1 -0
- package/models/parser-result.js +16 -0
- package/models/parser-result.js.map +1 -0
- package/models/sources/source-line-map.js +67 -0
- package/models/sources/source-line-map.js.map +1 -0
- package/models/sources/source-line.js +31 -0
- package/models/sources/source-line.js.map +1 -0
- package/models/sources/source-position.js +23 -0
- package/models/sources/source-position.js.map +1 -0
- package/models/sources/source-span.js +20 -0
- package/models/sources/source-span.js.map +1 -0
- package/models/sources/source.js +36 -0
- package/models/sources/source.js.map +1 -0
- package/package.json +26 -4
- package/resources.js +38 -0
- package/resources.js.map +1 -0
- package/services/lexer.js +38 -0
- package/services/lexer.js.map +1 -0
- package/services/parser.js +96 -0
- package/services/parser.js.map +1 -0
- package/services/source.reader.js +93 -0
- package/services/source.reader.js.map +1 -0
- package/types/node-type.js +9 -0
- package/types/node-type.js.map +1 -0
- package/types/value-node-type.js +9 -0
- package/types/value-node-type.js.map +1 -0
package/api/index.d.ts
ADDED
|
@@ -0,0 +1,1378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) Noldova.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Diagnostic, DiagnosticTemplate } from "@noldova/context.diagnostics";
|
|
10
|
+
import type { Exception, ExceptionOptions } from "@noldova/context.exceptions";
|
|
11
|
+
import type { LineTerminator, ReadOnlyStringSpan } from "@noldova/context.text";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The base exception for failures owned by the Parsers package.
|
|
15
|
+
* Invalid language input is normally reported as diagnostics; this exception
|
|
16
|
+
* represents an invalid parser operation or a broken implementation invariant.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { ParserException } from "@noldova/context.parsers";
|
|
21
|
+
* import { ExceptionOptions } from "@noldova/context.exceptions";
|
|
22
|
+
*
|
|
23
|
+
* const cause = new Error("Lexer failed.");
|
|
24
|
+
* const failure = new ParserException("Parsing could not continue.", new ExceptionOptions(cause));
|
|
25
|
+
* console.assert(failure.cause === cause);
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare class ParserException extends Exception {
|
|
29
|
+
/**
|
|
30
|
+
* Creates a parser failure without translating its cause.
|
|
31
|
+
*
|
|
32
|
+
* @param message The failure description, preserved as supplied.
|
|
33
|
+
* @param options Optional exception context, including the original cause.
|
|
34
|
+
* @see {@link ParserException} for an example.
|
|
35
|
+
*/
|
|
36
|
+
public constructor(message: string, options?: ExceptionOptions);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Reports an absolute UTF-16 offset or permitted range that is invalid.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { InvalidOffsetException } from "@noldova/context.parsers";
|
|
45
|
+
*
|
|
46
|
+
* InvalidOffsetException.throwIfOutsideRange(5, 1, 5); // Both endpoints are valid.
|
|
47
|
+
* try {
|
|
48
|
+
* InvalidOffsetException.throwIfOutsideRange(6, 1, 5);
|
|
49
|
+
* } catch (error) {
|
|
50
|
+
* if (!(error instanceof InvalidOffsetException))
|
|
51
|
+
* throw error;
|
|
52
|
+
* console.assert(error.offset === 6 && error.maximumOffset === 5);
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare class InvalidOffsetException extends ParserException {
|
|
57
|
+
/**
|
|
58
|
+
* The rejected absolute offset.
|
|
59
|
+
*/
|
|
60
|
+
public readonly offset: number;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The inclusive minimum permitted offset.
|
|
64
|
+
*/
|
|
65
|
+
public readonly minimumOffset: number;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The inclusive maximum permitted offset.
|
|
69
|
+
*/
|
|
70
|
+
public readonly maximumOffset: number;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Records rejected offset data without validating it again.
|
|
74
|
+
*
|
|
75
|
+
* @param offset The rejected absolute UTF-16 offset; invalid numeric values are preserved.
|
|
76
|
+
* @param minimumOffset The attempted inclusive lower bound.
|
|
77
|
+
* @param maximumOffset The attempted inclusive upper bound.
|
|
78
|
+
* @param options Optional exception context, including the original cause.
|
|
79
|
+
* @see {@link InvalidOffsetException} for a guard example.
|
|
80
|
+
*/
|
|
81
|
+
public constructor(offset: number, minimumOffset: number, maximumOffset: number, options?: ExceptionOptions);
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Validates an offset against an inclusive non-negative range.
|
|
85
|
+
*
|
|
86
|
+
* @param offset The absolute UTF-16 offset to validate, including either endpoint.
|
|
87
|
+
* @param minimumOffset The inclusive lower bound; a non-negative safe integer.
|
|
88
|
+
* @param maximumOffset The inclusive upper bound; a safe integer no smaller than the lower bound.
|
|
89
|
+
* @throws {InvalidOffsetException} If any number is not a safe integer, the bounds
|
|
90
|
+
* are invalid, or the offset lies outside them.
|
|
91
|
+
* @see {@link InvalidOffsetException} for accepted and rejected examples.
|
|
92
|
+
*/
|
|
93
|
+
public static throwIfOutsideRange(offset: number, minimumOffset: number, maximumOffset: number): void;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Reports a half-open UTF-16 span that is invalid for its source.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* import { InvalidSpanException } from "@noldova/context.parsers";
|
|
102
|
+
*
|
|
103
|
+
* InvalidSpanException.throwIfInvalid(0, 5, 5);
|
|
104
|
+
* InvalidSpanException.throwIfInvalid(5, 0, 5); // Empty at the source end.
|
|
105
|
+
* try {
|
|
106
|
+
* InvalidSpanException.throwIfInvalid(4, 2, 5);
|
|
107
|
+
* } catch (error) {
|
|
108
|
+
* if (!(error instanceof InvalidSpanException))
|
|
109
|
+
* throw error;
|
|
110
|
+
* console.assert(error.startOffset === 4 && error.length === 2);
|
|
111
|
+
* }
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
export declare class InvalidSpanException extends ParserException {
|
|
115
|
+
/**
|
|
116
|
+
* The rejected absolute start offset.
|
|
117
|
+
*/
|
|
118
|
+
public readonly startOffset: number;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The rejected span length.
|
|
122
|
+
*/
|
|
123
|
+
public readonly length: number;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The target source length.
|
|
127
|
+
*/
|
|
128
|
+
public readonly sourceLength: number;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Records rejected span data without validating it again.
|
|
132
|
+
*
|
|
133
|
+
* @param startOffset The attempted absolute UTF-16 start offset.
|
|
134
|
+
* @param length The attempted number of UTF-16 code units.
|
|
135
|
+
* @param sourceLength The source length used for validation.
|
|
136
|
+
* @param options Optional exception context, including the original cause.
|
|
137
|
+
* @see {@link InvalidSpanException} for a guard example.
|
|
138
|
+
*/
|
|
139
|
+
public constructor(startOffset: number, length: number, sourceLength: number, options?: ExceptionOptions);
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Validates a half-open span, including empty spans at the source end.
|
|
143
|
+
*
|
|
144
|
+
* @param startOffset The non-negative safe-integer absolute UTF-16 start.
|
|
145
|
+
* @param length The non-negative safe-integer number of UTF-16 code units.
|
|
146
|
+
* @param sourceLength The non-negative safe-integer length of the complete source.
|
|
147
|
+
* @throws {InvalidSpanException} If an argument or derived end is not a safe
|
|
148
|
+
* integer, an argument is negative, or the span extends beyond the source.
|
|
149
|
+
* @see {@link InvalidSpanException} for boundary examples.
|
|
150
|
+
*/
|
|
151
|
+
public static throwIfInvalid(startOffset: number, length: number, sourceLength: number): void;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Reports a zero-based line index that cannot identify a line in the given count.
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```ts
|
|
159
|
+
* import { LineOutOfBoundsException } from "@noldova/context.parsers";
|
|
160
|
+
*
|
|
161
|
+
* LineOutOfBoundsException.throwIfOutOfBounds(1, 2);
|
|
162
|
+
* try {
|
|
163
|
+
* LineOutOfBoundsException.throwIfOutOfBounds(2, 2);
|
|
164
|
+
* } catch (error) {
|
|
165
|
+
* if (!(error instanceof LineOutOfBoundsException))
|
|
166
|
+
* throw error;
|
|
167
|
+
* console.assert(error.lineIndex === 2 && error.lineCount === 2);
|
|
168
|
+
* }
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
export declare class LineOutOfBoundsException extends ParserException {
|
|
172
|
+
/**
|
|
173
|
+
* The rejected zero-based line index.
|
|
174
|
+
*/
|
|
175
|
+
public readonly lineIndex: number;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* The source line count.
|
|
179
|
+
*/
|
|
180
|
+
public readonly lineCount: number;
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Records rejected line data without validating it again.
|
|
184
|
+
*
|
|
185
|
+
* @param lineIndex The attempted zero-based line index.
|
|
186
|
+
* @param lineCount The line count used for validation.
|
|
187
|
+
* @param options Optional exception context, including the original cause.
|
|
188
|
+
* @see {@link LineOutOfBoundsException} for a guard example.
|
|
189
|
+
*/
|
|
190
|
+
public constructor(lineIndex: number, lineCount: number, options?: ExceptionOptions);
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Validates that an index identifies an existing line.
|
|
194
|
+
*
|
|
195
|
+
* @param lineIndex The zero-based safe-integer index, strictly below the line count.
|
|
196
|
+
* @param lineCount The non-negative safe-integer count; zero permits no index.
|
|
197
|
+
* @throws {LineOutOfBoundsException} If either number is invalid or the index
|
|
198
|
+
* does not identify an existing line.
|
|
199
|
+
* @see {@link LineOutOfBoundsException} for boundary examples.
|
|
200
|
+
*/
|
|
201
|
+
public static throwIfOutOfBounds(lineIndex: number, lineCount: number): void;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* The runtime identity of a node type, including abstract base classes.
|
|
206
|
+
* TNode is the instance type the parser recognizes through this identity in
|
|
207
|
+
* {@link Parser.tryConsume} and {@link Parser.requireCurrentNode}; the
|
|
208
|
+
* contract requires a prototype, not a callable constructor. Grammar code
|
|
209
|
+
* recognizes a known type with `instanceof` instead.
|
|
210
|
+
*
|
|
211
|
+
* @see {@link Parser} for consumption by node type.
|
|
212
|
+
*/
|
|
213
|
+
export declare type NodeType<TNode extends Node = Node> = {
|
|
214
|
+
readonly prototype: TNode;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* A value-node class whose text is determined by its concrete type and whose
|
|
219
|
+
* constructor accepts a span and optional trivia. TNode is the created type.
|
|
220
|
+
* A class requiring a separate text argument does not satisfy this contract.
|
|
221
|
+
*
|
|
222
|
+
* @param span The absolute UTF-16 span to give the new node.
|
|
223
|
+
* @param leadingTrivia Optional language-owned trivia preceding the node.
|
|
224
|
+
* @param trailingTrivia Optional language-owned trivia following the node.
|
|
225
|
+
* @returns A new concrete value node.
|
|
226
|
+
* @example
|
|
227
|
+
* ```ts
|
|
228
|
+
* import { Node, SourceSpan, ValueNode, type ValueNodeType } from "@noldova/context.parsers";
|
|
229
|
+
*
|
|
230
|
+
* class PlusNode extends ValueNode {
|
|
231
|
+
* public constructor(span: SourceSpan, leadingTrivia?: Node, trailingTrivia?: Node) {
|
|
232
|
+
* super("+", span, leadingTrivia, trailingTrivia);
|
|
233
|
+
* }
|
|
234
|
+
* }
|
|
235
|
+
* const type: ValueNodeType<PlusNode> = PlusNode;
|
|
236
|
+
* const node = new type(new SourceSpan(0, 1));
|
|
237
|
+
* console.assert(node.value === "+");
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
export declare type ValueNodeType<TNode extends ValueNode = ValueNode> = NodeType<TNode> & (new (
|
|
241
|
+
span: SourceSpan,
|
|
242
|
+
leadingTrivia?: Node,
|
|
243
|
+
trailingTrivia?: Node) => TNode);
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* The immutable base of parser node hierarchies. Each node records its exact
|
|
247
|
+
* UTF-16 source span and optional language-owned trivia. Trivia is separate
|
|
248
|
+
* from a composite's semantic children. Nodes do not retain their parser. A
|
|
249
|
+
* language adds behavior to nodes through declaration merging on its own
|
|
250
|
+
* package; the shared node carries structure only.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```ts
|
|
254
|
+
* import { LiteralNode, Node, SourceSpan, ValueNode, WhitespaceNode } from "@noldova/context.parsers";
|
|
255
|
+
*
|
|
256
|
+
* class TextLiteralNode extends LiteralNode {
|
|
257
|
+
* public constructor(value: string, span: SourceSpan, leadingTrivia?: Node) {
|
|
258
|
+
* super(value, span, leadingTrivia);
|
|
259
|
+
* }
|
|
260
|
+
* }
|
|
261
|
+
* const trivia = new WhitespaceNode(" ", new SourceSpan(0, 1));
|
|
262
|
+
* const node: Node = new TextLiteralNode("a", new SourceSpan(1, 1), trivia);
|
|
263
|
+
* console.assert(node instanceof ValueNode);
|
|
264
|
+
* console.assert(node.leadingTrivia === trivia);
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
export declare abstract class Node {
|
|
268
|
+
/**
|
|
269
|
+
* The exact source span represented by this node.
|
|
270
|
+
*/
|
|
271
|
+
public readonly span: SourceSpan;
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Trivia preceding this node, when the owning language attaches it.
|
|
275
|
+
*/
|
|
276
|
+
public readonly leadingTrivia: Node | undefined;
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Trivia following this node, when the owning language attaches it.
|
|
280
|
+
*/
|
|
281
|
+
public readonly trailingTrivia: Node | undefined;
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Initializes the shared node state without copying spans or trivia.
|
|
285
|
+
*
|
|
286
|
+
* @param span The exact absolute UTF-16 span of the node itself.
|
|
287
|
+
* @param leadingTrivia Optional preceding trivia; omitted when the language attaches none.
|
|
288
|
+
* @param trailingTrivia Optional following trivia; omitted when the language attaches none.
|
|
289
|
+
* @see {@link Node} for a node with leading trivia.
|
|
290
|
+
*/
|
|
291
|
+
protected constructor(span: SourceSpan, leadingTrivia?: Node, trailingTrivia?: Node);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* The base of text-bearing nodes. Each concrete node defines whether its
|
|
296
|
+
* value contains source text or a decoded representation.
|
|
297
|
+
*/
|
|
298
|
+
export declare abstract class ValueNode extends Node {
|
|
299
|
+
/**
|
|
300
|
+
* The node's text value. The empty string is a valid value; the exact source
|
|
301
|
+
* spelling is identified by the node's span.
|
|
302
|
+
*/
|
|
303
|
+
public readonly value: string;
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Initializes a text-bearing node.
|
|
307
|
+
*
|
|
308
|
+
* @param value Source spelling or decoded text as defined by the concrete node; empty text is valid.
|
|
309
|
+
* @param span The exact absolute UTF-16 source span, which need not have the decoded value's length.
|
|
310
|
+
* @param leadingTrivia Optional preceding language-owned trivia.
|
|
311
|
+
* @param trailingTrivia Optional following language-owned trivia.
|
|
312
|
+
* @see {@link ValueNodeType} for a concrete subclass.
|
|
313
|
+
*/
|
|
314
|
+
protected constructor(value: string, span: SourceSpan, leadingTrivia?: Node, trailingTrivia?: Node);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* The base for genuine ordered semantic child collections, not fixed named
|
|
319
|
+
* syntax roles. The supplied array is retained; callers must not mutate it
|
|
320
|
+
* after publishing the node. Empty collections are valid.
|
|
321
|
+
*
|
|
322
|
+
* @example
|
|
323
|
+
* ```ts
|
|
324
|
+
* import { CompositeNode, LiteralNode, Node, SourceSpan } from "@noldova/context.parsers";
|
|
325
|
+
*
|
|
326
|
+
* class TextLiteralNode extends LiteralNode {
|
|
327
|
+
* public constructor(value: string, span: SourceSpan) {
|
|
328
|
+
* super(value, span);
|
|
329
|
+
* }
|
|
330
|
+
* }
|
|
331
|
+
* class SequenceNode extends CompositeNode {
|
|
332
|
+
* public constructor(children: readonly Node[], span: SourceSpan) {
|
|
333
|
+
* super(children, span);
|
|
334
|
+
* }
|
|
335
|
+
* }
|
|
336
|
+
* const leaf = new TextLiteralNode("a", new SourceSpan(0, 1));
|
|
337
|
+
* const sequence = new SequenceNode([leaf], new SourceSpan(0, 1));
|
|
338
|
+
* console.assert(sequence.children[0] === leaf);
|
|
339
|
+
* console.assert(new SequenceNode([], new SourceSpan(1, 0)).children.length === 0);
|
|
340
|
+
* ```
|
|
341
|
+
*/
|
|
342
|
+
export declare abstract class CompositeNode extends Node {
|
|
343
|
+
/**
|
|
344
|
+
* The children in source and grammar order. The supplied collection is
|
|
345
|
+
* retained without copying.
|
|
346
|
+
*/
|
|
347
|
+
public readonly children: readonly Node[];
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Initializes an ordered child collection without cloning its array or nodes.
|
|
351
|
+
*
|
|
352
|
+
* @param children The children in source and grammar order; must remain unchanged after publication.
|
|
353
|
+
* @param span The exact absolute UTF-16 source span represented by the composite.
|
|
354
|
+
* @param leadingTrivia Optional preceding trivia, separate from the semantic children.
|
|
355
|
+
* @param trailingTrivia Optional following trivia, separate from the semantic children.
|
|
356
|
+
* @see {@link CompositeNode} for populated and empty collections.
|
|
357
|
+
*/
|
|
358
|
+
protected constructor(children: readonly Node[], span: SourceSpan, leadingTrivia?: Node, trailingTrivia?: Node);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* The abstract base for language-owned literal tokens. Concrete node types
|
|
363
|
+
* identify the literal syntax and define whether their value is source text
|
|
364
|
+
* or decoded text. This base neither decodes nor validates a language's grammar.
|
|
365
|
+
*
|
|
366
|
+
* @see {@link Node} for a literal with trivia.
|
|
367
|
+
*/
|
|
368
|
+
export declare abstract class LiteralNode extends ValueNode {
|
|
369
|
+
/**
|
|
370
|
+
* Initializes a concrete language literal without normalizing its text.
|
|
371
|
+
*
|
|
372
|
+
* @param value Source spelling or decoded text as defined by the concrete
|
|
373
|
+
* node; empty text is permitted.
|
|
374
|
+
* @param span The exact absolute UTF-16 span in the original source.
|
|
375
|
+
* @param leadingTrivia Optional preceding language-owned trivia.
|
|
376
|
+
* @param trailingTrivia Optional following language-owned trivia.
|
|
377
|
+
* @see {@link Node} for an example.
|
|
378
|
+
*/
|
|
379
|
+
protected constructor(value: string, span: SourceSpan, leadingTrivia?: Node, trailingTrivia?: Node);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* The abstract base for language-owned comment syntax. The inherited value
|
|
384
|
+
* is the comment body without delimiters; whitespace, line breaks, and
|
|
385
|
+
* decorative characters are preserved. The span covers the complete original
|
|
386
|
+
* comment, including delimiters that were present. Original spelling can be
|
|
387
|
+
* recovered from the source through that span.
|
|
388
|
+
*
|
|
389
|
+
* @example
|
|
390
|
+
* ```ts
|
|
391
|
+
* import { CommentNode, Source, SourceSpan } from "@noldova/context.parsers";
|
|
392
|
+
*
|
|
393
|
+
* class LanguageCommentNode extends CommentNode {
|
|
394
|
+
* public constructor(value: string, span: SourceSpan) {
|
|
395
|
+
* super(value, span);
|
|
396
|
+
* }
|
|
397
|
+
* }
|
|
398
|
+
* const source = new Source("// note");
|
|
399
|
+
* const comment = new LanguageCommentNode(" note", new SourceSpan(0, 7));
|
|
400
|
+
* console.assert(comment.value === " note" && comment instanceof CommentNode);
|
|
401
|
+
* console.assert(source.getText(comment.span) === "// note");
|
|
402
|
+
* ```
|
|
403
|
+
*/
|
|
404
|
+
export declare abstract class CommentNode extends ValueNode {
|
|
405
|
+
/**
|
|
406
|
+
* Initializes a language-owned comment without normalizing its body.
|
|
407
|
+
*
|
|
408
|
+
* @param value The body selected by the language lexer, excluding opening
|
|
409
|
+
* and any present closing delimiters. Empty text is permitted; do not trim it.
|
|
410
|
+
* @param span The absolute UTF-16 span of the complete comment, including
|
|
411
|
+
* any delimiters that were present. Its length need not equal the body length.
|
|
412
|
+
* @param leadingTrivia Optional preceding language-owned trivia.
|
|
413
|
+
* @param trailingTrivia Optional following language-owned trivia.
|
|
414
|
+
* @see {@link CommentNode} for an example.
|
|
415
|
+
*/
|
|
416
|
+
protected constructor(value: string, span: SourceSpan, leadingTrivia?: Node, trailingTrivia?: Node);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* A generic whitespace node whose accepted characters are determined by its
|
|
421
|
+
* language lexer; construction does not classify or normalize its text.
|
|
422
|
+
*
|
|
423
|
+
* @see {@link Node} for a whitespace node used as leading trivia.
|
|
424
|
+
*/
|
|
425
|
+
export declare class WhitespaceNode extends ValueNode {
|
|
426
|
+
/**
|
|
427
|
+
* Creates a whitespace node without normalizing its text.
|
|
428
|
+
*
|
|
429
|
+
* @param value The text preserved by the language lexer; empty text is permitted.
|
|
430
|
+
* @param span The exact absolute UTF-16 span in the original source.
|
|
431
|
+
* @param leadingTrivia Optional preceding language-owned trivia.
|
|
432
|
+
* @param trailingTrivia Optional following language-owned trivia.
|
|
433
|
+
* @see {@link Node} for an example.
|
|
434
|
+
*/
|
|
435
|
+
public constructor(value: string, span: SourceSpan, leadingTrivia?: Node, trailingTrivia?: Node);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* A zero-length marker at the end of a lexer's reader region.
|
|
440
|
+
* For embedded input this can precede the end of the complete source.
|
|
441
|
+
* The node validates its length; the lexer validates its position.
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* ```ts
|
|
445
|
+
* import { EndOfFileNode, SourceSpan } from "@noldova/context.parsers";
|
|
446
|
+
*
|
|
447
|
+
* const end = new EndOfFileNode(new SourceSpan(3, 0));
|
|
448
|
+
* console.assert(end.span.isEmpty && end.span.startOffset === 3);
|
|
449
|
+
* ```
|
|
450
|
+
*/
|
|
451
|
+
export declare class EndOfFileNode extends Node {
|
|
452
|
+
/**
|
|
453
|
+
* Creates an end marker, optionally carrying final trivia.
|
|
454
|
+
*
|
|
455
|
+
* @param span A zero-length absolute UTF-16 span at the reader's end.
|
|
456
|
+
* @param leadingTrivia Optional trivia consumed before the end marker.
|
|
457
|
+
* @param trailingTrivia Optional language-owned trailing trivia.
|
|
458
|
+
* @throws {ArgumentException} If the span is not empty.
|
|
459
|
+
* @see {@link EndOfFileNode} for an end marker.
|
|
460
|
+
*/
|
|
461
|
+
public constructor(span: SourceSpan, leadingTrivia?: Node, trailingTrivia?: Node);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* An immutable half-open source span with no source identity or retained text.
|
|
466
|
+
* Offsets count UTF-16 code units, not Unicode code points. The span trusts
|
|
467
|
+
* its arguments: the reader that creates spans during lexing guarantees them,
|
|
468
|
+
* and a source or reader that accepts a span from elsewhere checks its bounds.
|
|
469
|
+
*
|
|
470
|
+
* @example
|
|
471
|
+
* ```ts
|
|
472
|
+
* import { SourceSpan } from "@noldova/context.parsers";
|
|
473
|
+
*
|
|
474
|
+
* const span = new SourceSpan(2, 3);
|
|
475
|
+
* console.assert(span.endOffset === 5 && !span.isEmpty);
|
|
476
|
+
* const empty = new SourceSpan(5, 0);
|
|
477
|
+
* console.assert(empty.startOffset === empty.endOffset && empty.isEmpty);
|
|
478
|
+
* ```
|
|
479
|
+
*/
|
|
480
|
+
export declare class SourceSpan {
|
|
481
|
+
/**
|
|
482
|
+
* The zero-based UTF-16 offset at which the span begins.
|
|
483
|
+
*/
|
|
484
|
+
public readonly startOffset: number;
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* The number of UTF-16 code units in the span.
|
|
488
|
+
*/
|
|
489
|
+
public readonly length: number;
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* The exclusive UTF-16 end offset.
|
|
493
|
+
*/
|
|
494
|
+
public readonly endOffset: number;
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* True when the span has zero length.
|
|
498
|
+
*/
|
|
499
|
+
public readonly isEmpty: boolean;
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Creates an offset-only span.
|
|
503
|
+
*
|
|
504
|
+
* @param startOffset The absolute UTF-16 start.
|
|
505
|
+
* @param length The number of UTF-16 code units.
|
|
506
|
+
* @see {@link SourceSpan} for non-empty and empty spans.
|
|
507
|
+
*/
|
|
508
|
+
public constructor(startOffset: number, length: number);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Immutable metadata for one source line, excluding its line terminator from
|
|
513
|
+
* the content span. The model does not retain a source or classify terminators.
|
|
514
|
+
*
|
|
515
|
+
* @example
|
|
516
|
+
* ```ts
|
|
517
|
+
* import { SourceLine } from "@noldova/context.parsers";
|
|
518
|
+
*
|
|
519
|
+
* const line = new SourceLine(0, 0, 3, 2); // "abc" followed by CRLF.
|
|
520
|
+
* console.assert(line.length === 3 && line.lineBreakLength === 2);
|
|
521
|
+
* ```
|
|
522
|
+
*/
|
|
523
|
+
export declare class SourceLine {
|
|
524
|
+
/**
|
|
525
|
+
* The zero-based index of the line within its source.
|
|
526
|
+
*/
|
|
527
|
+
public readonly lineIndex: number;
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* The absolute UTF-16 offset at which the line begins.
|
|
531
|
+
*/
|
|
532
|
+
public readonly startOffset: number;
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* The exclusive absolute offset of the line content, before its separator.
|
|
536
|
+
*/
|
|
537
|
+
public readonly endOffset: number;
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* The number of UTF-16 code units occupied by the line separator.
|
|
541
|
+
*/
|
|
542
|
+
public readonly lineBreakLength: 0 | 1 | 2;
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* The number of UTF-16 code units in the line, excluding its separator.
|
|
546
|
+
*/
|
|
547
|
+
public readonly length: number;
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Creates the metadata for one line.
|
|
551
|
+
*
|
|
552
|
+
* @param lineIndex The non-negative safe-integer zero-based line index.
|
|
553
|
+
* @param startOffset The non-negative safe-integer absolute UTF-16 content start.
|
|
554
|
+
* @param endOffset The safe-integer exclusive content end, no smaller than the start.
|
|
555
|
+
* @param lineBreakLength The terminator width in UTF-16 code units: zero for none, otherwise one or two.
|
|
556
|
+
* @throws {ArgumentOutOfRangeException} If the line index or content offsets are
|
|
557
|
+
* invalid, including an end before the start.
|
|
558
|
+
* @see {@link SourceLine} for CRLF metadata.
|
|
559
|
+
*/
|
|
560
|
+
public constructor(lineIndex: number, startOffset: number, endOffset: number, lineBreakLength: 0 | 1 | 2);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* An immutable zero-based line and UTF-16 character coordinate.
|
|
565
|
+
* This model validates the numbers; {@link SourceLineMap} checks whether the
|
|
566
|
+
* position exists in a particular source.
|
|
567
|
+
*
|
|
568
|
+
* @see {@link SourceLineMap} for offset/position conversion.
|
|
569
|
+
*/
|
|
570
|
+
export declare class SourcePosition {
|
|
571
|
+
/**
|
|
572
|
+
* The zero-based line index.
|
|
573
|
+
*/
|
|
574
|
+
public readonly lineIndex: number;
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* The zero-based UTF-16 character index from the start of the line.
|
|
578
|
+
*/
|
|
579
|
+
public readonly characterIndex: number;
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Creates line-relative coordinates without binding them to a source.
|
|
583
|
+
*
|
|
584
|
+
* @param lineIndex The non-negative safe-integer zero-based line index.
|
|
585
|
+
* @param characterIndex The non-negative safe-integer UTF-16 offset from the line's start.
|
|
586
|
+
* @throws {ArgumentOutOfRangeException} If either coordinate is negative or not a safe integer.
|
|
587
|
+
* @see {@link SourceLineMap} for a conversion example.
|
|
588
|
+
*/
|
|
589
|
+
public constructor(lineIndex: number, characterIndex: number);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* An immutable source buffer with an optional caller-supplied logical identity.
|
|
594
|
+
* Offsets count UTF-16 code units. A source need not correspond to a file,
|
|
595
|
+
* and this package neither resolves nor rewrites its identity.
|
|
596
|
+
*
|
|
597
|
+
* @example
|
|
598
|
+
* ```ts
|
|
599
|
+
* import { Source, SourceSpan } from "@noldova/context.parsers";
|
|
600
|
+
*
|
|
601
|
+
* const source = new Source("A😀B");
|
|
602
|
+
* console.assert(source.identity === undefined && source.length === 4);
|
|
603
|
+
* const view = source.getSpan(new SourceSpan(1, 2));
|
|
604
|
+
* console.assert(view.toString() === "😀");
|
|
605
|
+
* console.assert(source.getText(1, 2) === "😀");
|
|
606
|
+
* console.assert(source.getText(new SourceSpan(4, 0)) === "");
|
|
607
|
+
* ```
|
|
608
|
+
*/
|
|
609
|
+
export declare class Source {
|
|
610
|
+
/**
|
|
611
|
+
* The complete immutable source text.
|
|
612
|
+
*/
|
|
613
|
+
public readonly content: string;
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* The caller-supplied canonical logical identity, when the source has one.
|
|
617
|
+
*/
|
|
618
|
+
public readonly identity: string | undefined;
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* The number of UTF-16 code units in the source.
|
|
622
|
+
*/
|
|
623
|
+
public readonly length: number;
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Creates a complete source buffer without normalizing its content.
|
|
627
|
+
*
|
|
628
|
+
* @param content The exact input text, including any line terminators; empty input is valid.
|
|
629
|
+
* @param identity Optional non-empty, non-whitespace logical identity; omitted for anonymous input.
|
|
630
|
+
* @throws {ArgumentException} If a supplied identity is empty or whitespace-only.
|
|
631
|
+
* @see {@link Source} for anonymous input and slicing.
|
|
632
|
+
*/
|
|
633
|
+
public constructor(content: string, identity?: string);
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Creates a view of a half-open range without copying its text.
|
|
637
|
+
*
|
|
638
|
+
* @param startOffset The non-negative safe-integer absolute UTF-16 start, up to the source length.
|
|
639
|
+
* @param length The non-negative safe-integer width; the range must fit entirely in this source.
|
|
640
|
+
* @returns A new {@link ReadOnlyStringSpan} retaining the source string,
|
|
641
|
+
* including an empty view when length is zero.
|
|
642
|
+
* @throws {InvalidSpanException} If either number or the resulting range is invalid.
|
|
643
|
+
* @see {@link Source} for slicing examples.
|
|
644
|
+
*/
|
|
645
|
+
public getSpan(startOffset: number, length: number): ReadOnlyStringSpan;
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Creates a view of an existing offset-only span.
|
|
649
|
+
*
|
|
650
|
+
* @param span The absolute UTF-16 span, wholly contained in this source.
|
|
651
|
+
* @returns A new view retaining the source string without copying its text;
|
|
652
|
+
* an empty span at the source end is valid.
|
|
653
|
+
* @throws {InvalidSpanException} If the span extends beyond this source.
|
|
654
|
+
* @see {@link Source} for slicing examples.
|
|
655
|
+
*/
|
|
656
|
+
public getSpan(span: SourceSpan): ReadOnlyStringSpan;
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Returns the text selected by a half-open range.
|
|
660
|
+
*
|
|
661
|
+
* @param startOffset The non-negative safe-integer absolute UTF-16 start, up to the source length.
|
|
662
|
+
* @param length The non-negative safe-integer width; the range must fit entirely in this source.
|
|
663
|
+
* @returns The selected string, or an empty string for a zero-length range.
|
|
664
|
+
* @throws {InvalidSpanException} If either number or the resulting range is invalid.
|
|
665
|
+
* @see {@link Source} for slicing examples.
|
|
666
|
+
*/
|
|
667
|
+
public getText(startOffset: number, length: number): string;
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Returns the text selected by an offset-only span.
|
|
671
|
+
*
|
|
672
|
+
* @param span The absolute UTF-16 span, wholly contained in this source.
|
|
673
|
+
* @returns The selected string; an empty span at the source end returns an empty string.
|
|
674
|
+
* @throws {InvalidSpanException} If the span extends beyond this source.
|
|
675
|
+
* @see {@link Source} for slicing examples.
|
|
676
|
+
*/
|
|
677
|
+
public getText(span: SourceSpan): string;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* Maps one immutable source between absolute UTF-16 offsets and zero-based
|
|
682
|
+
* line coordinates under the supplied line-terminator policy. Line metadata
|
|
683
|
+
* is computed once at construction. An empty source has one
|
|
684
|
+
* empty line; a trailing terminator introduces a final empty line.
|
|
685
|
+
*
|
|
686
|
+
* @example
|
|
687
|
+
* ```ts
|
|
688
|
+
* import { Source, SourceLineMap, SourcePosition } from "@noldova/context.parsers";
|
|
689
|
+
* import { EcmaScriptLineTerminator } from "@noldova/context.text";
|
|
690
|
+
*
|
|
691
|
+
* const map = new SourceLineMap(new Source("a\r\nb\n"), new EcmaScriptLineTerminator());
|
|
692
|
+
* console.assert(map.lineCount === 3 && map.getLineText(2) === "");
|
|
693
|
+
* console.assert(map.getLineSpan(0).toString() === "a");
|
|
694
|
+
* const position = map.getPosition(2); // The LF within CRLF belongs to line zero.
|
|
695
|
+
* console.assert(position.lineIndex === 0 && position.characterIndex === 2);
|
|
696
|
+
* console.assert(map.getOffset(position) === 2);
|
|
697
|
+
* console.assert(map.getOffset(new SourcePosition(2, 0)) === 5); // End of source.
|
|
698
|
+
* ```
|
|
699
|
+
*/
|
|
700
|
+
export declare class SourceLineMap {
|
|
701
|
+
/**
|
|
702
|
+
* Maps the lines of a source under its language's line-terminator policy.
|
|
703
|
+
*
|
|
704
|
+
* @param source The complete immutable source whose lines are mapped.
|
|
705
|
+
* @param lineTerminator The policy used to identify terminator widths during construction.
|
|
706
|
+
* @see {@link SourceLineMap} for a CRLF and trailing-line example.
|
|
707
|
+
*/
|
|
708
|
+
public constructor(source: Source, lineTerminator: LineTerminator);
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* The number of lines. An empty source contains one empty line.
|
|
712
|
+
*/
|
|
713
|
+
public get lineCount(): number;
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* Line metadata in source order under the selected terminator rules.
|
|
717
|
+
*/
|
|
718
|
+
public readonly lines: readonly SourceLine[];
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Creates a view of one line's content, excluding its terminator.
|
|
722
|
+
*
|
|
723
|
+
* @param lineIndex The zero-based safe-integer index of an existing line.
|
|
724
|
+
* @returns A new view retaining the source string; empty lines produce empty views.
|
|
725
|
+
* @throws {LineOutOfBoundsException} If the index is invalid or no such line exists.
|
|
726
|
+
* @see {@link SourceLineMap} for line access examples.
|
|
727
|
+
*/
|
|
728
|
+
public getLineSpan(lineIndex: number): ReadOnlyStringSpan;
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Returns one line's content without its terminator.
|
|
732
|
+
*
|
|
733
|
+
* @param lineIndex The zero-based safe-integer index of an existing line.
|
|
734
|
+
* @returns The line text, or an empty string for an empty line.
|
|
735
|
+
* @throws {LineOutOfBoundsException} If the index is invalid or no such line exists.
|
|
736
|
+
* @see {@link SourceLineMap} for line access examples.
|
|
737
|
+
*/
|
|
738
|
+
public getLineText(lineIndex: number): string;
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Converts an absolute UTF-16 offset to zero-based line coordinates.
|
|
742
|
+
*
|
|
743
|
+
* @param offset A safe-integer offset from zero through the complete source length, inclusive.
|
|
744
|
+
* @returns A new position. Terminator code units belong to the line they end;
|
|
745
|
+
* the source end is representable, including the final empty line when present.
|
|
746
|
+
* @throws {InvalidOffsetException} If the offset is invalid or outside the source.
|
|
747
|
+
* @see {@link SourceLineMap} for CRLF and end-position examples.
|
|
748
|
+
*/
|
|
749
|
+
public getPosition(offset: number): SourcePosition;
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Converts line coordinates to an absolute UTF-16 offset.
|
|
753
|
+
*
|
|
754
|
+
* @param position An existing line and character position. The character index
|
|
755
|
+
* may address a terminator code unit or the end of the final line, but not the next line.
|
|
756
|
+
* @returns The absolute source offset, including the source end when selected.
|
|
757
|
+
* @throws {LineOutOfBoundsException} If the line does not exist.
|
|
758
|
+
* @throws {ArgumentOutOfRangeException} If the character index extends beyond the selected line's permitted positions.
|
|
759
|
+
* @see {@link SourceLineMap} for round-trip conversion.
|
|
760
|
+
*/
|
|
761
|
+
public getOffset(position: SourcePosition): number;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* A mutable UTF-16 cursor over an immutable source or a bounded source region.
|
|
766
|
+
* Offsets and spans remain absolute within the complete source. End-of-input
|
|
767
|
+
* means this reader's end, not necessarily the complete source's end. Reads at
|
|
768
|
+
* the end return the null character and code point 0 instead of a separate
|
|
769
|
+
* marker, so a lexer compares characters only; because a null character is
|
|
770
|
+
* also ordinary input, {@link SourceReader.isAtEnd} decides where that
|
|
771
|
+
* matters. Positions handed back to the reader, through seek, advance, a
|
|
772
|
+
* relative peek, or span creation, come from the reader itself and are not
|
|
773
|
+
* validated again; the constructor validates the region it is given.
|
|
774
|
+
* Language-specific trivia recognition belongs to the language lexer.
|
|
775
|
+
*
|
|
776
|
+
* @example
|
|
777
|
+
* ```ts
|
|
778
|
+
* import { Source, SourceReader, SourceSpan } from "@noldova/context.parsers";
|
|
779
|
+
*
|
|
780
|
+
* const reader = new SourceReader(new Source("xA😀By"), new SourceSpan(1, 4));
|
|
781
|
+
* console.assert(reader.peekCodeUnit() === "A");
|
|
782
|
+
* console.assert(reader.peekCodeUnit(-1) === "\0"); // Before the region.
|
|
783
|
+
* console.assert(reader.readCodeUnit() === "A");
|
|
784
|
+
* console.assert(reader.peekCodePoint() === 0x1F600);
|
|
785
|
+
* console.assert(reader.readCodePoint() === 0x1F600 && reader.currentOffset === 4);
|
|
786
|
+
* console.assert(reader.startsWith("B") && !reader.startsWith("By"));
|
|
787
|
+
* console.assert(reader.peekWhile(t => t === "B").toString() === "B");
|
|
788
|
+
* console.assert(reader.readUntil(t => t === "B").toString() === "");
|
|
789
|
+
* console.assert(reader.readWhile(t => t === "B").toString() === "B");
|
|
790
|
+
* console.assert(reader.isAtEnd && reader.readCodeUnit() === "\0");
|
|
791
|
+
* reader.seek(1);
|
|
792
|
+
* reader.advance(100); // Stops at region end (5), not source end (6).
|
|
793
|
+
* console.assert(reader.currentOffset === 5);
|
|
794
|
+
* console.assert(reader.createSpan(1, 4).endOffset === 5);
|
|
795
|
+
* console.assert(reader.createSpan(new SourceSpan(1, 1), new SourceSpan(4, 1)).length === 4);
|
|
796
|
+
* ```
|
|
797
|
+
*/
|
|
798
|
+
export declare class SourceReader {
|
|
799
|
+
/**
|
|
800
|
+
* The absolute exclusive end offset of this reader's source region.
|
|
801
|
+
*/
|
|
802
|
+
public readonly endOffset: number;
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* The immutable source traversed by this reader.
|
|
806
|
+
*/
|
|
807
|
+
public readonly source: Source;
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* The absolute start offset of this reader's source region.
|
|
811
|
+
*/
|
|
812
|
+
public readonly startOffset: number;
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* Creates a reader at the start of the complete source or a bounded region.
|
|
816
|
+
*
|
|
817
|
+
* @param source The immutable buffer to traverse.
|
|
818
|
+
* @param span Optional contained absolute UTF-16 region; omitted to use the complete source.
|
|
819
|
+
* @throws {InvalidSpanException} If the region extends beyond the source.
|
|
820
|
+
* @see {@link SourceReader} for bounded navigation.
|
|
821
|
+
*/
|
|
822
|
+
public constructor(source: Source, span?: SourceSpan);
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* The current zero-based UTF-16 offset.
|
|
826
|
+
*/
|
|
827
|
+
public get currentOffset(): number;
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* True when the cursor is at the end of the reader's source region.
|
|
831
|
+
*/
|
|
832
|
+
public get isAtEnd(): boolean;
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Reads the current UTF-16 code unit without moving the cursor.
|
|
836
|
+
*
|
|
837
|
+
* @returns A one-code-unit string, including a lone surrogate or null character;
|
|
838
|
+
* the null character at the reader's end.
|
|
839
|
+
* @see {@link SourceReader} for bounded navigation.
|
|
840
|
+
*/
|
|
841
|
+
public peekCodeUnit(): string;
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* Reads a Unicode code point without moving the cursor.
|
|
845
|
+
*
|
|
846
|
+
* @returns A numeric code point; 0 at the reader's end. Valid
|
|
847
|
+
* surrogate pairs combine only when both code units are inside the region;
|
|
848
|
+
* isolated surrogates return their code-unit values.
|
|
849
|
+
* @see {@link SourceReader} for a supplementary character.
|
|
850
|
+
*/
|
|
851
|
+
public peekCodePoint(): number;
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* Reads a code unit at a relative offset without moving the cursor.
|
|
855
|
+
*
|
|
856
|
+
* @param relativeOffset A UTF-16 distance from the cursor; negative values look backward.
|
|
857
|
+
* @returns A one-code-unit string; the null character outside the reader's region.
|
|
858
|
+
* @see {@link SourceReader} for a lookup before the region.
|
|
859
|
+
*/
|
|
860
|
+
public peekCodeUnit(relativeOffset: number): string;
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* Reads one UTF-16 code unit and advances when input remains.
|
|
864
|
+
*
|
|
865
|
+
* @returns A one-code-unit string. At the reader's end returns the null
|
|
866
|
+
* character without moving.
|
|
867
|
+
* @see {@link SourceReader} for bounded consumption.
|
|
868
|
+
*/
|
|
869
|
+
public readCodeUnit(): string;
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Reads a Unicode code point and advances by its UTF-16 width.
|
|
873
|
+
*
|
|
874
|
+
* @returns A numeric code point; paired surrogates consume two code units and
|
|
875
|
+
* isolated surrogates consume one. Pairs never cross the region boundary.
|
|
876
|
+
* At the reader's end returns 0 without moving.
|
|
877
|
+
* @see {@link SourceReader} for a supplementary character.
|
|
878
|
+
*/
|
|
879
|
+
public readCodePoint(): number;
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* Moves forward, stopping at this reader's end.
|
|
883
|
+
*
|
|
884
|
+
* @param count The non-negative UTF-16 distance; defaults to one. Zero leaves
|
|
885
|
+
* the cursor unchanged, and a larger distance is clamped to the region end.
|
|
886
|
+
* @see {@link SourceReader} for clamped movement.
|
|
887
|
+
*/
|
|
888
|
+
public advance(count?: number): void;
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* Moves directly to an absolute position in this reader's region.
|
|
892
|
+
*
|
|
893
|
+
* @param offset A UTF-16 offset between startOffset and endOffset, inclusive,
|
|
894
|
+
* previously observed from this reader or from a span it created.
|
|
895
|
+
* @see {@link SourceReader} for repositioning.
|
|
896
|
+
*/
|
|
897
|
+
public seek(offset: number): void;
|
|
898
|
+
|
|
899
|
+
/**
|
|
900
|
+
* Checks the text at the cursor without moving or crossing the region boundary.
|
|
901
|
+
*
|
|
902
|
+
* @param text The exact UTF-16 sequence to match; an empty string matches even at the reader's end.
|
|
903
|
+
* @returns True if the complete sequence fits and matches at the cursor; otherwise false.
|
|
904
|
+
* @see {@link SourceReader} for matches at the bounded end.
|
|
905
|
+
*/
|
|
906
|
+
public startsWith(text: string): boolean;
|
|
907
|
+
|
|
908
|
+
/**
|
|
909
|
+
* Consumes code units while the predicate accepts them.
|
|
910
|
+
*
|
|
911
|
+
* @param predicate A synchronous code-unit predicate; true consumes the current unit.
|
|
912
|
+
* It must not reposition this reader. It is not called at the reader's end.
|
|
913
|
+
* @returns A new view of the consumed text, retaining the source string;
|
|
914
|
+
* an initial rejection or exhausted reader yields an empty view.
|
|
915
|
+
* @remarks Predicate exceptions propagate, retaining progress already made.
|
|
916
|
+
* @see {@link SourceReader} for scanning examples.
|
|
917
|
+
*/
|
|
918
|
+
public readWhile(predicate: (character: string) => boolean): ReadOnlyStringSpan;
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* Consumes code units until the predicate identifies a stopping unit.
|
|
922
|
+
*
|
|
923
|
+
* @param predicate A synchronous code-unit predicate; true stops before consuming that unit.
|
|
924
|
+
* It must not reposition this reader. It is not called at the reader's end.
|
|
925
|
+
* @returns A new view of the consumed text, excluding the stopping unit and
|
|
926
|
+
* retaining the source string. An immediate match yields an empty view.
|
|
927
|
+
* @remarks Predicate exceptions propagate, retaining progress already made.
|
|
928
|
+
* @see {@link SourceReader} for a stopping unit left unconsumed.
|
|
929
|
+
*/
|
|
930
|
+
public readUntil(predicate: (character: string) => boolean): ReadOnlyStringSpan;
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Scans matching code units without moving the cursor.
|
|
934
|
+
*
|
|
935
|
+
* @param predicate A synchronous code-unit predicate; true includes the unit.
|
|
936
|
+
* It must not reposition this reader. It is not called beyond the region.
|
|
937
|
+
* @returns A new source-string view of the matching prefix, empty on immediate
|
|
938
|
+
* rejection or at the reader's end. The cursor remains unchanged.
|
|
939
|
+
* @remarks Predicate exceptions propagate without navigation by this method.
|
|
940
|
+
* @see {@link SourceReader} for a non-consuming scan.
|
|
941
|
+
*/
|
|
942
|
+
public peekWhile(predicate: (character: string) => boolean): ReadOnlyStringSpan;
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* Creates an offset-only span contained in this reader's region without moving.
|
|
946
|
+
*
|
|
947
|
+
* @param startOffset The absolute UTF-16 start, inside this reader's region.
|
|
948
|
+
* @param length The width of the span, ending inside this reader's region.
|
|
949
|
+
* @returns A new {@link SourceSpan}; zero-length spans at either region boundary are valid.
|
|
950
|
+
* @see {@link SourceReader} for a bounded span.
|
|
951
|
+
*/
|
|
952
|
+
public createSpan(startOffset: number, length: number): SourceSpan;
|
|
953
|
+
|
|
954
|
+
/**
|
|
955
|
+
* Creates a span from the first span's start to the second span's end.
|
|
956
|
+
* This uses those two endpoints; it does not compute a union of arbitrary spans.
|
|
957
|
+
*
|
|
958
|
+
* @param startSpan Supplies the absolute UTF-16 start offset.
|
|
959
|
+
* @param endSpan Supplies the exclusive end offset, which must not precede the selected start.
|
|
960
|
+
* @returns A new offset-only span contained in this reader's region, without moving the cursor.
|
|
961
|
+
* @see {@link SourceReader} for endpoint-based span construction.
|
|
962
|
+
*/
|
|
963
|
+
public createSpan(startSpan: SourceSpan, endSpan: SourceSpan): SourceSpan;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
/**
|
|
967
|
+
* Collects diagnostic occurrences for one source in production order.
|
|
968
|
+
* Lexing and parsing share this collector; published parser results copy
|
|
969
|
+
* the sequence, not the individual diagnostics.
|
|
970
|
+
*
|
|
971
|
+
* @example
|
|
972
|
+
* ```ts
|
|
973
|
+
* import { DiagnosticAuthority, DiagnosticCategory, DiagnosticCode, DiagnosticTemplate } from "@noldova/context.diagnostics";
|
|
974
|
+
* import { DiagnosticCollector, Source, SourceSpan } from "@noldova/context.parsers";
|
|
975
|
+
*
|
|
976
|
+
* const collector = new DiagnosticCollector(new Source("x", "sample.ts"));
|
|
977
|
+
* const template = new DiagnosticTemplate(
|
|
978
|
+
* new DiagnosticCode(DiagnosticAuthority.context, 1), DiagnosticCategory.Error, "Expected {0}.");
|
|
979
|
+
* collector.add(template, new SourceSpan(0, 1), ["name"]);
|
|
980
|
+
* console.assert(collector.diagnostics[0]?.message.text === "Expected name.");
|
|
981
|
+
* console.assert(collector.diagnostics[0]?.location?.sourceIdentity === "sample.ts");
|
|
982
|
+
* const occurrence = template.create(undefined, ["value"]);
|
|
983
|
+
* collector.add(occurrence);
|
|
984
|
+
* const view = collector.diagnostics;
|
|
985
|
+
* collector.truncate(1);
|
|
986
|
+
* console.assert(view.length === 1); // Live view, not a snapshot.
|
|
987
|
+
* ```
|
|
988
|
+
*/
|
|
989
|
+
export declare class DiagnosticCollector {
|
|
990
|
+
/**
|
|
991
|
+
* Associates template-based diagnostics with one source.
|
|
992
|
+
*
|
|
993
|
+
* @param source The immutable source supplying logical identity and bounds.
|
|
994
|
+
* @see {@link DiagnosticCollector} for positioned and prebuilt diagnostics.
|
|
995
|
+
*/
|
|
996
|
+
public constructor(source: Source);
|
|
997
|
+
|
|
998
|
+
/**
|
|
999
|
+
* A read-only view of the current sequence. It reflects later additions and
|
|
1000
|
+
* lookahead rollback; it is not a published snapshot.
|
|
1001
|
+
*/
|
|
1002
|
+
public get diagnostics(): readonly Diagnostic[];
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* Appends an existing occurrence without changing its location or identity.
|
|
1006
|
+
*
|
|
1007
|
+
* @param diagnostic The complete occurrence to retain; repeated occurrences are not deduplicated.
|
|
1008
|
+
* @see {@link DiagnosticCollector} for appending an unpositioned occurrence.
|
|
1009
|
+
*/
|
|
1010
|
+
public add(diagnostic: Diagnostic): void;
|
|
1011
|
+
|
|
1012
|
+
/**
|
|
1013
|
+
* Creates and appends a positioned occurrence from a reusable template.
|
|
1014
|
+
*
|
|
1015
|
+
* @param template The diagnostic code, category, message template, and default tags.
|
|
1016
|
+
* @param span The absolute UTF-16 location, wholly contained in the collector's source.
|
|
1017
|
+
* @param messageArguments Ordered values for numbered message placeholders; defaults to an empty sequence.
|
|
1018
|
+
* @throws {ArgumentOutOfRangeException} If the span extends beyond the source.
|
|
1019
|
+
* @throws {ArgumentException} If formatting produces an empty or whitespace-only message.
|
|
1020
|
+
* @remarks Message formatting and occurrence construction follow {@link DiagnosticTemplate.create}.
|
|
1021
|
+
* @see {@link DiagnosticCollector} for a positioned diagnostic.
|
|
1022
|
+
*/
|
|
1023
|
+
public add(template: DiagnosticTemplate, span: SourceSpan, messageArguments?: readonly (string | number)[]): void;
|
|
1024
|
+
|
|
1025
|
+
/**
|
|
1026
|
+
* Removes occurrences at and after a sequence index.
|
|
1027
|
+
*
|
|
1028
|
+
* @param count The number of occurrences to retain, previously observed as the
|
|
1029
|
+
* sequence length. A value at or beyond the current length leaves the sequence unchanged.
|
|
1030
|
+
* @see {@link DiagnosticCollector} for truncation of a live view.
|
|
1031
|
+
*/
|
|
1032
|
+
public truncate(count: number): void;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
/**
|
|
1036
|
+
* Shared grammar state for one parsing operation. It owns the diagnostic
|
|
1037
|
+
* sequence the parser publishes; derived contexts add grammar state and
|
|
1038
|
+
* recognition caches, and grammar services belong to the syntax parser.
|
|
1039
|
+
* Tentative parsing does not snapshot added fields.
|
|
1040
|
+
*
|
|
1041
|
+
* @see {@link Parser} for a lexer-backed session.
|
|
1042
|
+
*/
|
|
1043
|
+
export declare class ParserContext {
|
|
1044
|
+
/**
|
|
1045
|
+
* The diagnostics reported so far, in production order: the lexer reports
|
|
1046
|
+
* into it as it lexes, and the grammar reports into it as it parses.
|
|
1047
|
+
*/
|
|
1048
|
+
public readonly diagnosticCollector: DiagnosticCollector;
|
|
1049
|
+
|
|
1050
|
+
/**
|
|
1051
|
+
* The source being parsed, for grammar code that reads its text without
|
|
1052
|
+
* reaching through the lexer's reader.
|
|
1053
|
+
*/
|
|
1054
|
+
public readonly source: Source;
|
|
1055
|
+
|
|
1056
|
+
/**
|
|
1057
|
+
* Creates the diagnostic sequence for a source.
|
|
1058
|
+
*
|
|
1059
|
+
* @param source The immutable source being parsed; its identity positions every diagnostic.
|
|
1060
|
+
* @see {@link Parser} for session construction.
|
|
1061
|
+
*/
|
|
1062
|
+
public constructor(source: Source);
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
/**
|
|
1066
|
+
* Shared execution for top-level language lexers. A lexer owns its reader,
|
|
1067
|
+
* checks progress and end-marker invariants, and reports lexical diagnostics
|
|
1068
|
+
* into the parser context's collector as it lexes. Language lexers recognize
|
|
1069
|
+
* tokens and keep any lexical mode as their own fields.
|
|
1070
|
+
*
|
|
1071
|
+
* @see {@link Parser} for a complete lexer and parser example.
|
|
1072
|
+
*/
|
|
1073
|
+
export declare abstract class Lexer {
|
|
1074
|
+
/**
|
|
1075
|
+
* The collector lexical diagnostics are reported into: the same one the
|
|
1076
|
+
* parser context publishes, so a lexical diagnostic is visible the moment
|
|
1077
|
+
* its node is lexed.
|
|
1078
|
+
*/
|
|
1079
|
+
public readonly diagnosticCollector: DiagnosticCollector;
|
|
1080
|
+
|
|
1081
|
+
/**
|
|
1082
|
+
* The cursor over the source region being lexed. The parser rewinds it
|
|
1083
|
+
* during tentative parsing and contextual re-lexing.
|
|
1084
|
+
*/
|
|
1085
|
+
public readonly reader: SourceReader;
|
|
1086
|
+
|
|
1087
|
+
/**
|
|
1088
|
+
* Positions the lexer at the start of a source, or at a caller's reader, and
|
|
1089
|
+
* binds it to the collector its diagnostics go to.
|
|
1090
|
+
*
|
|
1091
|
+
* @param sourceOrReader The complete immutable source to tokenize from its start,
|
|
1092
|
+
* or the caller's reader with its region bounds and current offset. Navigation
|
|
1093
|
+
* is then shared with the caller; construction does not move it.
|
|
1094
|
+
* @param diagnosticCollector The parser context's collector for this parsing operation.
|
|
1095
|
+
* @see {@link Parser} for a concrete lexer.
|
|
1096
|
+
*/
|
|
1097
|
+
protected constructor(sourceOrReader: Source | SourceReader, diagnosticCollector: DiagnosticCollector);
|
|
1098
|
+
|
|
1099
|
+
/**
|
|
1100
|
+
* Produces the next node through {@link Lexer.lexNode}.
|
|
1101
|
+
*
|
|
1102
|
+
* @returns The recognized node. Diagnostics reported by the scan are already
|
|
1103
|
+
* in the collector when it returns.
|
|
1104
|
+
* @throws {ParserException} If the scan fails to advance without producing a
|
|
1105
|
+
* valid end marker, or reports an end marker away from the reader's end.
|
|
1106
|
+
* @remarks Exceptions from the concrete lexer propagate; this operation alone
|
|
1107
|
+
* does not roll back reader movement or diagnostics.
|
|
1108
|
+
* @see {@link Parser} for a concrete lexer.
|
|
1109
|
+
*/
|
|
1110
|
+
public lex(): Node;
|
|
1111
|
+
|
|
1112
|
+
/**
|
|
1113
|
+
* Recognizes the next node within a top-level lexical operation.
|
|
1114
|
+
*
|
|
1115
|
+
* @returns The recognized node, or an end marker at the reader's current end.
|
|
1116
|
+
* @remarks Consume input through {@link Lexer.reader} and report lexical
|
|
1117
|
+
* diagnostics through {@link Lexer.diagnosticCollector}.
|
|
1118
|
+
* @see {@link Parser} for an override.
|
|
1119
|
+
*/
|
|
1120
|
+
protected abstract lexNode(): Node;
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
* Lexes using the supplied scanning operation under the invariants applied to
|
|
1124
|
+
* ordinary lexing. A language's contextual re-lexing routine calls this so a
|
|
1125
|
+
* replacement scan is validated exactly like the scan it replaces.
|
|
1126
|
+
*
|
|
1127
|
+
* @param action The synchronous scan producing one immutable node from the
|
|
1128
|
+
* reader's current offset, with any lexical mode already selected by the caller.
|
|
1129
|
+
* @returns The node produced by the action.
|
|
1130
|
+
* @throws {ParserException} If progress or end-marker invariants are violated.
|
|
1131
|
+
* @remarks Action exceptions propagate without rollback by this method.
|
|
1132
|
+
* @see {@link Parser} for ordinary lexing through this execution boundary.
|
|
1133
|
+
*/
|
|
1134
|
+
protected lexWith(action: () => Node): Node;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* The published result of one parser operation.
|
|
1139
|
+
* TNode is the language's root node type. The diagnostic array is an independent
|
|
1140
|
+
* snapshot; its occurrences and the root node are shared immutable objects.
|
|
1141
|
+
*
|
|
1142
|
+
* @example
|
|
1143
|
+
* ```ts
|
|
1144
|
+
* import type { Diagnostic } from "@noldova/context.diagnostics";
|
|
1145
|
+
* import { LiteralNode, ParserResult, SourceSpan } from "@noldova/context.parsers";
|
|
1146
|
+
*
|
|
1147
|
+
* class TextLiteralNode extends LiteralNode {
|
|
1148
|
+
* public constructor(value: string, span: SourceSpan) {
|
|
1149
|
+
* super(value, span);
|
|
1150
|
+
* }
|
|
1151
|
+
* }
|
|
1152
|
+
* const diagnostics: Diagnostic[] = [];
|
|
1153
|
+
* const root = new TextLiteralNode("a", new SourceSpan(0, 1));
|
|
1154
|
+
* const result = new ParserResult(root, diagnostics);
|
|
1155
|
+
* console.assert(result.rootNode === root);
|
|
1156
|
+
* console.assert(result.diagnostics !== diagnostics && result.diagnostics.length === 0);
|
|
1157
|
+
* ```
|
|
1158
|
+
*/
|
|
1159
|
+
export declare class ParserResult<TNode extends Node> {
|
|
1160
|
+
/**
|
|
1161
|
+
* The diagnostics in production order.
|
|
1162
|
+
*/
|
|
1163
|
+
public readonly diagnostics: readonly Diagnostic[];
|
|
1164
|
+
|
|
1165
|
+
/**
|
|
1166
|
+
* The root of the produced syntax tree.
|
|
1167
|
+
*/
|
|
1168
|
+
public readonly rootNode: TNode;
|
|
1169
|
+
|
|
1170
|
+
/**
|
|
1171
|
+
* Publishes a root with an independent diagnostic sequence.
|
|
1172
|
+
*
|
|
1173
|
+
* @param rootNode The complete immutable root for the operation's declared scope.
|
|
1174
|
+
* @param diagnostics Occurrences in production order; the sequence is shallow-copied.
|
|
1175
|
+
* @see {@link ParserResult} for snapshot and node-sharing behavior.
|
|
1176
|
+
*/
|
|
1177
|
+
public constructor(rootNode: TNode, diagnostics: readonly Diagnostic[]);
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
/**
|
|
1181
|
+
* Shared execution for language syntax parsers.
|
|
1182
|
+
* TNode is the language's root node type and TContext its grammar context.
|
|
1183
|
+
* The parser holds one current node: it advances by lexing the next node, and
|
|
1184
|
+
* it rewinds by restoring the current node, the reader offset, and the length
|
|
1185
|
+
* of the diagnostic sequence, which the lexer and the grammar share. It keeps
|
|
1186
|
+
* no token cache and no parse memoization, so a rewound region is lexed again.
|
|
1187
|
+
*
|
|
1188
|
+
* Token navigation is the surface language syntax parsers inherit and share
|
|
1189
|
+
* with their grammar drivers; a language's public facade exposes input and
|
|
1190
|
+
* results instead of inheriting this surface.
|
|
1191
|
+
*
|
|
1192
|
+
* @example
|
|
1193
|
+
* ```ts
|
|
1194
|
+
* import { DiagnosticCollector, EndOfFileNode, Lexer, LiteralNode, Node, Parser, ParserContext, Source, SourceSpan } from "@noldova/context.parsers";
|
|
1195
|
+
*
|
|
1196
|
+
* class TextLiteralNode extends LiteralNode {
|
|
1197
|
+
* public constructor(value: string, span: SourceSpan) {
|
|
1198
|
+
* super(value, span);
|
|
1199
|
+
* }
|
|
1200
|
+
* }
|
|
1201
|
+
* class TextLexer extends Lexer {
|
|
1202
|
+
* public constructor(source: Source, diagnosticCollector: DiagnosticCollector) {
|
|
1203
|
+
* super(source, diagnosticCollector);
|
|
1204
|
+
* }
|
|
1205
|
+
*
|
|
1206
|
+
* protected override lexNode(): Node {
|
|
1207
|
+
* const startOffset = this.reader.currentOffset;
|
|
1208
|
+
* if (this.reader.isAtEnd)
|
|
1209
|
+
* return new EndOfFileNode(new SourceSpan(startOffset, 0));
|
|
1210
|
+
*
|
|
1211
|
+
* const text = this.reader.readWhile(() => true).toString();
|
|
1212
|
+
* return new TextLiteralNode(text, new SourceSpan(startOffset, text.length));
|
|
1213
|
+
* }
|
|
1214
|
+
* }
|
|
1215
|
+
* class TextParser extends Parser<LiteralNode> {
|
|
1216
|
+
* public constructor(context: ParserContext, lexer: TextLexer) {
|
|
1217
|
+
* super(context, lexer);
|
|
1218
|
+
* }
|
|
1219
|
+
*
|
|
1220
|
+
* protected override parseRootNode(): LiteralNode {
|
|
1221
|
+
* const root = this.requireCurrentNode(LiteralNode);
|
|
1222
|
+
* this.advance();
|
|
1223
|
+
* return root;
|
|
1224
|
+
* }
|
|
1225
|
+
* }
|
|
1226
|
+
* const source = new Source("text");
|
|
1227
|
+
* const node = new TextLexer(source, new DiagnosticCollector(source)).lex();
|
|
1228
|
+
* console.assert(node.span.startOffset === 0 && node.span.length === 4);
|
|
1229
|
+
* const context = new ParserContext(source);
|
|
1230
|
+
* const parser = new TextParser(context, new TextLexer(source, context.diagnosticCollector));
|
|
1231
|
+
* console.assert(parser.lookAhead(() => {
|
|
1232
|
+
* parser.advance();
|
|
1233
|
+
* return parser.isEndOfFile;
|
|
1234
|
+
* }));
|
|
1235
|
+
* const rejected = parser.tryParse(() => {
|
|
1236
|
+
* parser.advance();
|
|
1237
|
+
* return undefined;
|
|
1238
|
+
* });
|
|
1239
|
+
* console.assert(rejected === undefined && parser.currentNode instanceof LiteralNode);
|
|
1240
|
+
* console.assert(parser.tryConsume(EndOfFileNode) === undefined);
|
|
1241
|
+
* const result = parser.parse();
|
|
1242
|
+
* console.assert(result.rootNode.value === "text" && parser.isEndOfFile);
|
|
1243
|
+
* // The result remains usable; this parser cannot start another parse.
|
|
1244
|
+
* ```
|
|
1245
|
+
*/
|
|
1246
|
+
export declare abstract class Parser<TNode extends Node, TContext extends ParserContext = ParserContext> {
|
|
1247
|
+
/**
|
|
1248
|
+
* The shared context for this parsing operation.
|
|
1249
|
+
*/
|
|
1250
|
+
public readonly context: TContext;
|
|
1251
|
+
|
|
1252
|
+
/**
|
|
1253
|
+
* Starts a session by lexing its first node.
|
|
1254
|
+
*
|
|
1255
|
+
* @param context The grammar context whose collector publishes every diagnostic.
|
|
1256
|
+
* @param lexer The fully initialized token producer positioned where parsing
|
|
1257
|
+
* starts and reporting into that same collector.
|
|
1258
|
+
* @throws {ParserException} If the lexer reports into another collector,
|
|
1259
|
+
* which would keep its diagnostics out of the published result.
|
|
1260
|
+
* @remarks Lexical exceptions propagate during construction. The first scan
|
|
1261
|
+
* may advance the reader before construction completes.
|
|
1262
|
+
* @see {@link Parser} for a concrete syntax driver.
|
|
1263
|
+
*/
|
|
1264
|
+
protected constructor(context: TContext, lexer: Lexer);
|
|
1265
|
+
|
|
1266
|
+
/**
|
|
1267
|
+
* The node the parser is positioned on. The reader stands immediately after it.
|
|
1268
|
+
*/
|
|
1269
|
+
public get currentNode(): Node;
|
|
1270
|
+
|
|
1271
|
+
/**
|
|
1272
|
+
* True when the current node is an {@link EndOfFileNode}.
|
|
1273
|
+
*/
|
|
1274
|
+
public get isEndOfFile(): boolean;
|
|
1275
|
+
|
|
1276
|
+
/**
|
|
1277
|
+
* Parses the complete region and publishes its root and diagnostics.
|
|
1278
|
+
*
|
|
1279
|
+
* @returns The root node produced by {@link Parser.parseRootNode} with an
|
|
1280
|
+
* independent snapshot of the diagnostics reported for this source.
|
|
1281
|
+
* @throws {ParserException} If this parser has already parsed, or if input
|
|
1282
|
+
* remains after the root node.
|
|
1283
|
+
* @remarks Exceptions from the language's root parser propagate unchanged.
|
|
1284
|
+
* @see {@link Parser} for a complete session.
|
|
1285
|
+
*/
|
|
1286
|
+
public parse(): ParserResult<TNode>;
|
|
1287
|
+
|
|
1288
|
+
/**
|
|
1289
|
+
* Lexes the next node and positions the parser on it. At the end of input
|
|
1290
|
+
* the parser stays on its end marker.
|
|
1291
|
+
*
|
|
1292
|
+
* @throws {ParserException} If the lexer violates a progress or end-marker invariant.
|
|
1293
|
+
* @see {@link Parser} for ordinary consumption.
|
|
1294
|
+
*/
|
|
1295
|
+
public advance(): void;
|
|
1296
|
+
|
|
1297
|
+
/**
|
|
1298
|
+
* Consumes the current node when it has the requested type.
|
|
1299
|
+
* TCurrentNode is the instance type established by a successful check.
|
|
1300
|
+
*
|
|
1301
|
+
* @param type The required node class identity; abstract base classes are valid.
|
|
1302
|
+
* @returns The consumed node, leaving the parser on the following node, or
|
|
1303
|
+
* undefined with the position unchanged.
|
|
1304
|
+
* @throws {ParserException} If advancing violates a lexer invariant.
|
|
1305
|
+
* @see {@link Parser} for a rejected consumption.
|
|
1306
|
+
*/
|
|
1307
|
+
public tryConsume<TCurrentNode extends Node>(type: NodeType<TCurrentNode>): TCurrentNode | undefined;
|
|
1308
|
+
|
|
1309
|
+
/**
|
|
1310
|
+
* Requires the current node's type without consuming it.
|
|
1311
|
+
* TCurrentNode is the instance type established by a successful check.
|
|
1312
|
+
*
|
|
1313
|
+
* @param type The node class identity required by the recognized grammar branch.
|
|
1314
|
+
* @returns The current node, narrowed to the requested type.
|
|
1315
|
+
* @throws {ParserException} If the current node has another type, which
|
|
1316
|
+
* indicates a grammar implementation error rather than invalid source.
|
|
1317
|
+
* @see {@link Parser} for a required root node.
|
|
1318
|
+
*/
|
|
1319
|
+
public requireCurrentNode<TCurrentNode extends Node>(type: NodeType<TCurrentNode>): TCurrentNode;
|
|
1320
|
+
|
|
1321
|
+
/**
|
|
1322
|
+
* Runs a test that may move the parser, then always rewinds.
|
|
1323
|
+
*
|
|
1324
|
+
* @param action The synchronous test returning its recognition decision.
|
|
1325
|
+
* @returns The value returned by the test.
|
|
1326
|
+
* @throws {ParserException} If lexing during the test violates an invariant.
|
|
1327
|
+
* @remarks The current node, the reader offset, and the diagnostic sequence
|
|
1328
|
+
* are restored, including when the test throws; the exception then
|
|
1329
|
+
* propagates. Fields added by a language's lexer or parser context are not
|
|
1330
|
+
* restored, and nodes built during the test are discarded by the caller.
|
|
1331
|
+
* @see {@link Parser} for a lookahead that inspects the following node.
|
|
1332
|
+
*/
|
|
1333
|
+
public lookAhead(action: () => boolean): boolean;
|
|
1334
|
+
|
|
1335
|
+
/**
|
|
1336
|
+
* Runs a tentative interpretation, keeping its work only when it succeeds.
|
|
1337
|
+
* TResult is the language's accepted interpretation.
|
|
1338
|
+
*
|
|
1339
|
+
* @param action The synchronous callback returning a parsed result, or
|
|
1340
|
+
* undefined when the interpretation does not apply.
|
|
1341
|
+
* @returns The accepted result, including false, zero, or null, or undefined
|
|
1342
|
+
* after rejection and rewind.
|
|
1343
|
+
* @throws {ParserException} If lexing during the attempt violates an invariant.
|
|
1344
|
+
* @remarks A rejected or throwing attempt rewinds under {@link Parser.lookAhead}'s
|
|
1345
|
+
* obligations; a callback exception then propagates.
|
|
1346
|
+
* @see {@link Parser} for a rejected attempt.
|
|
1347
|
+
*/
|
|
1348
|
+
public tryParse<TResult>(action: () => TResult | undefined): TResult | undefined;
|
|
1349
|
+
|
|
1350
|
+
/**
|
|
1351
|
+
* Rewinds to the current node's start and replaces it through contextual
|
|
1352
|
+
* lexing, for a language whose token depends on the grammar position, such
|
|
1353
|
+
* as a slash that begins a regular expression rather than a division.
|
|
1354
|
+
*
|
|
1355
|
+
* @param lex The synchronous contextual scan producing the replacement node
|
|
1356
|
+
* from the rewound reader. Establish any required lexical mode before scanning,
|
|
1357
|
+
* and route the scan through {@link Lexer.lexWith} to keep its invariants.
|
|
1358
|
+
* @throws {ParserException} If the replacement scan violates a lexer invariant.
|
|
1359
|
+
* @remarks Nothing already reported is removed: the replacement scan reports
|
|
1360
|
+
* its own diagnostics after them, and a language whose scans could repeat a
|
|
1361
|
+
* diagnostic at the same position applies its own suppression rule, as
|
|
1362
|
+
* TypeScript does. Lexer exceptions propagate; this method alone does not
|
|
1363
|
+
* restore a failed scan.
|
|
1364
|
+
* @see {@link Parser} for the lexer and parser execution boundary.
|
|
1365
|
+
*/
|
|
1366
|
+
protected reLexCurrentNode(lex: () => Node): void;
|
|
1367
|
+
|
|
1368
|
+
/**
|
|
1369
|
+
* Parses the root through language-owned grammar and recovery.
|
|
1370
|
+
*
|
|
1371
|
+
* @returns The immutable root for this reader's region. The current node must
|
|
1372
|
+
* be an end marker before returning so {@link Parser.parse} can publish.
|
|
1373
|
+
* @remarks Diagnose invalid source and recover according to the language.
|
|
1374
|
+
* Implementation exceptions propagate through {@link Parser.parse}.
|
|
1375
|
+
* @see {@link Parser} for a root-parser override.
|
|
1376
|
+
*/
|
|
1377
|
+
protected abstract parseRootNode(): TNode;
|
|
1378
|
+
}
|