@thomasfosterau/effect-parse 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +550 -2
- package/dist/Ascii.d.ts +392 -0
- package/dist/Ascii.d.ts.map +1 -0
- package/dist/Ascii.js +610 -0
- package/dist/Ascii.js.map +1 -0
- package/dist/Binary.d.ts +153 -0
- package/dist/Binary.d.ts.map +1 -0
- package/dist/Binary.js +171 -0
- package/dist/Binary.js.map +1 -0
- package/dist/Bits.d.ts +60 -0
- package/dist/Bits.d.ts.map +1 -0
- package/dist/Bits.js +144 -0
- package/dist/Bits.js.map +1 -0
- package/dist/Encoding.d.ts +116 -0
- package/dist/Encoding.d.ts.map +1 -0
- package/dist/Encoding.js +85 -0
- package/dist/Encoding.js.map +1 -0
- package/dist/Expression.d.ts +124 -0
- package/dist/Expression.d.ts.map +1 -0
- package/dist/Expression.js +139 -0
- package/dist/Expression.js.map +1 -0
- package/dist/Input.d.ts +301 -0
- package/dist/Input.d.ts.map +1 -0
- package/dist/Input.js +312 -0
- package/dist/Input.js.map +1 -0
- package/dist/Nom.d.ts +518 -0
- package/dist/Nom.d.ts.map +1 -0
- package/dist/Nom.js +519 -0
- package/dist/Nom.js.map +1 -0
- package/dist/ParseError.d.ts +120 -0
- package/dist/ParseError.d.ts.map +1 -0
- package/dist/ParseError.js +301 -0
- package/dist/ParseError.js.map +1 -0
- package/dist/Parser.d.ts +1312 -0
- package/dist/Parser.d.ts.map +1 -0
- package/dist/Parser.js +1936 -0
- package/dist/Parser.js.map +1 -0
- package/dist/ParserSchema.d.ts +31 -0
- package/dist/ParserSchema.d.ts.map +1 -0
- package/dist/ParserSchema.js +56 -0
- package/dist/ParserSchema.js.map +1 -0
- package/dist/ParserStream.d.ts +127 -0
- package/dist/ParserStream.d.ts.map +1 -0
- package/dist/ParserStream.js +270 -0
- package/dist/ParserStream.js.map +1 -0
- package/dist/Text.d.ts +330 -0
- package/dist/Text.d.ts.map +1 -0
- package/dist/Text.js +529 -0
- package/dist/Text.js.map +1 -0
- package/dist/Token.d.ts +225 -0
- package/dist/Token.d.ts.map +1 -0
- package/dist/Token.js +294 -0
- package/dist/Token.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/compiler/closure.d.ts +25 -0
- package/dist/internal/compiler/closure.d.ts.map +1 -0
- package/dist/internal/compiler/closure.js +1668 -0
- package/dist/internal/compiler/closure.js.map +1 -0
- package/dist/internal/compiler/entries.d.ts +19 -0
- package/dist/internal/compiler/entries.d.ts.map +1 -0
- package/dist/internal/compiler/entries.js +32 -0
- package/dist/internal/compiler/entries.js.map +1 -0
- package/dist/internal/compiler/first.d.ts +33 -0
- package/dist/internal/compiler/first.d.ts.map +1 -0
- package/dist/internal/compiler/first.js +235 -0
- package/dist/internal/compiler/first.js.map +1 -0
- package/dist/internal/compiler/jit.d.ts +20 -0
- package/dist/internal/compiler/jit.d.ts.map +1 -0
- package/dist/internal/compiler/jit.js +1030 -0
- package/dist/internal/compiler/jit.js.map +1 -0
- package/dist/internal/compiler/registry.d.ts +34 -0
- package/dist/internal/compiler/registry.d.ts.map +1 -0
- package/dist/internal/compiler/registry.js +46 -0
- package/dist/internal/compiler/registry.js.map +1 -0
- package/dist/internal/compiler/sets.d.ts +29 -0
- package/dist/internal/compiler/sets.d.ts.map +1 -0
- package/dist/internal/compiler/sets.js +85 -0
- package/dist/internal/compiler/sets.js.map +1 -0
- package/dist/internal/entries.d.ts +27 -0
- package/dist/internal/entries.d.ts.map +1 -0
- package/dist/internal/entries.js +42 -0
- package/dist/internal/entries.js.map +1 -0
- package/dist/internal/node.d.ts +251 -0
- package/dist/internal/node.d.ts.map +1 -0
- package/dist/internal/node.js +73 -0
- package/dist/internal/node.js.map +1 -0
- package/dist/internal/numbers.d.ts +41 -0
- package/dist/internal/numbers.d.ts.map +1 -0
- package/dist/internal/numbers.js +302 -0
- package/dist/internal/numbers.js.map +1 -0
- package/dist/internal/resume.d.ts +39 -0
- package/dist/internal/resume.d.ts.map +1 -0
- package/dist/internal/resume.js +37 -0
- package/dist/internal/resume.js.map +1 -0
- package/dist/internal/text.d.ts +72 -0
- package/dist/internal/text.d.ts.map +1 -0
- package/dist/internal/text.js +391 -0
- package/dist/internal/text.js.map +1 -0
- package/dist/internal/tokens.d.ts +93 -0
- package/dist/internal/tokens.d.ts.map +1 -0
- package/dist/internal/tokens.js +400 -0
- package/dist/internal/tokens.js.map +1 -0
- package/package.json +115 -3
- package/src/Ascii.ts +741 -0
- package/src/Binary.ts +228 -0
- package/src/Bits.ts +192 -0
- package/src/Encoding.ts +128 -0
- package/src/Expression.ts +245 -0
- package/src/Input.ts +429 -0
- package/src/Nom.ts +642 -0
- package/src/ParseError.ts +378 -0
- package/src/Parser.ts +2763 -0
- package/src/ParserSchema.ts +87 -0
- package/src/ParserStream.ts +390 -0
- package/src/Text.ts +757 -0
- package/src/Token.ts +416 -0
- package/src/index.ts +16 -0
- package/src/internal/compiler/closure.ts +1583 -0
- package/src/internal/compiler/entries.ts +36 -0
- package/src/internal/compiler/first.ts +252 -0
- package/src/internal/compiler/jit.ts +1091 -0
- package/src/internal/compiler/registry.ts +79 -0
- package/src/internal/compiler/sets.ts +100 -0
- package/src/internal/entries.ts +51 -0
- package/src/internal/node.ts +207 -0
- package/src/internal/numbers.ts +291 -0
- package/src/internal/resume.ts +55 -0
- package/src/internal/text.ts +411 -0
- package/src/internal/tokens.ts +409 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The registry of compiled parsers, keyed by parser (see `Parser.compile`).
|
|
3
|
+
* Compiled steps are built lazily, once per input kind.
|
|
4
|
+
*/
|
|
5
|
+
import type { State } from "../../Input.js"
|
|
6
|
+
import type { ParserLike } from "../node.js"
|
|
7
|
+
import type { Fallback, Step } from "./closure.js"
|
|
8
|
+
import { compileClosure } from "./closure.js"
|
|
9
|
+
import { compileJit, jitSupported } from "./jit.js"
|
|
10
|
+
|
|
11
|
+
/** Which compiler to use. */
|
|
12
|
+
export type Backend = "auto" | "closure"
|
|
13
|
+
|
|
14
|
+
/** What was compiled for a parser and an input kind. */
|
|
15
|
+
export interface Compiled {
|
|
16
|
+
/** The compiler actually used. */
|
|
17
|
+
readonly backend: "jit" | "closure" | undefined
|
|
18
|
+
readonly step: Step | undefined
|
|
19
|
+
/** Parts of the grammar that run interpreted, and why. */
|
|
20
|
+
readonly fallbacks: ReadonlyArray<Fallback>
|
|
21
|
+
/** Why compilation failed, if it did (the parser then runs interpreted). */
|
|
22
|
+
readonly error: unknown
|
|
23
|
+
/** Why the JIT failed, if it did (the closure compiler is then used). */
|
|
24
|
+
readonly jitError?: unknown
|
|
25
|
+
/** The generated source, for the JIT. */
|
|
26
|
+
readonly source?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface Entry {
|
|
30
|
+
readonly backend: Backend
|
|
31
|
+
readonly byKind: Array<Compiled | undefined>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const registry = new WeakMap<ParserLike, Entry>()
|
|
35
|
+
|
|
36
|
+
export const register = (p: ParserLike, backend: Backend): void => {
|
|
37
|
+
const entry = registry.get(p)
|
|
38
|
+
if (entry === undefined || entry.backend !== backend) registry.set(p, { backend, byKind: [] })
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const isRegistered = (p: ParserLike): boolean => registry.has(p)
|
|
42
|
+
|
|
43
|
+
const build = (p: ParserLike, kind: 0 | 1 | 2, backend: Backend, resumable: boolean): Compiled => {
|
|
44
|
+
if (backend === "auto" && jitSupported()) {
|
|
45
|
+
try {
|
|
46
|
+
const { fallbacks, source, step } = compileJit(p, kind, resumable)
|
|
47
|
+
return { backend: "jit", step, fallbacks, error: undefined, source }
|
|
48
|
+
} catch (error) {
|
|
49
|
+
// Fall back to the closure compiler, remembering why
|
|
50
|
+
return { ...buildClosure(p, kind, resumable), jitError: error }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return buildClosure(p, kind, resumable)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const buildClosure = (p: ParserLike, kind: 0 | 1 | 2, resumable: boolean): Compiled => {
|
|
57
|
+
try {
|
|
58
|
+
const { fallbacks, step } = compileClosure(p, kind, resumable)
|
|
59
|
+
return { backend: "closure", step, fallbacks, error: undefined }
|
|
60
|
+
} catch (error) {
|
|
61
|
+
return { backend: undefined, step: undefined, fallbacks: [], error }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The compiled form of a registered parser for an input kind, compiling it
|
|
67
|
+
* if needed. `resumable` selects the build used by streaming parses, which
|
|
68
|
+
* supports partial input and checkpoints.
|
|
69
|
+
*/
|
|
70
|
+
export const compiled = (p: ParserLike, kind: 0 | 1 | 2, resumable = false): Compiled | undefined => {
|
|
71
|
+
const entry = registry.get(p)
|
|
72
|
+
if (entry === undefined) return undefined
|
|
73
|
+
const index = resumable ? kind + 3 : kind
|
|
74
|
+
return entry.byKind[index] ??= build(p, kind, entry.backend, resumable)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The compiled step for running `p` on `state`, if it is registered. */
|
|
78
|
+
export const stepFor = (p: ParserLike, state: State<any>, resumable = false): Step | undefined =>
|
|
79
|
+
compiled(p, state.inputKind, resumable)?.step
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token sets for compiled parsers: membership tables for small units, the
|
|
3
|
+
* exact test for the rest, and the code point ranges when they are known.
|
|
4
|
+
*/
|
|
5
|
+
import type { Encoding } from "../../Encoding.js"
|
|
6
|
+
import { compile, compileAny, PredicateRanges } from "../tokens.js"
|
|
7
|
+
|
|
8
|
+
/** Size of the membership table: every byte, and the Latin-1 code points. */
|
|
9
|
+
export const TableSize = 256
|
|
10
|
+
|
|
11
|
+
export interface CharSet {
|
|
12
|
+
/** Tests a code point (or byte, for `Token` on bytes). */
|
|
13
|
+
readonly test: (code: number) => boolean
|
|
14
|
+
/** `table[c] === 1` iff `test(c)`, for `c < TableSize`. */
|
|
15
|
+
readonly table: Uint8Array
|
|
16
|
+
/** Sorted inclusive ranges of accepted code points, when known. */
|
|
17
|
+
readonly ranges: ReadonlyArray<readonly [number, number]> | undefined
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const isRange = (u: unknown): u is { readonly from: string | number; readonly to: string | number } =>
|
|
21
|
+
typeof u === "object" && u !== null && (u as any)._tag === "TokenRange"
|
|
22
|
+
|
|
23
|
+
const toCode = (u: string | number): number => typeof u === "number" ? u : u.codePointAt(0)!
|
|
24
|
+
|
|
25
|
+
/** The code point ranges of a token set, or `undefined` if it has an unknown predicate. */
|
|
26
|
+
export const rangesOf = (set: unknown): Array<[number, number]> | undefined => {
|
|
27
|
+
const out: Array<[number, number]> = []
|
|
28
|
+
const add = (u: unknown): boolean => {
|
|
29
|
+
if (typeof u === "string") {
|
|
30
|
+
for (const c of u) {
|
|
31
|
+
const cp = c.codePointAt(0)!
|
|
32
|
+
out.push([cp, cp])
|
|
33
|
+
}
|
|
34
|
+
return true
|
|
35
|
+
}
|
|
36
|
+
if (typeof u === "number") {
|
|
37
|
+
out.push([u, u])
|
|
38
|
+
return true
|
|
39
|
+
}
|
|
40
|
+
if (u instanceof Uint8Array) {
|
|
41
|
+
for (const b of u) out.push([b, b])
|
|
42
|
+
return true
|
|
43
|
+
}
|
|
44
|
+
if (isRange(u)) {
|
|
45
|
+
out.push([toCode(u.from), toCode(u.to)])
|
|
46
|
+
return true
|
|
47
|
+
}
|
|
48
|
+
if (typeof u === "function") {
|
|
49
|
+
const r = (u as any)[PredicateRanges] as ReadonlyArray<readonly [number, number]> | undefined
|
|
50
|
+
if (r === undefined) return false
|
|
51
|
+
for (const [a, b] of r) out.push([a, b])
|
|
52
|
+
return true
|
|
53
|
+
}
|
|
54
|
+
if (Array.isArray(u)) return u.every(add)
|
|
55
|
+
return false
|
|
56
|
+
}
|
|
57
|
+
if (!add(set)) return undefined
|
|
58
|
+
out.sort((a, b) => a[0] - b[0])
|
|
59
|
+
const merged: Array<[number, number]> = []
|
|
60
|
+
for (const [a, b] of out) {
|
|
61
|
+
if (b < a) continue
|
|
62
|
+
const last = merged[merged.length - 1]
|
|
63
|
+
if (last !== undefined && a <= last[1] + 1) last[1] = Math.max(last[1], b)
|
|
64
|
+
else merged.push([a, b])
|
|
65
|
+
}
|
|
66
|
+
return merged
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const makeSet = (test: (code: number) => boolean, set: unknown): CharSet => {
|
|
70
|
+
// The table calls the test once per small code: token-set predicates are
|
|
71
|
+
// assumed to be pure (see the design notes).
|
|
72
|
+
const table = new Uint8Array(TableSize)
|
|
73
|
+
for (let c = 0; c < TableSize; c++) table[c] = test(c) ? 1 : 0
|
|
74
|
+
return { test, table, ranges: rangesOf(set) }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The set as a `Token` parser sees it: string predicates receive strings on
|
|
79
|
+
* string input, everything receives numbers on byte input.
|
|
80
|
+
*/
|
|
81
|
+
export const tokenSet = (set: unknown, kind: 0 | 1): CharSet => {
|
|
82
|
+
const m = compileAny(set)
|
|
83
|
+
return makeSet(kind === 0 ? m.forString.code : m.forCode.code, set)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** The set as a `Text` parser sees it: code points, for any encoding. */
|
|
87
|
+
export const textSet = (set: unknown): CharSet => makeSet(compile(set, "string").code, set)
|
|
88
|
+
|
|
89
|
+
/** Whether some accepted code point is at least `limit` (true when unknown). */
|
|
90
|
+
export const reachesAbove = (s: CharSet, limit: number): boolean =>
|
|
91
|
+
s.ranges === undefined || s.ranges.some(([, b]) => b >= limit)
|
|
92
|
+
|
|
93
|
+
/** The input kind a `Text` encoding reads: 0 for strings, 1 for bytes. */
|
|
94
|
+
export const kindOfEncoding = (enc: Encoding<any>): 0 | 1 => {
|
|
95
|
+
let k = kinds.get(enc)
|
|
96
|
+
if (k === undefined) kinds.set(enc, k = typeof enc.encode("") === "string" ? 0 : 1)
|
|
97
|
+
return k
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const kinds = new WeakMap<Encoding<any>, 0 | 1>()
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Building objects as `Object.fromEntries` does, without its array of
|
|
3
|
+
* `[key, value]` pairs: the compilers use these when a parser maps a
|
|
4
|
+
* repetition of pairs with `Object.fromEntries` (see `Parser.fromEntries`).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** The built-in `Object.fromEntries`, which compiled parsers recognize. */
|
|
8
|
+
export const fromEntries: <K extends PropertyKey, V>(entries: Iterable<readonly [K, V]>) => { [P in K]: V } =
|
|
9
|
+
Object.fromEntries as any
|
|
10
|
+
|
|
11
|
+
const objectPrototype = Object.prototype
|
|
12
|
+
const hasOwn = objectPrototype.hasOwnProperty
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Whether `o[key] = value` on a new plain object creates the same property
|
|
16
|
+
* as `Object.fromEntries` (a writable, enumerable, configurable data
|
|
17
|
+
* property): `key` is a string that `Object.prototype` has no property for,
|
|
18
|
+
* so there is no setter (`__proto__`) or read-only property to run into.
|
|
19
|
+
*/
|
|
20
|
+
export const isPlainKey = (key: unknown): key is string => typeof key === "string" && !hasOwn.call(objectPrototype, key)
|
|
21
|
+
|
|
22
|
+
const define = (o: object, key: unknown, value: unknown): void => {
|
|
23
|
+
Object.defineProperty(o, key as PropertyKey, { value, writable: true, enumerable: true, configurable: true })
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Adds an entry whose key is not plain to `o`, given the entries already
|
|
28
|
+
* deferred. Keys that are objects may run code when converted to property
|
|
29
|
+
* keys, which `Object.fromEntries` only does once every entry is parsed:
|
|
30
|
+
* from the first such key on, entries are deferred to {@link flushEntries}.
|
|
31
|
+
* Returns the deferred entries, if any.
|
|
32
|
+
*/
|
|
33
|
+
export const addEntry = (
|
|
34
|
+
o: object,
|
|
35
|
+
deferred: Array<unknown> | undefined,
|
|
36
|
+
key: unknown,
|
|
37
|
+
value: unknown
|
|
38
|
+
): Array<unknown> | undefined => {
|
|
39
|
+
if (deferred === undefined && (typeof key !== "object" || key === null) && typeof key !== "function") {
|
|
40
|
+
define(o, key, value)
|
|
41
|
+
return undefined
|
|
42
|
+
}
|
|
43
|
+
if (deferred === undefined) deferred = []
|
|
44
|
+
deferred.push(key, value)
|
|
45
|
+
return deferred
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Adds the deferred entries of {@link addEntry} to `o`, in order. */
|
|
49
|
+
export const flushEntries = (o: object, deferred: ReadonlyArray<unknown>): void => {
|
|
50
|
+
for (let i = 0; i < deferred.length; i += 2) define(o, deferred[i], deferred[i + 1])
|
|
51
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Descriptions of parsers, recorded next to their steps so that parsers can
|
|
3
|
+
* be compiled (see `docs/DESIGN.md`, "Compiled parsers").
|
|
4
|
+
*
|
|
5
|
+
* Nodes refer to their child parsers rather than to child nodes, so a
|
|
6
|
+
* child's own step is always at hand for fallback, and recursion through
|
|
7
|
+
* `Suspend` is just a reference.
|
|
8
|
+
*/
|
|
9
|
+
import type { Encoding } from "../Encoding.js"
|
|
10
|
+
import type { ErrMode, Input, State } from "../Input.js"
|
|
11
|
+
|
|
12
|
+
/** The parts of a parser the compiler needs. */
|
|
13
|
+
export interface ParserLike {
|
|
14
|
+
readonly step: (state: State<any>) => unknown
|
|
15
|
+
readonly name: string | undefined
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type P = ParserLike
|
|
19
|
+
type Step = (state: State<any>) => unknown
|
|
20
|
+
|
|
21
|
+
/** A repetition range, normalized: `max` may be `Infinity`. */
|
|
22
|
+
interface Bounds {
|
|
23
|
+
readonly min: number
|
|
24
|
+
readonly max: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* For token nodes: `undefined` for the `Token`/`Ascii` parsers (code points
|
|
29
|
+
* of strings with lenient surrogates, bytes, or array elements), or the
|
|
30
|
+
* encoding of a `Text` parser.
|
|
31
|
+
*/
|
|
32
|
+
type Enc = Encoding<any> | undefined
|
|
33
|
+
|
|
34
|
+
export type Node =
|
|
35
|
+
// Constants
|
|
36
|
+
| { readonly _tag: "Opaque"; readonly step: Step }
|
|
37
|
+
| { readonly _tag: "Succeed"; readonly value: unknown }
|
|
38
|
+
| { readonly _tag: "Sync"; readonly evaluate: () => unknown }
|
|
39
|
+
| { readonly _tag: "Fail"; readonly mode: ErrMode }
|
|
40
|
+
| { readonly _tag: "Eof" }
|
|
41
|
+
| { readonly _tag: "Location" }
|
|
42
|
+
| { readonly _tag: "Suspend"; readonly resolve: () => P }
|
|
43
|
+
// Tokens
|
|
44
|
+
| { readonly _tag: "Any"; readonly encoding: Enc }
|
|
45
|
+
| { readonly _tag: "Literal"; readonly literal: Input; readonly caseless: boolean; readonly encoding: Enc }
|
|
46
|
+
| { readonly _tag: "OneOf"; readonly set: unknown; readonly negate: boolean; readonly encoding: Enc }
|
|
47
|
+
| { readonly _tag: "Take"; readonly count: number; readonly encoding: Enc }
|
|
48
|
+
| ({ readonly _tag: "TakeWhile"; readonly set: unknown; readonly negate: boolean; readonly encoding: Enc } & Bounds)
|
|
49
|
+
| ({ readonly _tag: "TakeUntil"; readonly literal: Input; readonly encoding: Enc } & Bounds)
|
|
50
|
+
| { readonly _tag: "Rest"; readonly encoding: Enc }
|
|
51
|
+
| { readonly _tag: "RestLen" }
|
|
52
|
+
| { readonly _tag: "TillLineEnding"; readonly encoding: Enc }
|
|
53
|
+
| { readonly _tag: "Identifier"; readonly encoding: Enc }
|
|
54
|
+
| { readonly _tag: "Bom"; readonly encoding: Enc }
|
|
55
|
+
| {
|
|
56
|
+
readonly _tag: "Escaped"
|
|
57
|
+
readonly normal: P
|
|
58
|
+
/** The control character, as a code point (or a byte / unit for `Ascii`). */
|
|
59
|
+
readonly control: number
|
|
60
|
+
readonly escape: P
|
|
61
|
+
/** `takeEscaped`: return the raw slice instead of the unescaped text. */
|
|
62
|
+
readonly raw: boolean
|
|
63
|
+
readonly encoding: Enc
|
|
64
|
+
}
|
|
65
|
+
// Mapping
|
|
66
|
+
| { readonly _tag: "Map"; readonly self: P; readonly f: (a: any) => unknown }
|
|
67
|
+
| { readonly _tag: "As"; readonly self: P; readonly value: unknown }
|
|
68
|
+
| { readonly _tag: "Tap"; readonly self: P; readonly f: (a: any) => void }
|
|
69
|
+
| { readonly _tag: "TryMap"; readonly self: P; readonly f: (a: any) => unknown }
|
|
70
|
+
| { readonly _tag: "FilterMap"; readonly self: P; readonly f: (a: any) => unknown }
|
|
71
|
+
| { readonly _tag: "Filter"; readonly self: P; readonly predicate: (a: any) => boolean }
|
|
72
|
+
| { readonly _tag: "FlatMap"; readonly self: P; readonly f: (a: any) => P }
|
|
73
|
+
// Consumed input
|
|
74
|
+
| { readonly _tag: "Taken"; readonly self: P; readonly encoding: Enc }
|
|
75
|
+
| { readonly _tag: "WithTaken"; readonly self: P }
|
|
76
|
+
| { readonly _tag: "Span"; readonly self: P }
|
|
77
|
+
| { readonly _tag: "WithSpan"; readonly self: P }
|
|
78
|
+
// Errors
|
|
79
|
+
| { readonly _tag: "Mode"; readonly self: P; readonly from: ErrMode; readonly to: ErrMode }
|
|
80
|
+
| { readonly _tag: "Context"; readonly self: P; readonly label: string }
|
|
81
|
+
| { readonly _tag: "Expected"; readonly self: P; readonly description: string }
|
|
82
|
+
| { readonly _tag: "MapError"; readonly self: P }
|
|
83
|
+
| { readonly _tag: "OrFail"; readonly self: P }
|
|
84
|
+
// Control
|
|
85
|
+
| { readonly _tag: "Opt"; readonly self: P }
|
|
86
|
+
| { readonly _tag: "OrElseSucceed"; readonly self: P; readonly value: () => unknown }
|
|
87
|
+
| { readonly _tag: "Peek"; readonly self: P }
|
|
88
|
+
| { readonly _tag: "Not"; readonly self: P }
|
|
89
|
+
// Sequencing
|
|
90
|
+
| {
|
|
91
|
+
readonly _tag: "Seq"
|
|
92
|
+
readonly parsers: ReadonlyArray<P>
|
|
93
|
+
/** Which outputs are kept: all (a tuple), one index, or a pair of indices. */
|
|
94
|
+
readonly output: "tuple" | number | readonly [number, number]
|
|
95
|
+
}
|
|
96
|
+
| { readonly _tag: "Struct"; readonly keys: ReadonlyArray<string>; readonly parsers: ReadonlyArray<P> }
|
|
97
|
+
| { readonly _tag: "ZipWith"; readonly self: P; readonly that: P; readonly f: (a: any, b: any) => unknown }
|
|
98
|
+
// Choice
|
|
99
|
+
| { readonly _tag: "Alt"; readonly parsers: ReadonlyArray<P> }
|
|
100
|
+
| {
|
|
101
|
+
readonly _tag: "Dispatch"
|
|
102
|
+
readonly selector: P
|
|
103
|
+
/** A record of cases, when `dispatch` was given one. */
|
|
104
|
+
readonly cases: Readonly<Record<PropertyKey, P>> | undefined
|
|
105
|
+
readonly otherwise: P | undefined
|
|
106
|
+
readonly choose: (key: any) => P | undefined
|
|
107
|
+
}
|
|
108
|
+
// Repetition
|
|
109
|
+
| ({ readonly _tag: "Repeat"; readonly self: P } & Bounds)
|
|
110
|
+
| ({
|
|
111
|
+
readonly _tag: "Fold"
|
|
112
|
+
readonly self: P
|
|
113
|
+
readonly init: () => unknown
|
|
114
|
+
readonly f: (acc: any, a: any) => unknown
|
|
115
|
+
readonly name: string
|
|
116
|
+
} & Bounds)
|
|
117
|
+
| ({ readonly _tag: "RepeatTill"; readonly self: P; readonly end: P } & Bounds)
|
|
118
|
+
| ({ readonly _tag: "Separated"; readonly self: P; readonly separator: P } & Bounds)
|
|
119
|
+
| {
|
|
120
|
+
readonly _tag: "SeparatedFold"
|
|
121
|
+
readonly self: P
|
|
122
|
+
readonly separator: P
|
|
123
|
+
readonly op: (l: any, s: any, r: any) => unknown
|
|
124
|
+
readonly right: boolean
|
|
125
|
+
}
|
|
126
|
+
// Services, recovery, debugging
|
|
127
|
+
| { readonly _tag: "Service" }
|
|
128
|
+
| { readonly _tag: "Provide"; readonly self: P }
|
|
129
|
+
| { readonly _tag: "ResumeAfter"; readonly self: P; readonly recover: P }
|
|
130
|
+
| { readonly _tag: "RetryAfter"; readonly self: P; readonly recover: P }
|
|
131
|
+
| { readonly _tag: "Trace"; readonly self: P; readonly name: string }
|
|
132
|
+
|
|
133
|
+
export type Tag = Node["_tag"]
|
|
134
|
+
|
|
135
|
+
export type NodeOf<T extends Tag> = Extract<Node, { readonly _tag: T }>
|
|
136
|
+
|
|
137
|
+
/** Where a parser's description is stored. */
|
|
138
|
+
export const NodeId: unique symbol = Symbol.for("effect-parse/Parser/node")
|
|
139
|
+
|
|
140
|
+
/** The description of a parser: `Opaque` when it has none. */
|
|
141
|
+
export const nodeOf = (p: ParserLike): Node => (p as any)[NodeId] ?? { _tag: "Opaque", step: p.step }
|
|
142
|
+
|
|
143
|
+
/** Normalizes a range, as `repeat`, `takeWhile`, ... do. */
|
|
144
|
+
export const bounds = (range: number | { readonly min?: number | undefined; readonly max?: number | undefined } | undefined): Bounds => {
|
|
145
|
+
if (range === undefined) return { min: 0, max: Infinity }
|
|
146
|
+
if (typeof range === "number") return { min: range, max: range }
|
|
147
|
+
return { min: range.min ?? 0, max: range.max ?? Infinity }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** The child parsers of a node, in evaluation order. */
|
|
151
|
+
export const children = (node: Node): ReadonlyArray<P> => {
|
|
152
|
+
switch (node._tag) {
|
|
153
|
+
case "Escaped":
|
|
154
|
+
return [node.normal, node.escape]
|
|
155
|
+
case "Map":
|
|
156
|
+
case "As":
|
|
157
|
+
case "Tap":
|
|
158
|
+
case "TryMap":
|
|
159
|
+
case "FilterMap":
|
|
160
|
+
case "Filter":
|
|
161
|
+
case "FlatMap":
|
|
162
|
+
case "Taken":
|
|
163
|
+
case "WithTaken":
|
|
164
|
+
case "Span":
|
|
165
|
+
case "WithSpan":
|
|
166
|
+
case "Mode":
|
|
167
|
+
case "Context":
|
|
168
|
+
case "Expected":
|
|
169
|
+
case "MapError":
|
|
170
|
+
case "OrFail":
|
|
171
|
+
case "Opt":
|
|
172
|
+
case "OrElseSucceed":
|
|
173
|
+
case "Peek":
|
|
174
|
+
case "Not":
|
|
175
|
+
case "Repeat":
|
|
176
|
+
case "Fold":
|
|
177
|
+
case "Provide":
|
|
178
|
+
case "Trace":
|
|
179
|
+
return [node.self]
|
|
180
|
+
case "ResumeAfter":
|
|
181
|
+
case "RetryAfter":
|
|
182
|
+
return [node.self, node.recover]
|
|
183
|
+
case "Seq":
|
|
184
|
+
case "Struct":
|
|
185
|
+
case "Alt":
|
|
186
|
+
return node.parsers
|
|
187
|
+
case "ZipWith":
|
|
188
|
+
return [node.self, node.that]
|
|
189
|
+
case "Dispatch": {
|
|
190
|
+
const out: Array<P> = [node.selector]
|
|
191
|
+
if (node.cases !== undefined) {
|
|
192
|
+
for (const key of Reflect.ownKeys(node.cases)) out.push(node.cases[key as any]!)
|
|
193
|
+
}
|
|
194
|
+
if (node.otherwise !== undefined) out.push(node.otherwise)
|
|
195
|
+
return out
|
|
196
|
+
}
|
|
197
|
+
case "RepeatTill":
|
|
198
|
+
return [node.self, node.end]
|
|
199
|
+
case "Separated":
|
|
200
|
+
case "SeparatedFold":
|
|
201
|
+
return [node.self, node.separator]
|
|
202
|
+
case "Suspend":
|
|
203
|
+
return [node.resolve()]
|
|
204
|
+
default:
|
|
205
|
+
return []
|
|
206
|
+
}
|
|
207
|
+
}
|