@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/Parser.ts
CHANGED
|
@@ -45,6 +45,7 @@ import type { ErrorKind } from "./ParseError.js"
|
|
|
45
45
|
import * as ParseError from "./ParseError.js"
|
|
46
46
|
import * as Registry from "./internal/compiler/registry.js"
|
|
47
47
|
import * as Entries from "./internal/entries.js"
|
|
48
|
+
import { Key } from "./internal/key.js"
|
|
48
49
|
import * as Resume from "./internal/resume.js"
|
|
49
50
|
import * as Run from "./internal/run.js"
|
|
50
51
|
import * as Stack from "./internal/stack.js"
|
|
@@ -273,6 +274,8 @@ const isTwoParserArgs = (args: IArguments) => isParser(args[0]) && isParser(args
|
|
|
273
274
|
* The step function must either return the parsed value, having advanced
|
|
274
275
|
* `state.pos` past the consumed input, or return the result of one of
|
|
275
276
|
* `state.backtrack`, `state.cut`, `state.incomplete`, ... to signal failure.
|
|
277
|
+
* A step that moves the cursor backwards (to retry, or after a lookahead)
|
|
278
|
+
* calls `state.rewind(offset)` rather than assigning `state.pos`.
|
|
276
279
|
*
|
|
277
280
|
* @example
|
|
278
281
|
* ```ts
|
|
@@ -723,7 +726,7 @@ export const tryMap: {
|
|
|
723
726
|
if (a === Fail) return Fail
|
|
724
727
|
const r = f(a)
|
|
725
728
|
if (Result.isSuccess(r)) return r.success
|
|
726
|
-
state.
|
|
729
|
+
state.rewind(start)
|
|
727
730
|
return state.failCause("Backtrack", "Verify", r.failure)
|
|
728
731
|
},
|
|
729
732
|
self.name,
|
|
@@ -777,7 +780,7 @@ export const filterMap: {
|
|
|
777
780
|
if (a === Fail) return Fail
|
|
778
781
|
const o = f(a)
|
|
779
782
|
if (Option.isSome(o)) return o.value
|
|
780
|
-
state.
|
|
783
|
+
state.rewind(start)
|
|
781
784
|
return state.backtrack("Verify")
|
|
782
785
|
},
|
|
783
786
|
self.name,
|
|
@@ -808,7 +811,7 @@ export const filter: {
|
|
|
808
811
|
const a = step(state)
|
|
809
812
|
if (a === Fail) return Fail
|
|
810
813
|
if (predicate(a)) return a
|
|
811
|
-
state.
|
|
814
|
+
state.rewind(start)
|
|
812
815
|
return state.backtrack("Verify")
|
|
813
816
|
},
|
|
814
817
|
self.name,
|
|
@@ -852,7 +855,7 @@ export const filterOrFail: {
|
|
|
852
855
|
const a = step(state)
|
|
853
856
|
if (a === Fail) return Fail
|
|
854
857
|
if (predicate(a)) return a
|
|
855
|
-
state.
|
|
858
|
+
state.rewind(start)
|
|
856
859
|
return state.failCause("Backtrack", "Verify", orFailWith(a))
|
|
857
860
|
},
|
|
858
861
|
self.name,
|
|
@@ -1032,7 +1035,7 @@ export const complete = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Pa
|
|
|
1032
1035
|
const start = state.pos
|
|
1033
1036
|
const a = step(state)
|
|
1034
1037
|
if (a === Fail && state.mode === "Incomplete") {
|
|
1035
|
-
state.
|
|
1038
|
+
state.rewind(start)
|
|
1036
1039
|
return state.backtrack("Complete")
|
|
1037
1040
|
}
|
|
1038
1041
|
return a
|
|
@@ -1223,7 +1226,7 @@ export const orElseSucceed: {
|
|
|
1223
1226
|
|
|
1224
1227
|
/** Rewinds after a recoverable failure, remembering it as a hint. */
|
|
1225
1228
|
const recover = (state: State<any>, start: number): void => {
|
|
1226
|
-
state.
|
|
1229
|
+
state.rewind(start)
|
|
1227
1230
|
const f = state.failure
|
|
1228
1231
|
if (f.offset === start) {
|
|
1229
1232
|
state.hintOffset = start
|
|
@@ -1297,7 +1300,7 @@ export const peek = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser
|
|
|
1297
1300
|
if (a !== Fail) {
|
|
1298
1301
|
// Failures inside a lookahead that succeeded are about input that was
|
|
1299
1302
|
// never consumed, so they are not reported as the furthest failure
|
|
1300
|
-
state.
|
|
1303
|
+
state.rewind(start)
|
|
1301
1304
|
state.hintOffset = hintOffset
|
|
1302
1305
|
state.hintExpected = hintExpected
|
|
1303
1306
|
state.deepest = deepest
|
|
@@ -1327,12 +1330,12 @@ export const not = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<
|
|
|
1327
1330
|
const a = step(state)
|
|
1328
1331
|
if (a !== Fail) {
|
|
1329
1332
|
// The failures inside `self` are not this parser's failure
|
|
1330
|
-
state.
|
|
1333
|
+
state.rewind(start)
|
|
1331
1334
|
state.deepest = deepest
|
|
1332
1335
|
return state.backtrack("Not")
|
|
1333
1336
|
}
|
|
1334
1337
|
if (state.mode !== "Backtrack") return Fail
|
|
1335
|
-
state.
|
|
1338
|
+
state.rewind(start)
|
|
1336
1339
|
state.hintOffset = hintOffset
|
|
1337
1340
|
state.hintExpected = hintExpected
|
|
1338
1341
|
state.deepest = deepest
|
|
@@ -1807,7 +1810,7 @@ export const alt = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1807
1810
|
return a
|
|
1808
1811
|
}
|
|
1809
1812
|
if (state.mode !== "Backtrack") return Fail
|
|
1810
|
-
state.
|
|
1813
|
+
state.rewind(start)
|
|
1811
1814
|
// The branch's failure, whose own ties (from a nested `alt`) can be taken over unmerged
|
|
1812
1815
|
const head = state.failureHead
|
|
1813
1816
|
if (failure === undefined || head.offset > failure.offset) {
|
|
@@ -1906,7 +1909,7 @@ export const dispatch: {
|
|
|
1906
1909
|
if (key === Fail) return Fail
|
|
1907
1910
|
const parser = choose(key)
|
|
1908
1911
|
if (parser === undefined) {
|
|
1909
|
-
state.
|
|
1912
|
+
state.rewind(start)
|
|
1910
1913
|
return state.backtrack("Dispatch")
|
|
1911
1914
|
}
|
|
1912
1915
|
return parser.step(state)
|
|
@@ -1961,7 +1964,7 @@ export const permutation = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1961
1964
|
break
|
|
1962
1965
|
}
|
|
1963
1966
|
if (state.mode !== "Backtrack") return Fail
|
|
1964
|
-
state.
|
|
1967
|
+
state.rewind(start)
|
|
1965
1968
|
ties.add(state)
|
|
1966
1969
|
}
|
|
1967
1970
|
// Some parser is not done yet, and each one that fails is folded in
|
|
@@ -1995,7 +1998,7 @@ interface FoldCheckpoint<B> {
|
|
|
1995
1998
|
readonly pos: number
|
|
1996
1999
|
}
|
|
1997
2000
|
|
|
1998
|
-
const saveFold = <B>(state: State<any>, key:
|
|
2001
|
+
const saveFold = <B>(state: State<any>, key: Key, entry: number, acc: B, pos: number): void =>
|
|
1999
2002
|
Resume.save(state, key, entry, { acc, count: 0, pos } satisfies FoldCheckpoint<B>)
|
|
2000
2003
|
|
|
2001
2004
|
const foldImpl = <A, B, E, R, I extends Input>(
|
|
@@ -2010,7 +2013,7 @@ const foldImpl = <A, B, E, R, I extends Input>(
|
|
|
2010
2013
|
const [min, max] = normalizeRange(range)
|
|
2011
2014
|
const step = self.step
|
|
2012
2015
|
const checkLoop = max === Infinity
|
|
2013
|
-
const key =
|
|
2016
|
+
const key = new Key()
|
|
2014
2017
|
return described(
|
|
2015
2018
|
(state) => {
|
|
2016
2019
|
const entry = state.pos
|
|
@@ -2189,7 +2192,7 @@ export const repeatTill: {
|
|
|
2189
2192
|
if (b !== Fail) return [acc, b]
|
|
2190
2193
|
if (state.mode !== "Backtrack") return Fail
|
|
2191
2194
|
endFailure = state.failure
|
|
2192
|
-
state.
|
|
2195
|
+
state.rewind(start)
|
|
2193
2196
|
if (acc.length >= max) return Fail
|
|
2194
2197
|
}
|
|
2195
2198
|
const a = step(state)
|
|
@@ -2252,7 +2255,7 @@ export const separated: {
|
|
|
2252
2255
|
const step = self.step
|
|
2253
2256
|
const sepStep = separator.step
|
|
2254
2257
|
const checkLoop = max === Infinity
|
|
2255
|
-
const key =
|
|
2258
|
+
const key = new Key()
|
|
2256
2259
|
return described(
|
|
2257
2260
|
(state) => {
|
|
2258
2261
|
const entry = state.pos
|
|
@@ -2295,7 +2298,7 @@ export const separated: {
|
|
|
2295
2298
|
Resume.save(state, key, entry, { acc, pos: start } satisfies SeparatedCheckpoint<A>)
|
|
2296
2299
|
}
|
|
2297
2300
|
if (state.mode !== "Backtrack" || acc.length < min) return Fail
|
|
2298
|
-
state.
|
|
2301
|
+
state.rewind(start)
|
|
2299
2302
|
return Resume.done(state, key, entry, resumed !== undefined, acc)
|
|
2300
2303
|
}
|
|
2301
2304
|
acc.push(a)
|
|
@@ -2336,7 +2339,7 @@ export const separatedFoldl1 = <A, E, R, I extends Input, S, E2, R2, I2 extends
|
|
|
2336
2339
|
): Parser<A, E | E2, R | R2, I & I2> => {
|
|
2337
2340
|
const step = self.step
|
|
2338
2341
|
const sepStep = separator.step
|
|
2339
|
-
const key =
|
|
2342
|
+
const key = new Key()
|
|
2340
2343
|
return described(
|
|
2341
2344
|
(state) => {
|
|
2342
2345
|
const entry = state.pos
|
|
@@ -2367,7 +2370,7 @@ export const separatedFoldl1 = <A, E, R, I extends Input, S, E2, R2, I2 extends
|
|
|
2367
2370
|
if (a === Fail) {
|
|
2368
2371
|
if (state.mode === "Incomplete" && state.resume !== undefined) saveFold(state, key, entry, acc, start)
|
|
2369
2372
|
if (state.mode !== "Backtrack") return Fail
|
|
2370
|
-
state.
|
|
2373
|
+
state.rewind(start)
|
|
2371
2374
|
return Resume.done(state, key, entry, resumed !== undefined, acc as A)
|
|
2372
2375
|
}
|
|
2373
2376
|
acc = op(acc, s, a)
|
|
@@ -2412,7 +2415,7 @@ export const separatedFoldr1 = <A, E, R, I extends Input, S, E2, R2, I2 extends
|
|
|
2412
2415
|
const a = step(state)
|
|
2413
2416
|
if (a === Fail) {
|
|
2414
2417
|
if (state.mode !== "Backtrack") return Fail
|
|
2415
|
-
state.
|
|
2418
|
+
state.rewind(start)
|
|
2416
2419
|
break
|
|
2417
2420
|
}
|
|
2418
2421
|
seps.push(s)
|
|
@@ -2584,7 +2587,7 @@ export const resumeAfter: {
|
|
|
2584
2587
|
if (state.recovered === undefined || state.mode === "Incomplete") return Fail
|
|
2585
2588
|
const failure = state.failure
|
|
2586
2589
|
const mode = state.mode
|
|
2587
|
-
state.
|
|
2590
|
+
state.rewind(start)
|
|
2588
2591
|
if (recoverStep(state) === Fail) return state.failWith(mode, failure)
|
|
2589
2592
|
state.recovered.push(failure)
|
|
2590
2593
|
state.syntax?.skipped(start, failure, mode)
|
|
@@ -2631,7 +2634,7 @@ export const retryAfter: {
|
|
|
2631
2634
|
if (state.recovered === undefined || state.mode === "Incomplete") return Fail
|
|
2632
2635
|
const failure = state.failure
|
|
2633
2636
|
const mode = state.mode
|
|
2634
|
-
state.
|
|
2637
|
+
state.rewind(start)
|
|
2635
2638
|
if (recoverStep(state) === Fail || state.pos === start) return state.failWith(mode, failure)
|
|
2636
2639
|
state.recovered.push(failure)
|
|
2637
2640
|
state.syntax?.skipped(start, failure, mode)
|
|
@@ -2708,7 +2711,7 @@ const preview = (state: State<any>, offset: number): string => {
|
|
|
2708
2711
|
*/
|
|
2709
2712
|
export const consoleTracer = (event: TraceEvent): void => {
|
|
2710
2713
|
const indent = " ".repeat(event.depth)
|
|
2711
|
-
if (event
|
|
2714
|
+
if (Predicate.isTagged(event, "Enter")) {
|
|
2712
2715
|
console.log(`${indent}> ${event.name} @${event.offset} | ${preview(event.state, event.offset)}`)
|
|
2713
2716
|
} else {
|
|
2714
2717
|
const consumed = (event.endOffset ?? event.offset) - event.offset
|
package/src/ParserStream.ts
CHANGED
|
@@ -36,6 +36,7 @@ import * as Registry from "./internal/compiler/registry.js"
|
|
|
36
36
|
import type * as Resume from "./internal/resume.js"
|
|
37
37
|
import * as Run from "./internal/run.js"
|
|
38
38
|
import * as Stack from "./internal/stack.js"
|
|
39
|
+
import { isEmptyUnits } from "./internal/text.js"
|
|
39
40
|
import type { Parser } from "./Parser.js"
|
|
40
41
|
|
|
41
42
|
/**
|
|
@@ -49,7 +50,7 @@ export type Chunkable = string | Uint8Array | ReadonlyArray<unknown>
|
|
|
49
50
|
const concat = <I extends Chunkable>(a: I, b: I): I => {
|
|
50
51
|
if (Predicate.isString(a)) return (a + (b as string)) as I
|
|
51
52
|
if (a instanceof Uint8Array) {
|
|
52
|
-
if (a.
|
|
53
|
+
if (a.byteLength === 0) return b
|
|
53
54
|
const out = new Uint8Array(a.length + (b as Uint8Array).length)
|
|
54
55
|
out.set(a)
|
|
55
56
|
out.set(b as Uint8Array, a.length)
|
|
@@ -174,6 +175,7 @@ class Incremental<A, E, I extends Chunkable> {
|
|
|
174
175
|
/** The input length at the last attempt, and where that attempt ran out of input. */
|
|
175
176
|
private attempted = 0
|
|
176
177
|
private restart = 0
|
|
178
|
+
private pushed = 0
|
|
177
179
|
/** Set once an attempt settles: a success (with its end offset) or a definite failure. */
|
|
178
180
|
private settled: { readonly value: A; readonly pos: number } | "failed" | undefined
|
|
179
181
|
|
|
@@ -221,9 +223,14 @@ class Incremental<A, E, I extends Chunkable> {
|
|
|
221
223
|
}
|
|
222
224
|
|
|
223
225
|
push(chunk: I): void {
|
|
224
|
-
if (chunk
|
|
226
|
+
if (isEmptyUnits(chunk)) return
|
|
225
227
|
this.append(chunk)
|
|
226
228
|
if (this.settled !== undefined) return
|
|
229
|
+
// The first attempt waits for a second chunk: an input that arrives in
|
|
230
|
+
// one chunk (most small responses) is parsed once, by `finish`, instead
|
|
231
|
+
// of once in partial mode and again in complete mode, since a repetition
|
|
232
|
+
// that completes without resuming keeps no checkpoint
|
|
233
|
+
if (++this.pushed === 1) return
|
|
227
234
|
// An attempt re-reads what the last one could not finish, from `restart`;
|
|
228
235
|
// waiting until at least half as much new input has arrived keeps the
|
|
229
236
|
// total work linear in the input. Strings are also copied when they are
|
|
@@ -234,9 +241,12 @@ class Incremental<A, E, I extends Chunkable> {
|
|
|
234
241
|
this.attempt()
|
|
235
242
|
}
|
|
236
243
|
|
|
237
|
-
/**
|
|
238
|
-
|
|
239
|
-
|
|
244
|
+
/**
|
|
245
|
+
* The step attempts run: the parser's compiled code if it was compiled
|
|
246
|
+
* (the resumable build, unless `resumable` is false), else the interpreter.
|
|
247
|
+
*/
|
|
248
|
+
private step(state: State<I>, resumable = true): unknown {
|
|
249
|
+
return Stack.run(Registry.stepFor(this.parser, state, resumable) ?? this.parser.step, state)
|
|
240
250
|
}
|
|
241
251
|
|
|
242
252
|
private attempt(): void {
|
|
@@ -258,10 +268,12 @@ class Incremental<A, E, I extends Chunkable> {
|
|
|
258
268
|
if (settled !== undefined && settled !== "failed") {
|
|
259
269
|
if (settled.pos === this.length) return Effect.succeed(settled.value)
|
|
260
270
|
} else if (settled === undefined) {
|
|
261
|
-
// The last attempt, in complete mode, resumes from the checkpoints too
|
|
271
|
+
// The last attempt, in complete mode, resumes from the checkpoints too;
|
|
272
|
+
// with no attempt before it, it is an ordinary parse
|
|
262
273
|
const state = new State(this.input(), { context: this.context })
|
|
263
|
-
|
|
264
|
-
|
|
274
|
+
const resumable = this.attempted > 0
|
|
275
|
+
if (resumable) state.resume = this.resume
|
|
276
|
+
const a = this.step(state, resumable)
|
|
265
277
|
if (a !== Fail && state.pos === state.end) return Effect.succeed(a as A)
|
|
266
278
|
}
|
|
267
279
|
// The error comes from an ordinary parse of the whole input, so it is the
|
package/src/Syntax.ts
CHANGED
|
@@ -46,17 +46,19 @@
|
|
|
46
46
|
*
|
|
47
47
|
* @since 0.2.0
|
|
48
48
|
*/
|
|
49
|
+
import * as Str from "effect/String"
|
|
49
50
|
import * as Predicate from "effect/Predicate"
|
|
50
51
|
import type * as Context from "effect/Context"
|
|
51
52
|
import { dual } from "effect/Function"
|
|
52
53
|
import * as Option from "effect/Option"
|
|
53
54
|
import type { Input, Slice, Source } from "./Input.js"
|
|
54
|
-
import { Fail, fromSource } from "./Input.js"
|
|
55
|
+
import { Fail, fromSource, State } from "./Input.js"
|
|
55
56
|
import * as ParseError from "./ParseError.js"
|
|
56
57
|
import { nodeOf } from "./internal/node.js"
|
|
58
|
+
import { Key } from "./internal/key.js"
|
|
57
59
|
import * as Stack from "./internal/stack.js"
|
|
58
60
|
import type { ErrorInfo, Green, Previous, Segment } from "./internal/syntax.js"
|
|
59
|
-
import { absolute, errorKind,
|
|
61
|
+
import { absolute, errorKind, SyntaxBuilder } from "./internal/syntax.js"
|
|
60
62
|
import type { Parser } from "./Parser.js"
|
|
61
63
|
import { described, isParser } from "./Parser.js"
|
|
62
64
|
|
|
@@ -67,7 +69,7 @@ import { described, isParser } from "./Parser.js"
|
|
|
67
69
|
const syntaxNode = (self: Parser.Any, kind: string, named: boolean): Parser.Any => {
|
|
68
70
|
const step = self.step
|
|
69
71
|
// Identifies the nodes this parser builds, so a reparse can reuse them
|
|
70
|
-
const key =
|
|
72
|
+
const key = new Key()
|
|
71
73
|
return described<unknown, unknown, unknown, Input>(
|
|
72
74
|
(state) => {
|
|
73
75
|
const builder = state.syntax
|
|
@@ -133,7 +135,7 @@ export const token: {
|
|
|
133
135
|
|
|
134
136
|
const defaultTokenKind = (self: Parser.Any): string => {
|
|
135
137
|
const n = nodeOf(self)
|
|
136
|
-
if (n
|
|
138
|
+
if (Predicate.isTagged(n, "Literal") && Predicate.isString(n.literal) && !n.caseless) return n.literal
|
|
137
139
|
return self.name ?? "token"
|
|
138
140
|
}
|
|
139
141
|
|
|
@@ -577,7 +579,7 @@ export class SyntaxNode<I extends Input = string> {
|
|
|
577
579
|
let out = ""
|
|
578
580
|
const visit = (green: Green, field: string | undefined) => {
|
|
579
581
|
if (green.named || green === this.green) {
|
|
580
|
-
if (out
|
|
582
|
+
if (Str.isNonEmpty(out)) out += " "
|
|
581
583
|
if (field !== undefined) out += `${field}: `
|
|
582
584
|
out += `(${green.kind}`
|
|
583
585
|
for (let i = 0; i < green.children.length; i++) visit(green.children[i], green.fields?.[i])
|
|
@@ -708,7 +710,8 @@ export class TreeCursor<I extends Input = string> {
|
|
|
708
710
|
const expectedEof = ["end of input"]
|
|
709
711
|
|
|
710
712
|
const run = <A, E, I extends Input>(config: Config, input: I, previous: Previous | undefined): Tree<A, E, I> => {
|
|
711
|
-
const state = new
|
|
713
|
+
const state = new State<I>(input, { context: config.context })
|
|
714
|
+
const builder = new SyntaxBuilder(state, previous)
|
|
712
715
|
const a = Stack.run(config.inner.step, state)
|
|
713
716
|
// Text parsers record the encoding when they fail, which they may have done inside reused nodes
|
|
714
717
|
if (state.encoding === undefined && previous !== undefined) state.encoding = previous.encoding
|
|
@@ -722,8 +725,8 @@ const run = <A, E, I extends Input>(config: Config, input: I, previous: Previous
|
|
|
722
725
|
} else {
|
|
723
726
|
value = Option.some(a as A)
|
|
724
727
|
}
|
|
725
|
-
const green =
|
|
726
|
-
return new Tree<A, E, I>(input, value,
|
|
728
|
+
const green = builder.finish(config.kind, Option.getOrUndefined(value), fatal, state.pos)
|
|
729
|
+
return new Tree<A, E, I>(input, value, builder.reused, green, config, state.encoding)
|
|
727
730
|
}
|
|
728
731
|
|
|
729
732
|
/**
|
|
@@ -756,7 +759,7 @@ export const parse: {
|
|
|
756
759
|
options?: ParseOptions & { readonly context?: Context.Context<R> }
|
|
757
760
|
): Tree<A, E, I> => {
|
|
758
761
|
const n = nodeOf(grammar)
|
|
759
|
-
const isRoot = n
|
|
762
|
+
const isRoot = Predicate.isTagged(n, "Syntax") && n.kind !== undefined && n.named
|
|
760
763
|
const config: Config = {
|
|
761
764
|
kind: isRoot ? n.kind : (options?.kind ?? "document"),
|
|
762
765
|
inner: isRoot ? (n.self as Parser.Any) : grammar,
|