@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.
Files changed (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +550 -2
  3. package/dist/Ascii.d.ts +392 -0
  4. package/dist/Ascii.d.ts.map +1 -0
  5. package/dist/Ascii.js +610 -0
  6. package/dist/Ascii.js.map +1 -0
  7. package/dist/Binary.d.ts +153 -0
  8. package/dist/Binary.d.ts.map +1 -0
  9. package/dist/Binary.js +171 -0
  10. package/dist/Binary.js.map +1 -0
  11. package/dist/Bits.d.ts +60 -0
  12. package/dist/Bits.d.ts.map +1 -0
  13. package/dist/Bits.js +144 -0
  14. package/dist/Bits.js.map +1 -0
  15. package/dist/Encoding.d.ts +116 -0
  16. package/dist/Encoding.d.ts.map +1 -0
  17. package/dist/Encoding.js +85 -0
  18. package/dist/Encoding.js.map +1 -0
  19. package/dist/Expression.d.ts +124 -0
  20. package/dist/Expression.d.ts.map +1 -0
  21. package/dist/Expression.js +139 -0
  22. package/dist/Expression.js.map +1 -0
  23. package/dist/Input.d.ts +301 -0
  24. package/dist/Input.d.ts.map +1 -0
  25. package/dist/Input.js +312 -0
  26. package/dist/Input.js.map +1 -0
  27. package/dist/Nom.d.ts +518 -0
  28. package/dist/Nom.d.ts.map +1 -0
  29. package/dist/Nom.js +519 -0
  30. package/dist/Nom.js.map +1 -0
  31. package/dist/ParseError.d.ts +120 -0
  32. package/dist/ParseError.d.ts.map +1 -0
  33. package/dist/ParseError.js +301 -0
  34. package/dist/ParseError.js.map +1 -0
  35. package/dist/Parser.d.ts +1312 -0
  36. package/dist/Parser.d.ts.map +1 -0
  37. package/dist/Parser.js +1936 -0
  38. package/dist/Parser.js.map +1 -0
  39. package/dist/ParserSchema.d.ts +31 -0
  40. package/dist/ParserSchema.d.ts.map +1 -0
  41. package/dist/ParserSchema.js +56 -0
  42. package/dist/ParserSchema.js.map +1 -0
  43. package/dist/ParserStream.d.ts +127 -0
  44. package/dist/ParserStream.d.ts.map +1 -0
  45. package/dist/ParserStream.js +270 -0
  46. package/dist/ParserStream.js.map +1 -0
  47. package/dist/Text.d.ts +330 -0
  48. package/dist/Text.d.ts.map +1 -0
  49. package/dist/Text.js +529 -0
  50. package/dist/Text.js.map +1 -0
  51. package/dist/Token.d.ts +225 -0
  52. package/dist/Token.d.ts.map +1 -0
  53. package/dist/Token.js +294 -0
  54. package/dist/Token.js.map +1 -0
  55. package/dist/index.d.ts +17 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +17 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/internal/compiler/closure.d.ts +25 -0
  60. package/dist/internal/compiler/closure.d.ts.map +1 -0
  61. package/dist/internal/compiler/closure.js +1668 -0
  62. package/dist/internal/compiler/closure.js.map +1 -0
  63. package/dist/internal/compiler/entries.d.ts +19 -0
  64. package/dist/internal/compiler/entries.d.ts.map +1 -0
  65. package/dist/internal/compiler/entries.js +32 -0
  66. package/dist/internal/compiler/entries.js.map +1 -0
  67. package/dist/internal/compiler/first.d.ts +33 -0
  68. package/dist/internal/compiler/first.d.ts.map +1 -0
  69. package/dist/internal/compiler/first.js +235 -0
  70. package/dist/internal/compiler/first.js.map +1 -0
  71. package/dist/internal/compiler/jit.d.ts +20 -0
  72. package/dist/internal/compiler/jit.d.ts.map +1 -0
  73. package/dist/internal/compiler/jit.js +1030 -0
  74. package/dist/internal/compiler/jit.js.map +1 -0
  75. package/dist/internal/compiler/registry.d.ts +34 -0
  76. package/dist/internal/compiler/registry.d.ts.map +1 -0
  77. package/dist/internal/compiler/registry.js +46 -0
  78. package/dist/internal/compiler/registry.js.map +1 -0
  79. package/dist/internal/compiler/sets.d.ts +29 -0
  80. package/dist/internal/compiler/sets.d.ts.map +1 -0
  81. package/dist/internal/compiler/sets.js +85 -0
  82. package/dist/internal/compiler/sets.js.map +1 -0
  83. package/dist/internal/entries.d.ts +27 -0
  84. package/dist/internal/entries.d.ts.map +1 -0
  85. package/dist/internal/entries.js +42 -0
  86. package/dist/internal/entries.js.map +1 -0
  87. package/dist/internal/node.d.ts +251 -0
  88. package/dist/internal/node.d.ts.map +1 -0
  89. package/dist/internal/node.js +73 -0
  90. package/dist/internal/node.js.map +1 -0
  91. package/dist/internal/numbers.d.ts +41 -0
  92. package/dist/internal/numbers.d.ts.map +1 -0
  93. package/dist/internal/numbers.js +302 -0
  94. package/dist/internal/numbers.js.map +1 -0
  95. package/dist/internal/resume.d.ts +39 -0
  96. package/dist/internal/resume.d.ts.map +1 -0
  97. package/dist/internal/resume.js +37 -0
  98. package/dist/internal/resume.js.map +1 -0
  99. package/dist/internal/text.d.ts +72 -0
  100. package/dist/internal/text.d.ts.map +1 -0
  101. package/dist/internal/text.js +391 -0
  102. package/dist/internal/text.js.map +1 -0
  103. package/dist/internal/tokens.d.ts +93 -0
  104. package/dist/internal/tokens.d.ts.map +1 -0
  105. package/dist/internal/tokens.js +400 -0
  106. package/dist/internal/tokens.js.map +1 -0
  107. package/package.json +115 -3
  108. package/src/Ascii.ts +741 -0
  109. package/src/Binary.ts +228 -0
  110. package/src/Bits.ts +192 -0
  111. package/src/Encoding.ts +128 -0
  112. package/src/Expression.ts +245 -0
  113. package/src/Input.ts +429 -0
  114. package/src/Nom.ts +642 -0
  115. package/src/ParseError.ts +378 -0
  116. package/src/Parser.ts +2763 -0
  117. package/src/ParserSchema.ts +87 -0
  118. package/src/ParserStream.ts +390 -0
  119. package/src/Text.ts +757 -0
  120. package/src/Token.ts +416 -0
  121. package/src/index.ts +16 -0
  122. package/src/internal/compiler/closure.ts +1583 -0
  123. package/src/internal/compiler/entries.ts +36 -0
  124. package/src/internal/compiler/first.ts +252 -0
  125. package/src/internal/compiler/jit.ts +1091 -0
  126. package/src/internal/compiler/registry.ts +79 -0
  127. package/src/internal/compiler/sets.ts +100 -0
  128. package/src/internal/entries.ts +51 -0
  129. package/src/internal/node.ts +207 -0
  130. package/src/internal/numbers.ts +291 -0
  131. package/src/internal/resume.ts +55 -0
  132. package/src/internal/text.ts +411 -0
  133. package/src/internal/tokens.ts +409 -0
@@ -0,0 +1,411 @@
1
+ import type { Encoding } from "../Encoding.js"
2
+ import type { Fail, State } from "../Input.js"
3
+
4
+ // -----------------------------------------------------------------------------
5
+ // Decoding results
6
+ // -----------------------------------------------------------------------------
7
+
8
+ /** `Encoding.decode`: the input is malformed. */
9
+ export const Invalid = -1
10
+ /** `Encoding.decode`: the input ends inside a sequence. */
11
+ export const Truncated = -2
12
+ /** `next`: the input has ended. */
13
+ export const End = -3
14
+
15
+ // -----------------------------------------------------------------------------
16
+ // Encodings
17
+ // -----------------------------------------------------------------------------
18
+
19
+ /**
20
+ * Units below this value decode to themselves with width 1, so hot loops can
21
+ * skip `decode`. Defaults to 0x80 for ASCII compatible encodings.
22
+ */
23
+ const FastLimit: unique symbol = Symbol.for("effect-parse/Encoding/FastLimit")
24
+
25
+ /** @internal */
26
+ export const fastLimit = (enc: Encoding<any>): number =>
27
+ (enc as any)[FastLimit] ?? (enc.asciiCompatible && enc.unit === 1 ? 0x80 : 0)
28
+
29
+ const asciiChars: ReadonlyArray<string> = Array.from({ length: 128 }, (_, i) => String.fromCharCode(i))
30
+
31
+ /** `String.fromCodePoint`, with the ASCII characters cached. */
32
+ export const charOf = (cp: number): string =>
33
+ cp < 128 ? asciiChars[cp]! : cp < 0x10000 ? String.fromCharCode(cp) : String.fromCodePoint(cp)
34
+
35
+ const decodeString = (s: string, i: number, end: number): number => {
36
+ const c = s.charCodeAt(i)
37
+ if ((c & 0xf800) !== 0xd800) return c
38
+ if (c <= 0xdbff) {
39
+ if (i + 1 >= end) return Truncated
40
+ const d = s.charCodeAt(i + 1)
41
+ if ((d & 0xfc00) === 0xdc00) return ((c - 0xd800) << 10) + (d - 0xdc00) + 0x10000
42
+ }
43
+ return Invalid
44
+ }
45
+
46
+ /**
47
+ * JavaScript strings: UTF-16 code units. Offsets are code-unit indices and
48
+ * slices are `String.prototype.slice`s, so no decoding is needed.
49
+ */
50
+ export const string: Encoding<string> = {
51
+ name: "UTF-16",
52
+ unit: 1,
53
+ asciiCompatible: true,
54
+ decode: decodeString,
55
+ width: (cp) => cp > 0xffff ? 2 : 1,
56
+ encode: (text) => text,
57
+ text: (s, start, end) => s.slice(start, end),
58
+ [FastLimit]: 0xd800
59
+ } as Encoding<string>
60
+
61
+ const decodeUtf8 = (b: Uint8Array, i: number, end: number): number => {
62
+ const c = b[i]!
63
+ if (c < 0x80) return c
64
+ if (c < 0xc2) return Invalid
65
+ if (c < 0xe0) {
66
+ if (i + 1 >= end) return Truncated
67
+ const d = b[i + 1]!
68
+ return (d & 0xc0) === 0x80 ? ((c & 0x1f) << 6) | (d & 0x3f) : Invalid
69
+ }
70
+ if (c < 0xf0) {
71
+ if (i + 1 >= end) return Truncated
72
+ const d = b[i + 1]!
73
+ // Reject overlong encodings (E0 80..9F) and surrogates (ED A0..BF)
74
+ if (c === 0xe0 ? d < 0xa0 || d > 0xbf : c === 0xed ? d < 0x80 || d > 0x9f : (d & 0xc0) !== 0x80) return Invalid
75
+ if (i + 2 >= end) return Truncated
76
+ const e = b[i + 2]!
77
+ return (e & 0xc0) === 0x80 ? ((c & 0x0f) << 12) | ((d & 0x3f) << 6) | (e & 0x3f) : Invalid
78
+ }
79
+ if (c < 0xf5) {
80
+ if (i + 1 >= end) return Truncated
81
+ const d = b[i + 1]!
82
+ // Reject overlong encodings (F0 80..8F) and code points above U+10FFFF (F4 90..BF)
83
+ if (c === 0xf0 ? d < 0x90 || d > 0xbf : c === 0xf4 ? d < 0x80 || d > 0x8f : (d & 0xc0) !== 0x80) return Invalid
84
+ if (i + 2 >= end) return Truncated
85
+ const e = b[i + 2]!
86
+ if ((e & 0xc0) !== 0x80) return Invalid
87
+ if (i + 3 >= end) return Truncated
88
+ const f = b[i + 3]!
89
+ return (f & 0xc0) === 0x80 ? ((c & 0x07) << 18) | ((d & 0x3f) << 12) | ((e & 0x3f) << 6) | (f & 0x3f) : Invalid
90
+ }
91
+ return Invalid
92
+ }
93
+
94
+ // Building short strings by hand is several times faster than a
95
+ // `TextDecoder` call, which has a high fixed cost.
96
+ const SHORT = 24
97
+
98
+ const utf8Decoder = new TextDecoder("utf-8", { ignoreBOM: true })
99
+
100
+ /**
101
+ * Decodes `b[start, end)`. Some platforms' `TextDecoder` rejects views of a
102
+ * `SharedArrayBuffer`, so those are copied first.
103
+ */
104
+ const decodeRange = (decoder: TextDecoder, b: Uint8Array, start: number, end: number): string => {
105
+ const view = b.subarray(start, end)
106
+ return decoder.decode(b.buffer instanceof ArrayBuffer ? view : view.slice())
107
+ }
108
+ const utf8Encoder = new TextEncoder()
109
+
110
+ /** UTF-8 bytes. */
111
+ export const utf8: Encoding<Uint8Array> = {
112
+ name: "UTF-8",
113
+ unit: 1,
114
+ asciiCompatible: true,
115
+ decode: decodeUtf8,
116
+ width: (cp) => cp < 0x80 ? 1 : cp < 0x800 ? 2 : cp < 0x10000 ? 3 : 4,
117
+ encode: (text) => utf8Encoder.encode(text),
118
+ text: (b, start, end) => {
119
+ if (end - start > SHORT) return decodeRange(utf8Decoder, b, start, end)
120
+ let out = ""
121
+ for (let i = start; i < end;) {
122
+ const c = b[i]!
123
+ if (c < 0x80) {
124
+ out += asciiChars[c]
125
+ i++
126
+ } else {
127
+ const cp = decodeUtf8(b, i, end)
128
+ out += cp < 0x10000 ? String.fromCharCode(cp) : String.fromCodePoint(cp)
129
+ i += cp < 0x800 ? 2 : cp < 0x10000 ? 3 : 4
130
+ }
131
+ }
132
+ return out
133
+ }
134
+ }
135
+
136
+ const makeUtf16 = (littleEndian: boolean): Encoding<Uint8Array> => {
137
+ const lo = littleEndian ? 0 : 1
138
+ const hi = 1 - lo
139
+ let decoder: TextDecoder | undefined
140
+ return {
141
+ name: littleEndian ? "UTF-16LE" : "UTF-16BE",
142
+ unit: 2,
143
+ asciiCompatible: false,
144
+ decode: (b, i, end) => {
145
+ if (i + 1 >= end) return Truncated
146
+ const c = b[i + lo]! | (b[i + hi]! << 8)
147
+ if ((c & 0xf800) !== 0xd800) return c
148
+ if (c > 0xdbff) return Invalid
149
+ if (i + 3 >= end) return Truncated
150
+ const d = b[i + 2 + lo]! | (b[i + 2 + hi]! << 8)
151
+ return (d & 0xfc00) === 0xdc00 ? ((c - 0xd800) << 10) + (d - 0xdc00) + 0x10000 : Invalid
152
+ },
153
+ width: (cp) => cp > 0xffff ? 4 : 2,
154
+ encode: (text) => {
155
+ const out = new Uint8Array(text.length * 2)
156
+ for (let i = 0; i < text.length; i++) {
157
+ const c = text.charCodeAt(i)
158
+ out[2 * i + lo] = c & 0xff
159
+ out[2 * i + hi] = c >> 8
160
+ }
161
+ return out
162
+ },
163
+ text: (b, start, end) => {
164
+ if (end - start <= SHORT * 2) {
165
+ // The range is well formed, so surrogate pairs can be copied unit by unit
166
+ let out = ""
167
+ for (let i = start; i < end; i += 2) out += String.fromCharCode(b[i + lo]! | (b[i + hi]! << 8))
168
+ return out
169
+ }
170
+ decoder ??= new TextDecoder(littleEndian ? "utf-16le" : "utf-16be", { ignoreBOM: true })
171
+ return decodeRange(decoder, b, start, end)
172
+ }
173
+ }
174
+ }
175
+
176
+ /** UTF-16 bytes, little endian. */
177
+ export const utf16le: Encoding<Uint8Array> = makeUtf16(true)
178
+
179
+ /** UTF-16 bytes, big endian. */
180
+ export const utf16be: Encoding<Uint8Array> = makeUtf16(false)
181
+
182
+ /** ISO-8859-1: every byte is the code point of the same value. */
183
+ export const latin1: Encoding<Uint8Array> = {
184
+ name: "ISO-8859-1",
185
+ unit: 1,
186
+ asciiCompatible: true,
187
+ decode: (b, i) => b[i]!,
188
+ width: () => 1,
189
+ encode: (text) => {
190
+ const out = new Uint8Array(text.length)
191
+ for (let i = 0; i < text.length; i++) {
192
+ const c = text.charCodeAt(i)
193
+ if (c > 0xff) throw new RangeError(`${JSON.stringify(text[i])} can't be encoded as ISO-8859-1`)
194
+ out[i] = c
195
+ }
196
+ return out
197
+ },
198
+ text: (b, start, end) => {
199
+ let out = ""
200
+ for (let i = start; i < end; i += 8192) {
201
+ out += String.fromCharCode.apply(null, b.subarray(i, Math.min(end, i + 8192)) as unknown as Array<number>)
202
+ }
203
+ return out
204
+ },
205
+ [FastLimit]: 0x100
206
+ } as Encoding<Uint8Array>
207
+
208
+ // -----------------------------------------------------------------------------
209
+ // Reading code points from a State
210
+ // -----------------------------------------------------------------------------
211
+
212
+ /** The width of the code point returned by the latest {@link next}. */
213
+ export let width = 0
214
+
215
+ /**
216
+ * Decodes the code point at the cursor without consuming it. Returns the
217
+ * code point (setting {@link width}), {@link End}, {@link Invalid}, or
218
+ * {@link Truncated} (only for partial input; on complete input a truncated
219
+ * sequence is {@link Invalid}).
220
+ */
221
+ export const next = (enc: Encoding<any>, state: State<any>, pos: number = state.pos): number => {
222
+ const end = state.end
223
+ if (pos >= end) return End
224
+ const cp = enc.decode(state.input, pos, end)
225
+ if (cp >= 0) {
226
+ width = enc.width(cp)
227
+ return cp
228
+ }
229
+ return cp === Truncated && !state.partial ? Invalid : cp
230
+ }
231
+
232
+ /**
233
+ * Fails a parser that expected a code point but found `cp` (one of the
234
+ * negative results of {@link next}, or a code point that did not match).
235
+ */
236
+ export const failAt = (
237
+ state: State<any>,
238
+ cp: number,
239
+ kind: Parameters<State["backtrack"]>[0],
240
+ expected: ReadonlyArray<string>,
241
+ offset: number = state.pos
242
+ ): Fail => {
243
+ if (cp === End) return state.eoi(kind, expected, 1)
244
+ if (cp === Truncated) return state.incomplete(1, expected)
245
+ return state.backtrack(kind, expected, offset)
246
+ }
247
+
248
+ // Results of `scan`, kept in module variables to avoid allocating
249
+ /** Offset reached by the latest {@link scan}. */
250
+ export let scanOffset = 0
251
+ /** Number of code points matched by the latest {@link scan}. */
252
+ export let scanCount = 0
253
+ /**
254
+ * Why the latest {@link scan} stopped: 0 = a non-matching code point or the
255
+ * maximum count, or one of {@link End}, {@link Invalid}, {@link Truncated}.
256
+ */
257
+ export let scanStop = 0
258
+
259
+ /**
260
+ * Scans code points from `pos` while `test(cp) !== negate`, up to `max`
261
+ * code points. `fast` is `fastLimit(enc)`, which callers can precompute.
262
+ */
263
+ export const scan = (
264
+ enc: Encoding<any>,
265
+ state: State<any>,
266
+ test: (cp: number) => boolean,
267
+ negate: boolean,
268
+ max: number,
269
+ pos: number = state.pos,
270
+ fast: number = fastLimit(enc)
271
+ ): void => {
272
+ const end = state.end
273
+ const decode = enc.decode
274
+ let i = pos
275
+ let count = 0
276
+ let stop = 0
277
+ if (state.inputKind === 0) {
278
+ const s = state.input as string
279
+ while (count < max) {
280
+ if (i >= end) {
281
+ stop = End
282
+ break
283
+ }
284
+ const c = s.charCodeAt(i)
285
+ if (c < fast) {
286
+ if (test(c) === negate) break
287
+ i++
288
+ } else {
289
+ const cp = decode(s, i, end)
290
+ if (cp < 0) {
291
+ stop = cp === Truncated && state.partial ? Truncated : Invalid
292
+ break
293
+ }
294
+ if (test(cp) === negate) break
295
+ i += enc.width(cp)
296
+ }
297
+ count++
298
+ }
299
+ } else {
300
+ const b = state.input as Uint8Array
301
+ while (count < max) {
302
+ if (i >= end) {
303
+ stop = End
304
+ break
305
+ }
306
+ const c = b[i]!
307
+ if (c < fast) {
308
+ if (test(c) === negate) break
309
+ i++
310
+ } else {
311
+ const cp = decode(b, i, end)
312
+ if (cp < 0) {
313
+ stop = cp === Truncated && state.partial ? Truncated : Invalid
314
+ break
315
+ }
316
+ if (test(cp) === negate) break
317
+ i += enc.width(cp)
318
+ }
319
+ count++
320
+ }
321
+ }
322
+ scanOffset = i
323
+ scanCount = count
324
+ scanStop = stop
325
+ }
326
+
327
+ const always = () => true
328
+
329
+ /**
330
+ * Checks that `[start, end)` is well formed, counting its code points.
331
+ * Returns the count, or `-1 - offset` of the first malformed sequence.
332
+ */
333
+ export const validate = (enc: Encoding<any>, state: State<any>, start: number, end: number): number => {
334
+ const savedEnd = state.end
335
+ const savedPartial = state.partial
336
+ state.end = end
337
+ state.partial = false
338
+ scan(enc, state, always, false, Infinity, start)
339
+ state.end = savedEnd
340
+ state.partial = savedPartial
341
+ return scanStop === Invalid ? -1 - scanOffset : scanCount
342
+ }
343
+
344
+ // -----------------------------------------------------------------------------
345
+ // Case folding
346
+ // -----------------------------------------------------------------------------
347
+
348
+ const foldCache = new Map<number, number>()
349
+
350
+ const single = (s: string): number => {
351
+ const cp = s.codePointAt(0)!
352
+ return s.length === (cp > 0xffff ? 2 : 1) ? cp : -1
353
+ }
354
+
355
+ /**
356
+ * Simple case folding of a code point: the lowercase of its uppercase, when
357
+ * both map to a single code point (so `"K"` (Kelvin) and `"ſ"` fold to `"k"`
358
+ * and `"s"`), otherwise the code point itself.
359
+ */
360
+ export const fold = (cp: number): number => {
361
+ if (cp < 128) return cp >= 65 && cp <= 90 ? cp + 32 : cp
362
+ let f = foldCache.get(cp)
363
+ if (f === undefined) {
364
+ const c = String.fromCodePoint(cp)
365
+ const upper = c.toUpperCase()
366
+ const viaUpper = single(upper) >= 0 ? single(upper.toLowerCase()) : -1
367
+ f = viaUpper >= 0 ? viaUpper : single(c.toLowerCase())
368
+ if (f < 0) f = cp
369
+ foldCache.set(cp, f)
370
+ }
371
+ return f
372
+ }
373
+
374
+ // -----------------------------------------------------------------------------
375
+ // Error rendering
376
+ // -----------------------------------------------------------------------------
377
+
378
+ /**
379
+ * Walks the code points of `[start, end)` leniently: malformed sequences
380
+ * are reported as `-1` and skipped one code unit at a time.
381
+ */
382
+ export const walk = (
383
+ enc: Encoding<any>,
384
+ input: string | Uint8Array,
385
+ start: number,
386
+ end: number,
387
+ f: (cp: number, offset: number, width: number) => boolean | void
388
+ ): void => {
389
+ let i = start
390
+ while (i < end) {
391
+ let cp = enc.decode(input, i, end)
392
+ let w: number
393
+ if (cp >= 0) {
394
+ w = enc.width(cp)
395
+ } else {
396
+ cp = -1
397
+ w = Math.min(enc.unit, end - i)
398
+ }
399
+ if (f(cp, i, w) === false) return
400
+ i += w
401
+ }
402
+ }
403
+
404
+ /** Decodes `[start, end)`, replacing malformed sequences with U+FFFD. */
405
+ export const lenientText = (enc: Encoding<any>, input: string | Uint8Array, start: number, end: number): string => {
406
+ let out = ""
407
+ walk(enc, input, start, end, (cp) => {
408
+ out += cp < 0 ? "�" : charOf(cp)
409
+ })
410
+ return out
411
+ }