@thomasfosterau/effect-parse 0.3.0 → 0.4.1
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/README.md +3 -1
- 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 +2 -4
- 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 +6 -4
- 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 +3 -4
- package/dist/ParserSchema.js.map +1 -1
- package/dist/ParserStream.d.ts +0 -2
- package/dist/ParserStream.d.ts.map +1 -1
- package/dist/ParserStream.js +17 -10
- 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 +2 -4
- package/dist/internal/entries.js.map +1 -1
- 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 +7 -7
- 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 +13 -9
- package/src/Parser.ts +30 -25
- package/src/ParserSchema.ts +1 -1
- package/src/ParserStream.ts +23 -9
- 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/entries.ts +2 -2
- 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"
|
|
@@ -82,6 +83,8 @@ export interface Parser<out A, out E = never, out R = never, in I extends Input
|
|
|
82
83
|
/** A human-readable name, used by `toString` and tracing. */
|
|
83
84
|
readonly name: string | undefined
|
|
84
85
|
[Symbol.iterator](): ParserIterator<Parser<A, E, R, I>>
|
|
86
|
+
/** Describes the parser: `Parser(<name>)`, or `Parser` when it has no name. */
|
|
87
|
+
toString(): string
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
/**
|
|
@@ -273,6 +276,8 @@ const isTwoParserArgs = (args: IArguments) => isParser(args[0]) && isParser(args
|
|
|
273
276
|
* The step function must either return the parsed value, having advanced
|
|
274
277
|
* `state.pos` past the consumed input, or return the result of one of
|
|
275
278
|
* `state.backtrack`, `state.cut`, `state.incomplete`, ... to signal failure.
|
|
279
|
+
* A step that moves the cursor backwards (to retry, or after a lookahead)
|
|
280
|
+
* calls `state.rewind(offset)` rather than assigning `state.pos`.
|
|
276
281
|
*
|
|
277
282
|
* @example
|
|
278
283
|
* ```ts
|
|
@@ -723,7 +728,7 @@ export const tryMap: {
|
|
|
723
728
|
if (a === Fail) return Fail
|
|
724
729
|
const r = f(a)
|
|
725
730
|
if (Result.isSuccess(r)) return r.success
|
|
726
|
-
state.
|
|
731
|
+
state.rewind(start)
|
|
727
732
|
return state.failCause("Backtrack", "Verify", r.failure)
|
|
728
733
|
},
|
|
729
734
|
self.name,
|
|
@@ -740,8 +745,8 @@ export const tryMap: {
|
|
|
740
745
|
* @category mapping
|
|
741
746
|
*/
|
|
742
747
|
export const tryMapUnknown: {
|
|
743
|
-
<A, B>(f: (a: A) => B): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<B,
|
|
744
|
-
<A, E, R, I extends Input, B>(self: Parser<A, E, R, I>, f: (a: A) => B): Parser<B,
|
|
748
|
+
<A, B>(f: (a: A) => B): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<B, unknown, R, I>
|
|
749
|
+
<A, E, R, I extends Input, B>(self: Parser<A, E, R, I>, f: (a: A) => B): Parser<B, unknown, R, I>
|
|
745
750
|
} = /*#__PURE__*/ dual(
|
|
746
751
|
2,
|
|
747
752
|
<A, E, R, I extends Input, B>(self: Parser<A, E, R, I>, f: (a: A) => B): Parser<B, unknown, R, I> =>
|
|
@@ -777,7 +782,7 @@ export const filterMap: {
|
|
|
777
782
|
if (a === Fail) return Fail
|
|
778
783
|
const o = f(a)
|
|
779
784
|
if (Option.isSome(o)) return o.value
|
|
780
|
-
state.
|
|
785
|
+
state.rewind(start)
|
|
781
786
|
return state.backtrack("Verify")
|
|
782
787
|
},
|
|
783
788
|
self.name,
|
|
@@ -808,7 +813,7 @@ export const filter: {
|
|
|
808
813
|
const a = step(state)
|
|
809
814
|
if (a === Fail) return Fail
|
|
810
815
|
if (predicate(a)) return a
|
|
811
|
-
state.
|
|
816
|
+
state.rewind(start)
|
|
812
817
|
return state.backtrack("Verify")
|
|
813
818
|
},
|
|
814
819
|
self.name,
|
|
@@ -852,7 +857,7 @@ export const filterOrFail: {
|
|
|
852
857
|
const a = step(state)
|
|
853
858
|
if (a === Fail) return Fail
|
|
854
859
|
if (predicate(a)) return a
|
|
855
|
-
state.
|
|
860
|
+
state.rewind(start)
|
|
856
861
|
return state.failCause("Backtrack", "Verify", orFailWith(a))
|
|
857
862
|
},
|
|
858
863
|
self.name,
|
|
@@ -1032,7 +1037,7 @@ export const complete = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Pa
|
|
|
1032
1037
|
const start = state.pos
|
|
1033
1038
|
const a = step(state)
|
|
1034
1039
|
if (a === Fail && state.mode === "Incomplete") {
|
|
1035
|
-
state.
|
|
1040
|
+
state.rewind(start)
|
|
1036
1041
|
return state.backtrack("Complete")
|
|
1037
1042
|
}
|
|
1038
1043
|
return a
|
|
@@ -1223,7 +1228,7 @@ export const orElseSucceed: {
|
|
|
1223
1228
|
|
|
1224
1229
|
/** Rewinds after a recoverable failure, remembering it as a hint. */
|
|
1225
1230
|
const recover = (state: State<any>, start: number): void => {
|
|
1226
|
-
state.
|
|
1231
|
+
state.rewind(start)
|
|
1227
1232
|
const f = state.failure
|
|
1228
1233
|
if (f.offset === start) {
|
|
1229
1234
|
state.hintOffset = start
|
|
@@ -1297,7 +1302,7 @@ export const peek = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser
|
|
|
1297
1302
|
if (a !== Fail) {
|
|
1298
1303
|
// Failures inside a lookahead that succeeded are about input that was
|
|
1299
1304
|
// never consumed, so they are not reported as the furthest failure
|
|
1300
|
-
state.
|
|
1305
|
+
state.rewind(start)
|
|
1301
1306
|
state.hintOffset = hintOffset
|
|
1302
1307
|
state.hintExpected = hintExpected
|
|
1303
1308
|
state.deepest = deepest
|
|
@@ -1327,12 +1332,12 @@ export const not = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<
|
|
|
1327
1332
|
const a = step(state)
|
|
1328
1333
|
if (a !== Fail) {
|
|
1329
1334
|
// The failures inside `self` are not this parser's failure
|
|
1330
|
-
state.
|
|
1335
|
+
state.rewind(start)
|
|
1331
1336
|
state.deepest = deepest
|
|
1332
1337
|
return state.backtrack("Not")
|
|
1333
1338
|
}
|
|
1334
1339
|
if (state.mode !== "Backtrack") return Fail
|
|
1335
|
-
state.
|
|
1340
|
+
state.rewind(start)
|
|
1336
1341
|
state.hintOffset = hintOffset
|
|
1337
1342
|
state.hintExpected = hintExpected
|
|
1338
1343
|
state.deepest = deepest
|
|
@@ -1807,7 +1812,7 @@ export const alt = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1807
1812
|
return a
|
|
1808
1813
|
}
|
|
1809
1814
|
if (state.mode !== "Backtrack") return Fail
|
|
1810
|
-
state.
|
|
1815
|
+
state.rewind(start)
|
|
1811
1816
|
// The branch's failure, whose own ties (from a nested `alt`) can be taken over unmerged
|
|
1812
1817
|
const head = state.failureHead
|
|
1813
1818
|
if (failure === undefined || head.offset > failure.offset) {
|
|
@@ -1906,7 +1911,7 @@ export const dispatch: {
|
|
|
1906
1911
|
if (key === Fail) return Fail
|
|
1907
1912
|
const parser = choose(key)
|
|
1908
1913
|
if (parser === undefined) {
|
|
1909
|
-
state.
|
|
1914
|
+
state.rewind(start)
|
|
1910
1915
|
return state.backtrack("Dispatch")
|
|
1911
1916
|
}
|
|
1912
1917
|
return parser.step(state)
|
|
@@ -1961,7 +1966,7 @@ export const permutation = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1961
1966
|
break
|
|
1962
1967
|
}
|
|
1963
1968
|
if (state.mode !== "Backtrack") return Fail
|
|
1964
|
-
state.
|
|
1969
|
+
state.rewind(start)
|
|
1965
1970
|
ties.add(state)
|
|
1966
1971
|
}
|
|
1967
1972
|
// Some parser is not done yet, and each one that fails is folded in
|
|
@@ -1995,7 +2000,7 @@ interface FoldCheckpoint<B> {
|
|
|
1995
2000
|
readonly pos: number
|
|
1996
2001
|
}
|
|
1997
2002
|
|
|
1998
|
-
const saveFold = <B>(state: State<any>, key:
|
|
2003
|
+
const saveFold = <B>(state: State<any>, key: Key, entry: number, acc: B, pos: number): void =>
|
|
1999
2004
|
Resume.save(state, key, entry, { acc, count: 0, pos } satisfies FoldCheckpoint<B>)
|
|
2000
2005
|
|
|
2001
2006
|
const foldImpl = <A, B, E, R, I extends Input>(
|
|
@@ -2010,7 +2015,7 @@ const foldImpl = <A, B, E, R, I extends Input>(
|
|
|
2010
2015
|
const [min, max] = normalizeRange(range)
|
|
2011
2016
|
const step = self.step
|
|
2012
2017
|
const checkLoop = max === Infinity
|
|
2013
|
-
const key =
|
|
2018
|
+
const key = new Key()
|
|
2014
2019
|
return described(
|
|
2015
2020
|
(state) => {
|
|
2016
2021
|
const entry = state.pos
|
|
@@ -2189,7 +2194,7 @@ export const repeatTill: {
|
|
|
2189
2194
|
if (b !== Fail) return [acc, b]
|
|
2190
2195
|
if (state.mode !== "Backtrack") return Fail
|
|
2191
2196
|
endFailure = state.failure
|
|
2192
|
-
state.
|
|
2197
|
+
state.rewind(start)
|
|
2193
2198
|
if (acc.length >= max) return Fail
|
|
2194
2199
|
}
|
|
2195
2200
|
const a = step(state)
|
|
@@ -2252,7 +2257,7 @@ export const separated: {
|
|
|
2252
2257
|
const step = self.step
|
|
2253
2258
|
const sepStep = separator.step
|
|
2254
2259
|
const checkLoop = max === Infinity
|
|
2255
|
-
const key =
|
|
2260
|
+
const key = new Key()
|
|
2256
2261
|
return described(
|
|
2257
2262
|
(state) => {
|
|
2258
2263
|
const entry = state.pos
|
|
@@ -2295,7 +2300,7 @@ export const separated: {
|
|
|
2295
2300
|
Resume.save(state, key, entry, { acc, pos: start } satisfies SeparatedCheckpoint<A>)
|
|
2296
2301
|
}
|
|
2297
2302
|
if (state.mode !== "Backtrack" || acc.length < min) return Fail
|
|
2298
|
-
state.
|
|
2303
|
+
state.rewind(start)
|
|
2299
2304
|
return Resume.done(state, key, entry, resumed !== undefined, acc)
|
|
2300
2305
|
}
|
|
2301
2306
|
acc.push(a)
|
|
@@ -2336,7 +2341,7 @@ export const separatedFoldl1 = <A, E, R, I extends Input, S, E2, R2, I2 extends
|
|
|
2336
2341
|
): Parser<A, E | E2, R | R2, I & I2> => {
|
|
2337
2342
|
const step = self.step
|
|
2338
2343
|
const sepStep = separator.step
|
|
2339
|
-
const key =
|
|
2344
|
+
const key = new Key()
|
|
2340
2345
|
return described(
|
|
2341
2346
|
(state) => {
|
|
2342
2347
|
const entry = state.pos
|
|
@@ -2367,7 +2372,7 @@ export const separatedFoldl1 = <A, E, R, I extends Input, S, E2, R2, I2 extends
|
|
|
2367
2372
|
if (a === Fail) {
|
|
2368
2373
|
if (state.mode === "Incomplete" && state.resume !== undefined) saveFold(state, key, entry, acc, start)
|
|
2369
2374
|
if (state.mode !== "Backtrack") return Fail
|
|
2370
|
-
state.
|
|
2375
|
+
state.rewind(start)
|
|
2371
2376
|
return Resume.done(state, key, entry, resumed !== undefined, acc as A)
|
|
2372
2377
|
}
|
|
2373
2378
|
acc = op(acc, s, a)
|
|
@@ -2412,7 +2417,7 @@ export const separatedFoldr1 = <A, E, R, I extends Input, S, E2, R2, I2 extends
|
|
|
2412
2417
|
const a = step(state)
|
|
2413
2418
|
if (a === Fail) {
|
|
2414
2419
|
if (state.mode !== "Backtrack") return Fail
|
|
2415
|
-
state.
|
|
2420
|
+
state.rewind(start)
|
|
2416
2421
|
break
|
|
2417
2422
|
}
|
|
2418
2423
|
seps.push(s)
|
|
@@ -2584,7 +2589,7 @@ export const resumeAfter: {
|
|
|
2584
2589
|
if (state.recovered === undefined || state.mode === "Incomplete") return Fail
|
|
2585
2590
|
const failure = state.failure
|
|
2586
2591
|
const mode = state.mode
|
|
2587
|
-
state.
|
|
2592
|
+
state.rewind(start)
|
|
2588
2593
|
if (recoverStep(state) === Fail) return state.failWith(mode, failure)
|
|
2589
2594
|
state.recovered.push(failure)
|
|
2590
2595
|
state.syntax?.skipped(start, failure, mode)
|
|
@@ -2631,7 +2636,7 @@ export const retryAfter: {
|
|
|
2631
2636
|
if (state.recovered === undefined || state.mode === "Incomplete") return Fail
|
|
2632
2637
|
const failure = state.failure
|
|
2633
2638
|
const mode = state.mode
|
|
2634
|
-
state.
|
|
2639
|
+
state.rewind(start)
|
|
2635
2640
|
if (recoverStep(state) === Fail || state.pos === start) return state.failWith(mode, failure)
|
|
2636
2641
|
state.recovered.push(failure)
|
|
2637
2642
|
state.syntax?.skipped(start, failure, mode)
|
|
@@ -2708,7 +2713,7 @@ const preview = (state: State<any>, offset: number): string => {
|
|
|
2708
2713
|
*/
|
|
2709
2714
|
export const consoleTracer = (event: TraceEvent): void => {
|
|
2710
2715
|
const indent = " ".repeat(event.depth)
|
|
2711
|
-
if (event
|
|
2716
|
+
if (Predicate.isTagged(event, "Enter")) {
|
|
2712
2717
|
console.log(`${indent}> ${event.name} @${event.offset} | ${preview(event.state, event.offset)}`)
|
|
2713
2718
|
} else {
|
|
2714
2719
|
const consumed = (event.endOffset ?? event.offset) - event.offset
|
package/src/ParserSchema.ts
CHANGED
|
@@ -431,7 +431,7 @@ const isAnnotated = (ast: SchemaAST.AST): boolean => lookup(ast, annotationKey)
|
|
|
431
431
|
|
|
432
432
|
const withStop = <T>(ctx: Context<T>, text: string | undefined): Context<T> => {
|
|
433
433
|
if (text === undefined || text === "" || ctx.stop.includes(text[0])) return ctx
|
|
434
|
-
return { ...ctx, stop: Arr.sort([...ctx.stop, text[0]], Order.String).join("") }
|
|
434
|
+
return { ...ctx, stop: Arr.sort([...ctx.stop.split(""), text[0]], Order.String).join("") }
|
|
435
435
|
}
|
|
436
436
|
|
|
437
437
|
const at = <T>(ctx: Context<T>, key: PropertyKey): Context<T> => ({
|
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
|
/**
|
|
@@ -46,10 +47,12 @@ import type { Parser } from "./Parser.js"
|
|
|
46
47
|
*/
|
|
47
48
|
export type Chunkable = string | Uint8Array | ReadonlyArray<unknown>
|
|
48
49
|
|
|
49
|
-
const concat = <I extends Chunkable>(
|
|
50
|
+
const concat = <I extends Chunkable>(first: I, b: I): I => {
|
|
51
|
+
// Widened so the checks below narrow it (a type parameter does not narrow).
|
|
52
|
+
const a: Chunkable = first
|
|
50
53
|
if (Predicate.isString(a)) return (a + (b as string)) as I
|
|
51
54
|
if (a instanceof Uint8Array) {
|
|
52
|
-
if (a.
|
|
55
|
+
if (a.byteLength === 0) return b
|
|
53
56
|
const out = new Uint8Array(a.length + (b as Uint8Array).length)
|
|
54
57
|
out.set(a)
|
|
55
58
|
out.set(b as Uint8Array, a.length)
|
|
@@ -174,6 +177,7 @@ class Incremental<A, E, I extends Chunkable> {
|
|
|
174
177
|
/** The input length at the last attempt, and where that attempt ran out of input. */
|
|
175
178
|
private attempted = 0
|
|
176
179
|
private restart = 0
|
|
180
|
+
private pushed = 0
|
|
177
181
|
/** Set once an attempt settles: a success (with its end offset) or a definite failure. */
|
|
178
182
|
private settled: { readonly value: A; readonly pos: number } | "failed" | undefined
|
|
179
183
|
|
|
@@ -221,9 +225,14 @@ class Incremental<A, E, I extends Chunkable> {
|
|
|
221
225
|
}
|
|
222
226
|
|
|
223
227
|
push(chunk: I): void {
|
|
224
|
-
if (chunk
|
|
228
|
+
if (isEmptyUnits(chunk)) return
|
|
225
229
|
this.append(chunk)
|
|
226
230
|
if (this.settled !== undefined) return
|
|
231
|
+
// The first attempt waits for a second chunk: an input that arrives in
|
|
232
|
+
// one chunk (most small responses) is parsed once, by `finish`, instead
|
|
233
|
+
// of once in partial mode and again in complete mode, since a repetition
|
|
234
|
+
// that completes without resuming keeps no checkpoint
|
|
235
|
+
if (++this.pushed === 1) return
|
|
227
236
|
// An attempt re-reads what the last one could not finish, from `restart`;
|
|
228
237
|
// waiting until at least half as much new input has arrived keeps the
|
|
229
238
|
// total work linear in the input. Strings are also copied when they are
|
|
@@ -234,9 +243,12 @@ class Incremental<A, E, I extends Chunkable> {
|
|
|
234
243
|
this.attempt()
|
|
235
244
|
}
|
|
236
245
|
|
|
237
|
-
/**
|
|
238
|
-
|
|
239
|
-
|
|
246
|
+
/**
|
|
247
|
+
* The step attempts run: the parser's compiled code if it was compiled
|
|
248
|
+
* (the resumable build, unless `resumable` is false), else the interpreter.
|
|
249
|
+
*/
|
|
250
|
+
private step(state: State<I>, resumable = true): unknown {
|
|
251
|
+
return Stack.run(Registry.stepFor(this.parser, state, resumable) ?? this.parser.step, state)
|
|
240
252
|
}
|
|
241
253
|
|
|
242
254
|
private attempt(): void {
|
|
@@ -258,10 +270,12 @@ class Incremental<A, E, I extends Chunkable> {
|
|
|
258
270
|
if (settled !== undefined && settled !== "failed") {
|
|
259
271
|
if (settled.pos === this.length) return Effect.succeed(settled.value)
|
|
260
272
|
} else if (settled === undefined) {
|
|
261
|
-
// The last attempt, in complete mode, resumes from the checkpoints too
|
|
273
|
+
// The last attempt, in complete mode, resumes from the checkpoints too;
|
|
274
|
+
// with no attempt before it, it is an ordinary parse
|
|
262
275
|
const state = new State(this.input(), { context: this.context })
|
|
263
|
-
|
|
264
|
-
|
|
276
|
+
const resumable = this.attempted > 0
|
|
277
|
+
if (resumable) state.resume = this.resume
|
|
278
|
+
const a = this.step(state, resumable)
|
|
265
279
|
if (a !== Fail && state.pos === state.end) return Effect.succeed(a as A)
|
|
266
280
|
}
|
|
267
281
|
// 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,
|