@thomasfosterau/effect-parse 0.0.0-stage → 0.0.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/LICENSE +21 -0
- package/README.md +550 -2
- package/dist/Ascii.d.ts +392 -0
- package/dist/Ascii.d.ts.map +1 -0
- package/dist/Ascii.js +610 -0
- package/dist/Ascii.js.map +1 -0
- package/dist/Binary.d.ts +153 -0
- package/dist/Binary.d.ts.map +1 -0
- package/dist/Binary.js +171 -0
- package/dist/Binary.js.map +1 -0
- package/dist/Bits.d.ts +60 -0
- package/dist/Bits.d.ts.map +1 -0
- package/dist/Bits.js +144 -0
- package/dist/Bits.js.map +1 -0
- package/dist/Encoding.d.ts +116 -0
- package/dist/Encoding.d.ts.map +1 -0
- package/dist/Encoding.js +85 -0
- package/dist/Encoding.js.map +1 -0
- package/dist/Expression.d.ts +124 -0
- package/dist/Expression.d.ts.map +1 -0
- package/dist/Expression.js +139 -0
- package/dist/Expression.js.map +1 -0
- package/dist/Input.d.ts +301 -0
- package/dist/Input.d.ts.map +1 -0
- package/dist/Input.js +312 -0
- package/dist/Input.js.map +1 -0
- package/dist/Nom.d.ts +518 -0
- package/dist/Nom.d.ts.map +1 -0
- package/dist/Nom.js +519 -0
- package/dist/Nom.js.map +1 -0
- package/dist/ParseError.d.ts +120 -0
- package/dist/ParseError.d.ts.map +1 -0
- package/dist/ParseError.js +301 -0
- package/dist/ParseError.js.map +1 -0
- package/dist/Parser.d.ts +1312 -0
- package/dist/Parser.d.ts.map +1 -0
- package/dist/Parser.js +1936 -0
- package/dist/Parser.js.map +1 -0
- package/dist/ParserSchema.d.ts +31 -0
- package/dist/ParserSchema.d.ts.map +1 -0
- package/dist/ParserSchema.js +56 -0
- package/dist/ParserSchema.js.map +1 -0
- package/dist/ParserStream.d.ts +127 -0
- package/dist/ParserStream.d.ts.map +1 -0
- package/dist/ParserStream.js +270 -0
- package/dist/ParserStream.js.map +1 -0
- package/dist/Text.d.ts +330 -0
- package/dist/Text.d.ts.map +1 -0
- package/dist/Text.js +529 -0
- package/dist/Text.js.map +1 -0
- package/dist/Token.d.ts +225 -0
- package/dist/Token.d.ts.map +1 -0
- package/dist/Token.js +294 -0
- package/dist/Token.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/compiler/closure.d.ts +25 -0
- package/dist/internal/compiler/closure.d.ts.map +1 -0
- package/dist/internal/compiler/closure.js +1668 -0
- package/dist/internal/compiler/closure.js.map +1 -0
- package/dist/internal/compiler/entries.d.ts +19 -0
- package/dist/internal/compiler/entries.d.ts.map +1 -0
- package/dist/internal/compiler/entries.js +32 -0
- package/dist/internal/compiler/entries.js.map +1 -0
- package/dist/internal/compiler/first.d.ts +33 -0
- package/dist/internal/compiler/first.d.ts.map +1 -0
- package/dist/internal/compiler/first.js +235 -0
- package/dist/internal/compiler/first.js.map +1 -0
- package/dist/internal/compiler/jit.d.ts +20 -0
- package/dist/internal/compiler/jit.d.ts.map +1 -0
- package/dist/internal/compiler/jit.js +1030 -0
- package/dist/internal/compiler/jit.js.map +1 -0
- package/dist/internal/compiler/registry.d.ts +34 -0
- package/dist/internal/compiler/registry.d.ts.map +1 -0
- package/dist/internal/compiler/registry.js +46 -0
- package/dist/internal/compiler/registry.js.map +1 -0
- package/dist/internal/compiler/sets.d.ts +29 -0
- package/dist/internal/compiler/sets.d.ts.map +1 -0
- package/dist/internal/compiler/sets.js +85 -0
- package/dist/internal/compiler/sets.js.map +1 -0
- package/dist/internal/entries.d.ts +27 -0
- package/dist/internal/entries.d.ts.map +1 -0
- package/dist/internal/entries.js +42 -0
- package/dist/internal/entries.js.map +1 -0
- package/dist/internal/node.d.ts +251 -0
- package/dist/internal/node.d.ts.map +1 -0
- package/dist/internal/node.js +73 -0
- package/dist/internal/node.js.map +1 -0
- package/dist/internal/numbers.d.ts +41 -0
- package/dist/internal/numbers.d.ts.map +1 -0
- package/dist/internal/numbers.js +302 -0
- package/dist/internal/numbers.js.map +1 -0
- package/dist/internal/resume.d.ts +39 -0
- package/dist/internal/resume.d.ts.map +1 -0
- package/dist/internal/resume.js +37 -0
- package/dist/internal/resume.js.map +1 -0
- package/dist/internal/text.d.ts +72 -0
- package/dist/internal/text.d.ts.map +1 -0
- package/dist/internal/text.js +391 -0
- package/dist/internal/text.js.map +1 -0
- package/dist/internal/tokens.d.ts +93 -0
- package/dist/internal/tokens.d.ts.map +1 -0
- package/dist/internal/tokens.js +400 -0
- package/dist/internal/tokens.js.map +1 -0
- package/package.json +115 -3
- package/src/Ascii.ts +741 -0
- package/src/Binary.ts +228 -0
- package/src/Bits.ts +192 -0
- package/src/Encoding.ts +128 -0
- package/src/Expression.ts +245 -0
- package/src/Input.ts +429 -0
- package/src/Nom.ts +642 -0
- package/src/ParseError.ts +378 -0
- package/src/Parser.ts +2763 -0
- package/src/ParserSchema.ts +87 -0
- package/src/ParserStream.ts +390 -0
- package/src/Text.ts +757 -0
- package/src/Token.ts +416 -0
- package/src/index.ts +16 -0
- package/src/internal/compiler/closure.ts +1583 -0
- package/src/internal/compiler/entries.ts +36 -0
- package/src/internal/compiler/first.ts +252 -0
- package/src/internal/compiler/jit.ts +1091 -0
- package/src/internal/compiler/registry.ts +79 -0
- package/src/internal/compiler/sets.ts +100 -0
- package/src/internal/entries.ts +51 -0
- package/src/internal/node.ts +207 -0
- package/src/internal/numbers.ts +291 -0
- package/src/internal/resume.ts +55 -0
- package/src/internal/text.ts +411 -0
- package/src/internal/tokens.ts +409 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The error type produced when running a parser.
|
|
3
|
+
*
|
|
4
|
+
* `ParseError` merges winnow's `ContextError`/`ParseError` and nom's
|
|
5
|
+
* `VerboseError`. It records where parsing failed, what was expected there,
|
|
6
|
+
* the context labels that were active, and an optional typed `cause`
|
|
7
|
+
* produced by `Parser.fail`, `Parser.tryMap` and friends. Its `message`
|
|
8
|
+
* shows the failing position in the input:
|
|
9
|
+
*
|
|
10
|
+
* ```text
|
|
11
|
+
* Parse error at line 1, column 4:
|
|
12
|
+
* 1 | [1,,2]
|
|
13
|
+
* | ^
|
|
14
|
+
* expected digit
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* `ParseError` is a `Data.TaggedError`, so it can be yielded directly inside
|
|
18
|
+
* `Effect.gen`.
|
|
19
|
+
*
|
|
20
|
+
* @since 0.1.0
|
|
21
|
+
*/
|
|
22
|
+
import * as Data from "effect/Data"
|
|
23
|
+
import * as Option from "effect/Option"
|
|
24
|
+
import type { Encoding } from "./Encoding.js"
|
|
25
|
+
import type { ErrMode, Failure, Input } from "./Input.js"
|
|
26
|
+
import * as text from "./internal/text.js"
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The kind of parser that produced an error, in the spirit of nom's
|
|
30
|
+
* `ErrorKind`.
|
|
31
|
+
*
|
|
32
|
+
* @since 0.1.0
|
|
33
|
+
* @category models
|
|
34
|
+
*/
|
|
35
|
+
export type ErrorKind =
|
|
36
|
+
| "Alpha"
|
|
37
|
+
| "AlphaNumeric"
|
|
38
|
+
| "Alt"
|
|
39
|
+
| "Assert"
|
|
40
|
+
| "Bits"
|
|
41
|
+
| "Complete"
|
|
42
|
+
| "CrLf"
|
|
43
|
+
| "Digit"
|
|
44
|
+
| "Dispatch"
|
|
45
|
+
| "Eof"
|
|
46
|
+
| "Escaped"
|
|
47
|
+
| "Fail"
|
|
48
|
+
| "Float"
|
|
49
|
+
| "HexDigit"
|
|
50
|
+
| "Incomplete"
|
|
51
|
+
| "LineEnding"
|
|
52
|
+
| "Literal"
|
|
53
|
+
| "Many"
|
|
54
|
+
| "MultiSpace"
|
|
55
|
+
| "NoneOf"
|
|
56
|
+
| "Not"
|
|
57
|
+
| "OctDigit"
|
|
58
|
+
| "OneOf"
|
|
59
|
+
| "Permutation"
|
|
60
|
+
| "Separated"
|
|
61
|
+
| "Slice"
|
|
62
|
+
| "Space"
|
|
63
|
+
| "Take"
|
|
64
|
+
| "TakeUntil"
|
|
65
|
+
| "TakeWhile"
|
|
66
|
+
| "Token"
|
|
67
|
+
| "Verify"
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @since 0.1.0
|
|
71
|
+
* @category type ids
|
|
72
|
+
*/
|
|
73
|
+
export const TypeId: "~effect-parse/ParseError" = "~effect-parse/ParseError"
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* An error produced by running a parser.
|
|
77
|
+
*
|
|
78
|
+
* @since 0.1.0
|
|
79
|
+
* @category models
|
|
80
|
+
*/
|
|
81
|
+
export class ParseError<out E = never> extends Data.TaggedError("ParseError")<{
|
|
82
|
+
/** The input that was being parsed. */
|
|
83
|
+
readonly input: Input
|
|
84
|
+
/** The offset of the failure in the input. */
|
|
85
|
+
readonly offset: number
|
|
86
|
+
/** The kind of parser that failed. */
|
|
87
|
+
readonly kind: ErrorKind
|
|
88
|
+
/** How the error propagated before parsing stopped. */
|
|
89
|
+
readonly mode: ErrMode
|
|
90
|
+
/** Descriptions of what was expected at `offset`. */
|
|
91
|
+
readonly expected: ReadonlyArray<string>
|
|
92
|
+
/** Context labels (`Parser.context`), innermost first. */
|
|
93
|
+
readonly context: ReadonlyArray<string>
|
|
94
|
+
/** The user supplied cause, if any. */
|
|
95
|
+
readonly cause: E | undefined
|
|
96
|
+
/** An optional free-form description. */
|
|
97
|
+
readonly description: string | undefined
|
|
98
|
+
/**
|
|
99
|
+
* For byte inputs parsed by `Text` parsers, the text encoding. Errors
|
|
100
|
+
* then show the input as text, with line and column numbers.
|
|
101
|
+
*/
|
|
102
|
+
readonly encoding?: Encoding<Uint8Array> | undefined
|
|
103
|
+
}> {
|
|
104
|
+
/**
|
|
105
|
+
* @since 0.1.0
|
|
106
|
+
*/
|
|
107
|
+
readonly [TypeId]: typeof TypeId = TypeId
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The 1-based line and column of the failure (for strings and encoded
|
|
111
|
+
* text; for other inputs the line is 1 and the column is `offset + 1`).
|
|
112
|
+
*
|
|
113
|
+
* @since 0.1.0
|
|
114
|
+
*/
|
|
115
|
+
get position(): LineColumn {
|
|
116
|
+
return lineColumn(this.input, this.offset, this.encoding)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The rendered, human-readable error message.
|
|
121
|
+
*
|
|
122
|
+
* @since 0.1.0
|
|
123
|
+
*/
|
|
124
|
+
override get message(): string {
|
|
125
|
+
return render(this)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @since 0.1.0
|
|
130
|
+
*/
|
|
131
|
+
override toString(): string {
|
|
132
|
+
return `ParseError: ${this.message}`
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @since 0.1.0
|
|
138
|
+
* @category guards
|
|
139
|
+
*/
|
|
140
|
+
export const isParseError = (u: unknown): u is ParseError<unknown> =>
|
|
141
|
+
typeof u === "object" && u !== null && TypeId in u
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Builds a `ParseError` from the failure details recorded while parsing.
|
|
145
|
+
*
|
|
146
|
+
* @since 0.1.0
|
|
147
|
+
* @category constructors
|
|
148
|
+
*/
|
|
149
|
+
export const fromFailure = <E = never>(
|
|
150
|
+
input: Input,
|
|
151
|
+
failure: Failure,
|
|
152
|
+
mode: ErrMode,
|
|
153
|
+
encoding?: Encoding<any> | undefined
|
|
154
|
+
): ParseError<E> =>
|
|
155
|
+
new ParseError<E>({
|
|
156
|
+
input,
|
|
157
|
+
offset: failure.offset,
|
|
158
|
+
kind: failure.kind,
|
|
159
|
+
mode,
|
|
160
|
+
expected: failure.expected,
|
|
161
|
+
context: failure.context,
|
|
162
|
+
cause: failure.hasCause ? failure.cause as E : undefined,
|
|
163
|
+
description: failure.message,
|
|
164
|
+
encoding: input instanceof Uint8Array ? encoding : undefined
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* The user supplied cause of an error, if any.
|
|
169
|
+
*
|
|
170
|
+
* @since 0.1.0
|
|
171
|
+
* @category getters
|
|
172
|
+
*/
|
|
173
|
+
export const getCause = <E>(self: ParseError<E>): Option.Option<E> =>
|
|
174
|
+
self.cause === undefined ? Option.none() : Option.some(self.cause)
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* A 1-based line/column position.
|
|
178
|
+
*
|
|
179
|
+
* @since 0.1.0
|
|
180
|
+
* @category models
|
|
181
|
+
*/
|
|
182
|
+
export interface LineColumn {
|
|
183
|
+
readonly line: number
|
|
184
|
+
readonly column: number
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Computes the 1-based line and column (in code points) of an offset. Lines
|
|
189
|
+
* are separated by `\n`. Byte inputs are read as text when an `encoding`
|
|
190
|
+
* is given. For other inputs the line is always 1 and the column is
|
|
191
|
+
* `offset + 1`.
|
|
192
|
+
*
|
|
193
|
+
* @since 0.1.0
|
|
194
|
+
* @category utils
|
|
195
|
+
*/
|
|
196
|
+
export const lineColumn = (input: Input, offset: number, encoding?: Encoding<any> | undefined): LineColumn => {
|
|
197
|
+
if (encoding !== undefined && input instanceof Uint8Array) {
|
|
198
|
+
let line = 1
|
|
199
|
+
let column = 1
|
|
200
|
+
text.walk(encoding, input, 0, Math.min(offset, input.length), (cp) => {
|
|
201
|
+
if (cp === 10) {
|
|
202
|
+
line++
|
|
203
|
+
column = 1
|
|
204
|
+
} else {
|
|
205
|
+
column++
|
|
206
|
+
}
|
|
207
|
+
})
|
|
208
|
+
return { line, column }
|
|
209
|
+
}
|
|
210
|
+
if (typeof input !== "string") return { line: 1, column: offset + 1 }
|
|
211
|
+
let line = 1
|
|
212
|
+
let lineStart = 0
|
|
213
|
+
for (let i = 0; i < offset && i < input.length; i++) {
|
|
214
|
+
if (input.charCodeAt(i) === 10) {
|
|
215
|
+
line++
|
|
216
|
+
lineStart = i + 1
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return { line, column: codePointCount(input, lineStart, Math.min(offset, input.length)) + 1 }
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const codePointCount = (s: string, start: number, end: number): number => {
|
|
223
|
+
let n = 0
|
|
224
|
+
for (let i = start; i < end; i++) {
|
|
225
|
+
const c = s.charCodeAt(i)
|
|
226
|
+
if (c >= 0xd800 && c <= 0xdbff && i + 1 < end) {
|
|
227
|
+
const d = s.charCodeAt(i + 1)
|
|
228
|
+
if (d >= 0xdc00 && d <= 0xdfff) i++
|
|
229
|
+
}
|
|
230
|
+
n++
|
|
231
|
+
}
|
|
232
|
+
return n
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Formats a list of expectations: `a`, `a or b`, `a, b or c`.
|
|
237
|
+
*
|
|
238
|
+
* @since 0.1.0
|
|
239
|
+
* @category utils
|
|
240
|
+
*/
|
|
241
|
+
export const formatExpected = (expected: ReadonlyArray<string>): string => {
|
|
242
|
+
if (expected.length === 0) return ""
|
|
243
|
+
if (expected.length === 1) return expected[0]!
|
|
244
|
+
return `${expected.slice(0, -1).join(", ")} or ${expected[expected.length - 1]}`
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const hexBytes = (bytes: Uint8Array): string =>
|
|
248
|
+
Array.from(bytes, (b) => `0x${b.toString(16).padStart(2, "0")}`).join(" ")
|
|
249
|
+
|
|
250
|
+
const describeFound = (input: Input, offset: number, encoding: Encoding<any> | undefined): string => {
|
|
251
|
+
if (offset >= input.length) return "end of input"
|
|
252
|
+
if (encoding !== undefined && input instanceof Uint8Array) {
|
|
253
|
+
const cp = encoding.decode(input, offset, input.length)
|
|
254
|
+
if (cp >= 0) return JSON.stringify(String.fromCodePoint(cp))
|
|
255
|
+
const bad = input.subarray(offset, Math.min(input.length, offset + Math.max(encoding.unit, 1)))
|
|
256
|
+
return cp === text.Truncated
|
|
257
|
+
? `incomplete ${encoding.name} sequence ${hexBytes(input.subarray(offset))}`
|
|
258
|
+
: `invalid ${encoding.name} ${hexBytes(bad)}`
|
|
259
|
+
}
|
|
260
|
+
if (typeof input === "string") {
|
|
261
|
+
const cp = input.codePointAt(offset)!
|
|
262
|
+
return JSON.stringify(String.fromCodePoint(cp))
|
|
263
|
+
}
|
|
264
|
+
if (input instanceof Uint8Array) {
|
|
265
|
+
return `0x${input[offset]!.toString(16).padStart(2, "0")}`
|
|
266
|
+
}
|
|
267
|
+
return inspectToken(input[offset])
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** @internal */
|
|
271
|
+
export const inspectToken = (u: unknown): string => {
|
|
272
|
+
if (typeof u === "string") return JSON.stringify(u)
|
|
273
|
+
if (typeof u === "object" && u !== null) {
|
|
274
|
+
try {
|
|
275
|
+
return JSON.stringify(u)
|
|
276
|
+
} catch {
|
|
277
|
+
return String(u)
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return String(u)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const causeMessage = (cause: unknown): string | undefined => {
|
|
284
|
+
if (cause === undefined) return undefined
|
|
285
|
+
if (typeof cause === "string") return cause
|
|
286
|
+
if (cause instanceof Error) return cause.message
|
|
287
|
+
if (typeof cause === "object" && cause !== null && "message" in cause && typeof cause.message === "string") {
|
|
288
|
+
return cause.message
|
|
289
|
+
}
|
|
290
|
+
return inspectToken(cause)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const renderSnippet = (input: Input, offset: number, encoding: Encoding<any> | undefined): Array<string> => {
|
|
294
|
+
if (encoding !== undefined && input instanceof Uint8Array) {
|
|
295
|
+
// Find the line containing `offset`, then decode it
|
|
296
|
+
let lineStart = 0
|
|
297
|
+
let end = input.length
|
|
298
|
+
let column = 1
|
|
299
|
+
text.walk(encoding, input, 0, input.length, (cp, i, w) => {
|
|
300
|
+
if (i >= offset && cp === 10) {
|
|
301
|
+
end = i
|
|
302
|
+
return false
|
|
303
|
+
}
|
|
304
|
+
if (i < offset) {
|
|
305
|
+
if (cp === 10) {
|
|
306
|
+
lineStart = i + w
|
|
307
|
+
column = 1
|
|
308
|
+
} else {
|
|
309
|
+
column++
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
})
|
|
313
|
+
let line = text.lenientText(encoding, input, lineStart, end)
|
|
314
|
+
if (line.endsWith("\r")) line = line.slice(0, -1)
|
|
315
|
+
const gutter = String(lineColumn(input, offset, encoding).line)
|
|
316
|
+
const pad = " ".repeat(gutter.length)
|
|
317
|
+
return [
|
|
318
|
+
`${gutter} | ${line}`,
|
|
319
|
+
`${pad} | ${" ".repeat(column - 1)}^`
|
|
320
|
+
]
|
|
321
|
+
}
|
|
322
|
+
if (typeof input === "string") {
|
|
323
|
+
const { column, line } = lineColumn(input, offset)
|
|
324
|
+
let start = offset
|
|
325
|
+
while (start > 0 && input.charCodeAt(start - 1) !== 10) start--
|
|
326
|
+
let end = offset
|
|
327
|
+
while (end < input.length && input.charCodeAt(end) !== 10) end++
|
|
328
|
+
let text = input.slice(start, end)
|
|
329
|
+
if (text.endsWith("\r")) text = text.slice(0, -1)
|
|
330
|
+
const gutter = String(line)
|
|
331
|
+
const pad = " ".repeat(gutter.length)
|
|
332
|
+
return [
|
|
333
|
+
`${gutter} | ${text}`,
|
|
334
|
+
`${pad} | ${" ".repeat(column - 1)}^`
|
|
335
|
+
]
|
|
336
|
+
}
|
|
337
|
+
if (input instanceof Uint8Array) {
|
|
338
|
+
const start = Math.max(0, offset - 8)
|
|
339
|
+
const end = Math.min(input.length, offset + 8)
|
|
340
|
+
const bytes: Array<string> = []
|
|
341
|
+
for (let i = start; i < end; i++) bytes.push(input[i]!.toString(16).padStart(2, "0"))
|
|
342
|
+
const caret = (offset - start) * 3
|
|
343
|
+
return [
|
|
344
|
+
`${start.toString(16).padStart(8, "0")} | ${bytes.join(" ")}`,
|
|
345
|
+
`${" ".repeat(8)} | ${" ".repeat(caret)}^`
|
|
346
|
+
]
|
|
347
|
+
}
|
|
348
|
+
return []
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const render = (error: ParseError<unknown>): string => {
|
|
352
|
+
const { encoding, input, offset } = error
|
|
353
|
+
const lines: Array<string> = []
|
|
354
|
+
if (typeof input === "string") {
|
|
355
|
+
const { column, line } = lineColumn(input, offset)
|
|
356
|
+
lines.push(`Parse error at line ${line}, column ${column}:`)
|
|
357
|
+
} else if (encoding !== undefined) {
|
|
358
|
+
const { column, line } = lineColumn(input, offset, encoding)
|
|
359
|
+
lines.push(`Parse error at line ${line}, column ${column} (byte ${offset}):`)
|
|
360
|
+
} else {
|
|
361
|
+
lines.push(`Parse error at offset ${offset}:`)
|
|
362
|
+
}
|
|
363
|
+
lines.push(...renderSnippet(input, offset, encoding))
|
|
364
|
+
for (let i = error.context.length - 1; i >= 0; i--) {
|
|
365
|
+
lines.push(`while parsing ${error.context[i]}`)
|
|
366
|
+
}
|
|
367
|
+
const cause = causeMessage(error.cause)
|
|
368
|
+
if (cause !== undefined) lines.push(cause)
|
|
369
|
+
if (error.description !== undefined) lines.push(error.description)
|
|
370
|
+
if (error.mode === "Incomplete") {
|
|
371
|
+
lines.push("unexpected end of input (incomplete)")
|
|
372
|
+
} else if (error.expected.length > 0) {
|
|
373
|
+
lines.push(`expected ${formatExpected(error.expected)}, found ${describeFound(input, offset, encoding)}`)
|
|
374
|
+
} else if (cause === undefined && error.description === undefined) {
|
|
375
|
+
lines.push(`unexpected ${describeFound(input, offset, encoding)} (${error.kind})`)
|
|
376
|
+
}
|
|
377
|
+
return lines.join("\n")
|
|
378
|
+
}
|