@thomasfosterau/effect-parse 0.3.0 → 0.4.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/dist/Ascii.d.ts +0 -2
- package/dist/Ascii.d.ts.map +1 -1
- package/dist/Ascii.js +6 -6
- package/dist/Ascii.js.map +1 -1
- package/dist/Binary.d.ts +0 -2
- package/dist/Binary.d.ts.map +1 -1
- package/dist/Binary.js +1 -2
- package/dist/Bits.d.ts +0 -2
- package/dist/Bits.d.ts.map +1 -1
- package/dist/Bits.js +2 -3
- package/dist/Bits.js.map +1 -1
- package/dist/Compiler.d.ts +0 -2
- package/dist/Compiler.d.ts.map +1 -1
- package/dist/Compiler.js +1 -2
- package/dist/Encoding.d.ts +0 -2
- package/dist/Encoding.d.ts.map +1 -1
- package/dist/Encoding.js +1 -2
- package/dist/Expression.d.ts +0 -2
- package/dist/Expression.d.ts.map +1 -1
- package/dist/Expression.js +44 -8
- package/dist/Expression.js.map +1 -1
- package/dist/Input.d.ts +32 -4
- package/dist/Input.d.ts.map +1 -1
- package/dist/Input.js +47 -8
- package/dist/Input.js.map +1 -1
- package/dist/Nom.d.ts +0 -2
- package/dist/Nom.d.ts.map +1 -1
- package/dist/Nom.js +1 -2
- package/dist/ParseError.d.ts +0 -2
- package/dist/ParseError.d.ts.map +1 -1
- package/dist/ParseError.js +6 -9
- package/dist/ParseError.js.map +1 -1
- package/dist/Parser.d.ts +2 -2
- package/dist/Parser.d.ts.map +1 -1
- package/dist/Parser.js +26 -24
- package/dist/Parser.js.map +1 -1
- package/dist/ParserSchema.d.ts +0 -2
- package/dist/ParserSchema.d.ts.map +1 -1
- package/dist/ParserSchema.js +2 -3
- package/dist/ParserStream.d.ts +0 -2
- package/dist/ParserStream.d.ts.map +1 -1
- package/dist/ParserStream.js +15 -9
- package/dist/ParserStream.js.map +1 -1
- package/dist/Syntax.d.ts +0 -2
- package/dist/Syntax.d.ts.map +1 -1
- package/dist/Syntax.js +13 -11
- package/dist/Syntax.js.map +1 -1
- package/dist/Text.d.ts +0 -2
- package/dist/Text.d.ts.map +1 -1
- package/dist/Text.js +1 -2
- package/dist/Token.d.ts +0 -2
- package/dist/Token.d.ts.map +1 -1
- package/dist/Token.js +1 -2
- package/dist/internal/compiler/closure.d.ts +5 -2
- package/dist/internal/compiler/closure.d.ts.map +1 -1
- package/dist/internal/compiler/closure.js +160 -92
- package/dist/internal/compiler/closure.js.map +1 -1
- package/dist/internal/compiler/entries.d.ts +0 -2
- package/dist/internal/compiler/entries.d.ts.map +1 -1
- package/dist/internal/compiler/entries.js +6 -7
- package/dist/internal/compiler/entries.js.map +1 -1
- package/dist/internal/compiler/first.d.ts +0 -2
- package/dist/internal/compiler/first.d.ts.map +1 -1
- package/dist/internal/compiler/first.js +10 -10
- package/dist/internal/compiler/first.js.map +1 -1
- package/dist/internal/compiler/jit.d.ts +0 -2
- package/dist/internal/compiler/jit.d.ts.map +1 -1
- package/dist/internal/compiler/jit.js +52 -51
- package/dist/internal/compiler/jit.js.map +1 -1
- package/dist/internal/compiler/registry.d.ts +0 -2
- package/dist/internal/compiler/registry.d.ts.map +1 -1
- package/dist/internal/compiler/registry.js +1 -2
- package/dist/internal/compiler/sets.d.ts +2 -2
- package/dist/internal/compiler/sets.d.ts.map +1 -1
- package/dist/internal/compiler/sets.js +8 -3
- package/dist/internal/compiler/sets.js.map +1 -1
- package/dist/internal/entries.d.ts +0 -2
- package/dist/internal/entries.d.ts.map +1 -1
- package/dist/internal/entries.js +1 -2
- package/dist/internal/key.d.ts +10 -0
- package/dist/internal/key.d.ts.map +1 -0
- package/dist/internal/key.js +11 -0
- package/dist/internal/key.js.map +1 -0
- package/dist/internal/node.d.ts +11 -4
- package/dist/internal/node.d.ts.map +1 -1
- package/dist/internal/node.js +15 -3
- package/dist/internal/node.js.map +1 -1
- package/dist/internal/numbers.d.ts +0 -2
- package/dist/internal/numbers.d.ts.map +1 -1
- package/dist/internal/numbers.js +1 -2
- package/dist/internal/resume.d.ts +5 -6
- package/dist/internal/resume.d.ts.map +1 -1
- package/dist/internal/resume.js +1 -2
- package/dist/internal/resume.js.map +1 -1
- package/dist/internal/run.d.ts +0 -2
- package/dist/internal/run.d.ts.map +1 -1
- package/dist/internal/run.js +2 -3
- package/dist/internal/run.js.map +1 -1
- package/dist/internal/stack.d.ts +0 -2
- package/dist/internal/stack.d.ts.map +1 -1
- package/dist/internal/stack.js +1 -2
- package/dist/internal/syntax.d.ts +30 -20
- package/dist/internal/syntax.d.ts.map +1 -1
- package/dist/internal/syntax.js +73 -66
- package/dist/internal/syntax.js.map +1 -1
- package/dist/internal/text.d.ts +5 -2
- package/dist/internal/text.d.ts.map +1 -1
- package/dist/internal/text.js +14 -5
- package/dist/internal/text.js.map +1 -1
- package/dist/internal/ties.d.ts +0 -2
- package/dist/internal/ties.d.ts.map +1 -1
- package/dist/internal/ties.js +1 -2
- package/dist/internal/tokens.d.ts +10 -9
- package/dist/internal/tokens.d.ts.map +1 -1
- package/dist/internal/tokens.js +4 -8
- package/dist/internal/tokens.js.map +1 -1
- package/package.json +6 -6
- package/src/Ascii.ts +6 -5
- package/src/Bits.ts +1 -1
- package/src/Expression.ts +7 -6
- package/src/Input.ts +50 -11
- package/src/ParseError.ts +9 -7
- package/src/Parser.ts +26 -23
- package/src/ParserStream.ts +20 -8
- package/src/Syntax.ts +12 -9
- package/src/internal/compiler/closure.ts +202 -90
- package/src/internal/compiler/entries.ts +14 -11
- package/src/internal/compiler/first.ts +9 -8
- package/src/internal/compiler/jit.ts +95 -74
- package/src/internal/compiler/sets.ts +7 -0
- package/src/internal/key.ts +11 -0
- package/src/internal/node.ts +19 -2
- package/src/internal/resume.ts +5 -4
- package/src/internal/run.ts +1 -1
- package/src/internal/syntax.ts +78 -87
- package/src/internal/text.ts +24 -4
- package/src/internal/tokens.ts +20 -13
package/src/internal/syntax.ts
CHANGED
|
@@ -14,23 +14,32 @@
|
|
|
14
14
|
* completes and adopts them. Nodes built by an attempt that is later
|
|
15
15
|
* abandoned must not end up in the tree. Every combinator that abandons an
|
|
16
16
|
* attempt moves the cursor back to (or before) where the attempt started,
|
|
17
|
-
* and every node has a positive width, so the nodes of
|
|
18
|
-
* are exactly those that end after the position the
|
|
19
|
-
*
|
|
20
|
-
* lookahead (`peek`, `not`) and custom parsers written with
|
|
17
|
+
* with `State.rewind`, and every node has a positive width, so the nodes of
|
|
18
|
+
* an abandoned attempt are exactly those that end after the position the
|
|
19
|
+
* cursor moves back to: {@link SyntaxBuilder} drops them when told of the rewind. This also
|
|
20
|
+
* covers lookahead (`peek`, `not`) and custom parsers written with
|
|
21
|
+
* `Parser.make`, which rewind the same way.
|
|
22
|
+
*
|
|
23
|
+
* `pos` stays a plain field, not an accessor that would watch every move: a
|
|
24
|
+
* `pos` accessor on this subclass made every parser's cursor accesses
|
|
25
|
+
* polymorphic, slowing ordinary parses by about a fifth in any program that
|
|
26
|
+
* also built a syntax tree, and the syntax parse itself about twofold.
|
|
21
27
|
*
|
|
22
28
|
* For incremental parsing, every node records how far its parse looked: the
|
|
23
29
|
* furthest position the cursor reached and the furthest failure recorded
|
|
24
|
-
* while it was being parsed (its *lookahead*).
|
|
30
|
+
* while it was being parsed (its *lookahead*). The cursor only moves forward
|
|
31
|
+
* between rewinds, so the furthest position is where it was at a rewind, or
|
|
32
|
+
* where it is now. A node from the previous tree
|
|
25
33
|
* is reused at a position when it was built by the same `Syntax.node`, and
|
|
26
34
|
* the input it looked at, plus a margin for the tokens that read a little
|
|
27
35
|
* past where they stop, is unchanged.
|
|
28
36
|
*/
|
|
37
|
+
import * as Arr from "effect/Array"
|
|
29
38
|
import * as Predicate from "effect/Predicate"
|
|
30
|
-
import type * as Context from "effect/Context"
|
|
31
39
|
import type { Encoding } from "../Encoding.js"
|
|
32
|
-
import type { ErrMode, Input
|
|
40
|
+
import type { ErrMode, Input } from "../Input.js"
|
|
33
41
|
import { Fail, Failure, State } from "../Input.js"
|
|
42
|
+
import type { Key } from "./key.js"
|
|
34
43
|
|
|
35
44
|
/** What a failure inside an `ERROR` node was. Offsets are relative to the node. */
|
|
36
45
|
export interface ErrorInfo {
|
|
@@ -44,7 +53,7 @@ export class Green {
|
|
|
44
53
|
readonly kind: string,
|
|
45
54
|
readonly named: boolean,
|
|
46
55
|
/** The `Syntax.node` that built it; `undefined` for roots and errors, which are never reused alone. */
|
|
47
|
-
readonly key:
|
|
56
|
+
readonly key: Key | undefined,
|
|
48
57
|
readonly width: number,
|
|
49
58
|
/** How far its parse looked, relative to its start (at least `width`). */
|
|
50
59
|
readonly lookahead: number,
|
|
@@ -103,7 +112,7 @@ export const absolute = (f: Failure, start: number): Failure => rebase(f, -start
|
|
|
103
112
|
const build = (
|
|
104
113
|
kind: string,
|
|
105
114
|
named: boolean,
|
|
106
|
-
key:
|
|
115
|
+
key: Key | undefined,
|
|
107
116
|
start: number,
|
|
108
117
|
end: number,
|
|
109
118
|
lookahead: number,
|
|
@@ -153,7 +162,7 @@ export interface Previous {
|
|
|
153
162
|
|
|
154
163
|
/** The hooks that `Syntax` combinators and `Parser.resumeAfter`/`retryAfter` call. */
|
|
155
164
|
export interface Builder {
|
|
156
|
-
node(state: State<any>, step: (state: State<any>) => unknown, key:
|
|
165
|
+
node(state: State<any>, step: (state: State<any>) => unknown, key: Key, kind: string, named: boolean): unknown
|
|
157
166
|
field(state: State<any>, step: (state: State<any>) => unknown, name: string): unknown
|
|
158
167
|
/** After error recovery: `[start, pos)` was skipped after `failure`. */
|
|
159
168
|
skipped(start: number, failure: Failure, mode: ErrMode): void
|
|
@@ -162,79 +171,61 @@ export interface Builder {
|
|
|
162
171
|
* where they leave the cursor or record a failure.
|
|
163
172
|
*/
|
|
164
173
|
read(end: number): void
|
|
174
|
+
/** A failure or hint was recorded at `offset`. */
|
|
175
|
+
reach(offset: number): void
|
|
176
|
+
/** The cursor moved back from `from` to `to` (see `State.rewind`). */
|
|
177
|
+
rewound(from: number, to: number): void
|
|
165
178
|
}
|
|
166
179
|
|
|
167
|
-
/**
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
180
|
+
/**
|
|
181
|
+
* The tree builder of a `Syntax` parse, reached through `state.syntax`. It
|
|
182
|
+
* is not a `State` subclass: one class of state keeps every parser's cursor
|
|
183
|
+
* and failure accesses monomorphic, whether or not the program also builds
|
|
184
|
+
* syntax trees. `State` calls {@link reach} and {@link rewound} itself.
|
|
185
|
+
*/
|
|
186
|
+
export class SyntaxBuilder implements Builder {
|
|
187
|
+
/**
|
|
188
|
+
* The furthest position the cursor reached before a rewind, or failure
|
|
189
|
+
* recorded, since the innermost open node started; {@link looked} adds
|
|
190
|
+
* where the cursor is now.
|
|
191
|
+
*/
|
|
192
|
+
highWater: number
|
|
174
193
|
/** Completed nodes waiting for their parent, in input order. */
|
|
175
|
-
|
|
176
|
-
declare previous: Previous | undefined
|
|
194
|
+
readonly stack: Array<Child> = []
|
|
177
195
|
/** How many nodes were reused from the previous tree. */
|
|
178
|
-
|
|
196
|
+
reused = 0
|
|
179
197
|
/** Where {@link find} found the last node it found. */
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
static {
|
|
184
|
-
Object.defineProperty(this.prototype, "pos", {
|
|
185
|
-
get(this: SyntaxState) {
|
|
186
|
-
return this.cursor
|
|
187
|
-
},
|
|
188
|
-
set(this: SyntaxState, p: number) {
|
|
189
|
-
if (p < this.cursor) {
|
|
190
|
-
// An attempt was abandoned (or a lookahead finished): drop its nodes
|
|
191
|
-
const stack = this.stack
|
|
192
|
-
while (stack.length > 0 && stack[stack.length - 1].end > p) stack.pop()
|
|
193
|
-
}
|
|
194
|
-
if (p > this.highWater) this.highWater = p
|
|
195
|
-
this.cursor = p
|
|
196
|
-
},
|
|
197
|
-
configurable: true,
|
|
198
|
-
enumerable: true
|
|
199
|
-
})
|
|
200
|
-
}
|
|
198
|
+
lastParent: Green | undefined = undefined
|
|
199
|
+
lastParentStart = 0
|
|
201
200
|
|
|
202
201
|
constructor(
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
readonly context?: Context.Context<never> | undefined
|
|
206
|
-
},
|
|
207
|
-
previous: Previous | undefined
|
|
202
|
+
readonly state: State<any>,
|
|
203
|
+
readonly previous: Previous | undefined
|
|
208
204
|
) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
this.stack = []
|
|
212
|
-
this.previous = previous
|
|
213
|
-
this.reused = 0
|
|
214
|
-
this.lastParent = undefined
|
|
215
|
-
this.lastParentStart = 0
|
|
216
|
-
this.syntax = this
|
|
205
|
+
this.highWater = state.pos
|
|
206
|
+
state.syntax = this
|
|
217
207
|
// Error recovery is always on: like Tree-sitter, a syntax parse always produces a tree
|
|
218
|
-
|
|
208
|
+
state.recovered = []
|
|
219
209
|
}
|
|
220
210
|
|
|
221
|
-
|
|
222
|
-
if (
|
|
223
|
-
return super.failWith(mode, failure)
|
|
211
|
+
reach(offset: number): void {
|
|
212
|
+
if (offset > this.highWater) this.highWater = offset
|
|
224
213
|
}
|
|
225
214
|
|
|
226
|
-
|
|
227
|
-
if (
|
|
228
|
-
|
|
215
|
+
rewound(from: number, to: number): void {
|
|
216
|
+
if (from > this.highWater) this.highWater = from
|
|
217
|
+
// An attempt was abandoned (or a lookahead finished): drop its nodes
|
|
218
|
+
const stack = this.stack
|
|
219
|
+
while (Arr.isArrayNonEmpty(stack) && Arr.lastNonEmpty(stack).end > to) stack.pop()
|
|
229
220
|
}
|
|
230
221
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
222
|
+
/** How far the parse looked: {@link highWater}, or the cursor if it is further. */
|
|
223
|
+
private looked(): number {
|
|
224
|
+
return this.state.pos > this.highWater ? this.state.pos : this.highWater
|
|
234
225
|
}
|
|
235
226
|
|
|
236
|
-
node(state: State<any>, step: (state: State<any>) => unknown, key:
|
|
237
|
-
const start = this.
|
|
227
|
+
node(state: State<any>, step: (state: State<any>) => unknown, key: Key, kind: string, named: boolean): unknown {
|
|
228
|
+
const start = this.state.pos
|
|
238
229
|
const stack = this.stack
|
|
239
230
|
if (this.previous !== undefined) {
|
|
240
231
|
const green = this.reusable(this.previous, key, start)
|
|
@@ -247,7 +238,7 @@ export class SyntaxState<I extends Input = Input> extends State<I> implements Bu
|
|
|
247
238
|
const d = green.diagnostics
|
|
248
239
|
if (d !== undefined) {
|
|
249
240
|
if (d.deepest !== undefined) this.deepen(start === d.base ? d.deepest : rebase(d.deepest, d.base - start))
|
|
250
|
-
if (d.hintExpected !== undefined) this.setHint(start + d.hintOffset, d.hintExpected)
|
|
241
|
+
if (d.hintExpected !== undefined) this.state.setHint(start + d.hintOffset, d.hintExpected)
|
|
251
242
|
}
|
|
252
243
|
stack.push(new Child(green, start, end, undefined))
|
|
253
244
|
return green.value
|
|
@@ -255,34 +246,34 @@ export class SyntaxState<I extends Input = Input> extends State<I> implements Bu
|
|
|
255
246
|
}
|
|
256
247
|
const mark = stack.length
|
|
257
248
|
const outer = this.highWater
|
|
258
|
-
const outerDeepest = this.deepest
|
|
259
|
-
const hintOffset = this.hintOffset
|
|
260
|
-
const hintExpected = this.hintExpected
|
|
249
|
+
const outerDeepest = this.state.deepest
|
|
250
|
+
const hintOffset = this.state.hintOffset
|
|
251
|
+
const hintExpected = this.state.hintExpected
|
|
261
252
|
this.highWater = start
|
|
262
253
|
// Collect the furthest failure of this node alone, then merge it as `failWith` would have
|
|
263
|
-
this.deepest = undefined
|
|
254
|
+
this.state.deepest = undefined
|
|
264
255
|
const a = step(state)
|
|
265
|
-
const looked = this.
|
|
266
|
-
|
|
267
|
-
const deepest = this.deepest as Failure | undefined
|
|
268
|
-
this.deepest = outerDeepest
|
|
256
|
+
const looked = this.looked()
|
|
257
|
+
this.highWater = outer > looked ? outer : looked
|
|
258
|
+
const deepest = this.state.deepest as Failure | undefined
|
|
259
|
+
this.state.deepest = outerDeepest
|
|
269
260
|
if (deepest !== undefined) this.deepen(deepest)
|
|
270
261
|
if (a === Fail) {
|
|
271
262
|
if (stack.length > mark) stack.length = mark
|
|
272
263
|
return a
|
|
273
264
|
}
|
|
274
|
-
const end = this.
|
|
265
|
+
const end = this.state.pos
|
|
275
266
|
// Like Tree-sitter's rules, nodes are never empty: an empty match adds nothing to the tree
|
|
276
267
|
if (end === start) return a
|
|
277
|
-
const hinted = this.hintOffset !== hintOffset || this.hintExpected !== hintExpected
|
|
268
|
+
const hinted = this.state.hintOffset !== hintOffset || this.state.hintExpected !== hintExpected
|
|
278
269
|
const diagnostics: Diagnostics | undefined =
|
|
279
270
|
deepest === undefined && !hinted
|
|
280
271
|
? undefined
|
|
281
272
|
: {
|
|
282
273
|
deepest,
|
|
283
274
|
base: start,
|
|
284
|
-
hintOffset: this.hintOffset - start,
|
|
285
|
-
hintExpected: hinted ? this.hintExpected : undefined
|
|
275
|
+
hintOffset: this.state.hintOffset - start,
|
|
276
|
+
hintExpected: hinted ? this.state.hintExpected : undefined
|
|
286
277
|
}
|
|
287
278
|
const entries = stack.splice(mark)
|
|
288
279
|
stack.push(
|
|
@@ -298,7 +289,7 @@ export class SyntaxState<I extends Input = Input> extends State<I> implements Bu
|
|
|
298
289
|
|
|
299
290
|
/** Records `failure` as the furthest failure if it is, as `failWith` does. */
|
|
300
291
|
private deepen(failure: Failure): void {
|
|
301
|
-
if (this.deepest === undefined || failure.offset >= this.deepest.offset) this.deepest = failure
|
|
292
|
+
if (this.state.deepest === undefined || failure.offset >= this.state.deepest.offset) this.state.deepest = failure
|
|
302
293
|
}
|
|
303
294
|
|
|
304
295
|
field(state: State<any>, step: (state: State<any>) => unknown, name: string): unknown {
|
|
@@ -313,8 +304,8 @@ export class SyntaxState<I extends Input = Input> extends State<I> implements Bu
|
|
|
313
304
|
const stack = this.stack
|
|
314
305
|
let i = stack.length
|
|
315
306
|
while (i > 0 && stack[i - 1].start >= start && stack[i - 1].end > start) i--
|
|
316
|
-
const end = this.
|
|
317
|
-
const green = build(errorKind, true, undefined, start, end, this.
|
|
307
|
+
const end = this.state.pos
|
|
308
|
+
const green = build(errorKind, true, undefined, start, end, this.looked(), stack.splice(i), undefined, {
|
|
318
309
|
failure: rebase(failure, start),
|
|
319
310
|
mode
|
|
320
311
|
})
|
|
@@ -328,9 +319,9 @@ export class SyntaxState<I extends Input = Input> extends State<I> implements Bu
|
|
|
328
319
|
*/
|
|
329
320
|
finish(kind: string, value: unknown, fatal: ErrorInfo | undefined, errorStart: number): Green {
|
|
330
321
|
const entries = this.stack.splice(0)
|
|
331
|
-
const end = this.end
|
|
322
|
+
const end = this.state.end
|
|
332
323
|
if (fatal !== undefined) {
|
|
333
|
-
const last = entries
|
|
324
|
+
const last = Arr.isArrayNonEmpty(entries) ? Arr.lastNonEmpty(entries).end : 0
|
|
334
325
|
const start = Math.max(Math.min(errorStart, end), last)
|
|
335
326
|
const error = build(errorKind, true, undefined, start, end, end, [], undefined, {
|
|
336
327
|
failure: rebase(fatal.failure, start),
|
|
@@ -338,7 +329,7 @@ export class SyntaxState<I extends Input = Input> extends State<I> implements Bu
|
|
|
338
329
|
})
|
|
339
330
|
entries.push(new Child(error, start, end, undefined))
|
|
340
331
|
}
|
|
341
|
-
return build(kind, true, undefined, 0, end, Math.max(end, this.
|
|
332
|
+
return build(kind, true, undefined, 0, end, Math.max(end, this.looked()), entries, value, undefined)
|
|
342
333
|
}
|
|
343
334
|
|
|
344
335
|
/**
|
|
@@ -347,7 +338,7 @@ export class SyntaxState<I extends Input = Input> extends State<I> implements Bu
|
|
|
347
338
|
* so the search starts from the parent of the last one found when
|
|
348
339
|
* `offset` lies inside it: no ancestor of that parent can start there.
|
|
349
340
|
*/
|
|
350
|
-
private find(root: Green, offset: number, key:
|
|
341
|
+
private find(root: Green, offset: number, key: Key): Green | undefined {
|
|
351
342
|
let node = root
|
|
352
343
|
let start = 0
|
|
353
344
|
const last = this.lastParent
|
|
@@ -385,7 +376,7 @@ export class SyntaxState<I extends Input = Input> extends State<I> implements Bu
|
|
|
385
376
|
}
|
|
386
377
|
|
|
387
378
|
/** The node of the previous tree built by `key` at `pos` (in the new input), if it can be reused. */
|
|
388
|
-
private reusable(previous: Previous, key:
|
|
379
|
+
private reusable(previous: Previous, key: Key, pos: number): Green | undefined {
|
|
389
380
|
const segments = previous.segments
|
|
390
381
|
// The segment containing `pos`
|
|
391
382
|
let lo = 0
|
package/src/internal/text.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import * as Arr from "effect/Array"
|
|
2
|
+
import * as Predicate from "effect/Predicate"
|
|
3
|
+
import * as Str from "effect/String"
|
|
1
4
|
import type { Encoding } from "../Encoding.js"
|
|
2
5
|
import type { Fail, State } from "../Input.js"
|
|
3
6
|
|
|
@@ -22,6 +25,17 @@ export const End = -3
|
|
|
22
25
|
*/
|
|
23
26
|
const FastLimit: unique symbol = /*#__PURE__*/ Symbol.for("effect-parse/Encoding/FastLimit")
|
|
24
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Whether an input, a literal or a chunk has no units (characters, bytes or
|
|
30
|
+
* tokens).
|
|
31
|
+
*/
|
|
32
|
+
export const isEmptyUnits = (units: string | Uint8Array | ReadonlyArray<unknown>): boolean =>
|
|
33
|
+
Predicate.isString(units)
|
|
34
|
+
? Str.isEmpty(units)
|
|
35
|
+
: units instanceof Uint8Array
|
|
36
|
+
? units.byteLength === 0
|
|
37
|
+
: Arr.isReadonlyArrayEmpty(units)
|
|
38
|
+
|
|
25
39
|
/** @internal */
|
|
26
40
|
export const fastLimit = (enc: Encoding<any>): number =>
|
|
27
41
|
(enc as any)[FastLimit] ?? (enc.asciiCompatible && enc.unit === 1 ? 0x80 : 0)
|
|
@@ -115,6 +129,7 @@ const decodeRange = (decoder: TextDecoder, b: Uint8Array, start: number, end: nu
|
|
|
115
129
|
const utf8Encoder = /*#__PURE__*/ new TextEncoder()
|
|
116
130
|
|
|
117
131
|
/** UTF-8 bytes. */
|
|
132
|
+
// SAFETY: an `Encoding` with the internal `FastLimit` key, which its type doesn't declare
|
|
118
133
|
export const utf8: Encoding<Uint8Array> = {
|
|
119
134
|
name: "UTF-8",
|
|
120
135
|
unit: 1,
|
|
@@ -137,13 +152,17 @@ export const utf8: Encoding<Uint8Array> = {
|
|
|
137
152
|
}
|
|
138
153
|
}
|
|
139
154
|
return out
|
|
140
|
-
}
|
|
141
|
-
|
|
155
|
+
},
|
|
156
|
+
// Every built-in encoding has the same keys in the same order, so code
|
|
157
|
+
// reading them sees one object shape
|
|
158
|
+
[FastLimit]: 0x80
|
|
159
|
+
} as Encoding<Uint8Array>
|
|
142
160
|
|
|
143
161
|
const makeUtf16 = (littleEndian: boolean): Encoding<Uint8Array> => {
|
|
144
162
|
const lo = littleEndian ? 0 : 1
|
|
145
163
|
const hi = 1 - lo
|
|
146
164
|
let decoder: TextDecoder | undefined
|
|
165
|
+
// SAFETY: an `Encoding` with the internal `FastLimit` key, which its type doesn't declare
|
|
147
166
|
return {
|
|
148
167
|
name: littleEndian ? "UTF-16LE" : "UTF-16BE",
|
|
149
168
|
unit: 2,
|
|
@@ -176,8 +195,9 @@ const makeUtf16 = (littleEndian: boolean): Encoding<Uint8Array> => {
|
|
|
176
195
|
}
|
|
177
196
|
decoder ??= new TextDecoder(littleEndian ? "utf-16le" : "utf-16be", { ignoreBOM: true })
|
|
178
197
|
return decodeRange(decoder, b, start, end)
|
|
179
|
-
}
|
|
180
|
-
|
|
198
|
+
},
|
|
199
|
+
[FastLimit]: 0
|
|
200
|
+
} as Encoding<Uint8Array>
|
|
181
201
|
}
|
|
182
202
|
|
|
183
203
|
/** UTF-16 bytes, little endian. */
|
package/src/internal/tokens.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// fallow-ignore-file duplicate-export -- `End` and `scan` here are the token-set versions of the text
|
|
2
2
|
// helpers in ./text.ts; both are internal modules, imported by path, never re-exported from one barrel.
|
|
3
3
|
import * as Predicate from "effect/Predicate"
|
|
4
|
+
import * as Result from "effect/Result"
|
|
4
5
|
import type { Input, State } from "../Input.js"
|
|
5
6
|
import { charOf, firstCodePoint } from "./text.js"
|
|
6
7
|
|
|
@@ -160,11 +161,10 @@ const compileCodeSafe = (set: unknown, kind: "string" | "code"): ((code: number)
|
|
|
160
161
|
return false
|
|
161
162
|
}
|
|
162
163
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
164
|
+
return Result.getOrElse(
|
|
165
|
+
Result.try(() => compileCode(set)),
|
|
166
|
+
(): ((code: number) => boolean) => () => false
|
|
167
|
+
)
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
/**
|
|
@@ -276,17 +276,18 @@ export const nextIf = (state: State<Input>, m: AnyMatcher, negate: boolean): unk
|
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
+
/** How far a {@link scan} got. */
|
|
280
|
+
export interface Scanned extends Advanced {
|
|
281
|
+
/** Whether the end of input was hit before `max` tokens. */
|
|
282
|
+
readonly hitEnd: boolean
|
|
283
|
+
}
|
|
284
|
+
|
|
279
285
|
/**
|
|
280
286
|
* Scans forward while tokens satisfy the matcher (xor `negate`), up to `max`
|
|
281
287
|
* tokens. Returns the offset reached, the number of tokens matched, and
|
|
282
288
|
* whether the end of input was hit before `max` tokens.
|
|
283
289
|
*/
|
|
284
|
-
export const scan = (
|
|
285
|
-
state: State<Input>,
|
|
286
|
-
m: AnyMatcher,
|
|
287
|
-
negate: boolean,
|
|
288
|
-
max: number
|
|
289
|
-
): { readonly offset: number; readonly count: number; readonly hitEnd: boolean } => {
|
|
290
|
+
export const scan = (state: State<Input>, m: AnyMatcher, negate: boolean, max: number): Scanned => {
|
|
290
291
|
let i = state.pos
|
|
291
292
|
const end = state.end
|
|
292
293
|
let count = 0
|
|
@@ -335,8 +336,14 @@ export const scan = (
|
|
|
335
336
|
}
|
|
336
337
|
}
|
|
337
338
|
|
|
339
|
+
/** The offset reached and the number of tokens passed. */
|
|
340
|
+
export interface Advanced {
|
|
341
|
+
readonly offset: number
|
|
342
|
+
readonly count: number
|
|
343
|
+
}
|
|
344
|
+
|
|
338
345
|
/** Advances `n` tokens from `start`, returning the offset reached and the count. */
|
|
339
|
-
export const advance = (state: State<Input>, start: number, n: number):
|
|
346
|
+
export const advance = (state: State<Input>, start: number, n: number): Advanced => {
|
|
340
347
|
if (state.inputKind !== 0) {
|
|
341
348
|
const offset = Math.min(state.end, start + n)
|
|
342
349
|
return { offset, count: offset - start }
|
|
@@ -379,7 +386,7 @@ export const indexOf = (state: State<Input>, needle: Input, from: number): numbe
|
|
|
379
386
|
case 1: {
|
|
380
387
|
const hay = state.input as Uint8Array
|
|
381
388
|
const n = needle as Uint8Array
|
|
382
|
-
if (n.
|
|
389
|
+
if (n.byteLength === 0) return from
|
|
383
390
|
const first = n[0]
|
|
384
391
|
let i = hay.indexOf(first, from)
|
|
385
392
|
while (i !== -1 && i <= last) {
|