@thomasfosterau/effect-parse 0.4.1 → 0.6.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/CHANGELOG.md +202 -0
- package/README.md +56 -14
- package/dist/Ascii.d.ts +11 -4
- package/dist/Ascii.d.ts.map +1 -1
- package/dist/Ascii.js +12 -4
- package/dist/Ascii.js.map +1 -1
- package/dist/Binary.d.ts +5 -2
- package/dist/Binary.d.ts.map +1 -1
- package/dist/Binary.js +83 -37
- package/dist/Binary.js.map +1 -1
- package/dist/Bits.d.ts.map +1 -1
- package/dist/Bits.js +11 -4
- package/dist/Bits.js.map +1 -1
- package/dist/Compiler.d.ts +74 -2
- package/dist/Compiler.d.ts.map +1 -1
- package/dist/Compiler.js +78 -1
- package/dist/Compiler.js.map +1 -1
- package/dist/Expression.d.ts.map +1 -1
- package/dist/Expression.js +86 -74
- package/dist/Expression.js.map +1 -1
- package/dist/Input.d.ts +56 -5
- package/dist/Input.d.ts.map +1 -1
- package/dist/Input.js +70 -1
- package/dist/Input.js.map +1 -1
- package/dist/Nom.d.ts +5 -15
- package/dist/Nom.d.ts.map +1 -1
- package/dist/Nom.js.map +1 -1
- package/dist/ParseError.d.ts +69 -11
- package/dist/ParseError.d.ts.map +1 -1
- package/dist/ParseError.js +129 -28
- package/dist/ParseError.js.map +1 -1
- package/dist/Parser.d.ts +375 -29
- package/dist/Parser.d.ts.map +1 -1
- package/dist/Parser.js +394 -37
- package/dist/Parser.js.map +1 -1
- package/dist/ParserSchema.d.ts.map +1 -1
- package/dist/ParserSchema.js +2 -3
- package/dist/ParserSchema.js.map +1 -1
- package/dist/ParserStream.d.ts +122 -14
- package/dist/ParserStream.d.ts.map +1 -1
- package/dist/ParserStream.js +293 -62
- package/dist/ParserStream.js.map +1 -1
- package/dist/Syntax.d.ts +23 -5
- package/dist/Syntax.d.ts.map +1 -1
- package/dist/Syntax.js +1 -1
- package/dist/Syntax.js.map +1 -1
- package/dist/Text.d.ts.map +1 -1
- package/dist/Text.js +21 -15
- package/dist/Text.js.map +1 -1
- package/dist/Token.d.ts +65 -11
- package/dist/Token.d.ts.map +1 -1
- package/dist/Token.js +73 -26
- package/dist/Token.js.map +1 -1
- package/dist/internal/compiler/closure.d.ts +1 -1
- package/dist/internal/compiler/closure.d.ts.map +1 -1
- package/dist/internal/compiler/closure.js +263 -113
- package/dist/internal/compiler/closure.js.map +1 -1
- package/dist/internal/compiler/entries.d.ts +3 -1
- package/dist/internal/compiler/entries.d.ts.map +1 -1
- package/dist/internal/compiler/entries.js +3 -2
- package/dist/internal/compiler/entries.js.map +1 -1
- package/dist/internal/compiler/first.d.ts.map +1 -1
- package/dist/internal/compiler/first.js +9 -2
- package/dist/internal/compiler/first.js.map +1 -1
- package/dist/internal/compiler/graph.d.ts +39 -0
- package/dist/internal/compiler/graph.d.ts.map +1 -0
- package/dist/internal/compiler/graph.js +78 -0
- package/dist/internal/compiler/graph.js.map +1 -0
- package/dist/internal/compiler/jit.d.ts +5 -0
- package/dist/internal/compiler/jit.d.ts.map +1 -1
- package/dist/internal/compiler/jit.js +208 -106
- package/dist/internal/compiler/jit.js.map +1 -1
- package/dist/internal/compiler/registry.d.ts +11 -3
- package/dist/internal/compiler/registry.d.ts.map +1 -1
- package/dist/internal/compiler/registry.js.map +1 -1
- package/dist/internal/compiler/search.d.ts +32 -0
- package/dist/internal/compiler/search.d.ts.map +1 -0
- package/dist/internal/compiler/search.js +68 -0
- package/dist/internal/compiler/search.js.map +1 -0
- package/dist/internal/compiler/sets.d.ts.map +1 -1
- package/dist/internal/compiler/sets.js +1 -1
- package/dist/internal/compiler/sets.js.map +1 -1
- package/dist/internal/compiler/wasm.d.ts +8 -0
- package/dist/internal/compiler/wasm.d.ts.map +1 -0
- package/dist/internal/compiler/wasm.js +2840 -0
- package/dist/internal/compiler/wasm.js.map +1 -0
- package/dist/internal/compiler/wasmAsm.d.ts +199 -0
- package/dist/internal/compiler/wasmAsm.d.ts.map +1 -0
- package/dist/internal/compiler/wasmAsm.js +427 -0
- package/dist/internal/compiler/wasmAsm.js.map +1 -0
- package/dist/internal/entries.d.ts +3 -1
- package/dist/internal/entries.d.ts.map +1 -1
- package/dist/internal/entries.js.map +1 -1
- package/dist/internal/node.d.ts +77 -3
- package/dist/internal/node.d.ts.map +1 -1
- package/dist/internal/node.js +54 -10
- package/dist/internal/node.js.map +1 -1
- package/dist/internal/numbers.d.ts.map +1 -1
- package/dist/internal/numbers.js.map +1 -1
- package/dist/internal/run.d.ts +14 -0
- package/dist/internal/run.d.ts.map +1 -1
- package/dist/internal/run.js +53 -11
- package/dist/internal/run.js.map +1 -1
- package/dist/internal/stack.d.ts +14 -0
- package/dist/internal/stack.d.ts.map +1 -1
- package/dist/internal/stack.js +24 -2
- package/dist/internal/stack.js.map +1 -1
- package/dist/internal/syntax.d.ts +4 -4
- package/dist/internal/syntax.d.ts.map +1 -1
- package/dist/internal/syntax.js +18 -10
- package/dist/internal/syntax.js.map +1 -1
- package/dist/internal/text.d.ts +32 -0
- package/dist/internal/text.d.ts.map +1 -1
- package/dist/internal/text.js +68 -14
- package/dist/internal/text.js.map +1 -1
- package/dist/internal/tokens.d.ts +20 -0
- package/dist/internal/tokens.d.ts.map +1 -1
- package/dist/internal/tokens.js +72 -39
- package/dist/internal/tokens.js.map +1 -1
- package/dist/internal/tuple.d.ts +6 -0
- package/dist/internal/tuple.d.ts.map +1 -0
- package/dist/internal/tuple.js +170 -0
- package/dist/internal/tuple.js.map +1 -0
- package/package.json +18 -10
- package/src/Ascii.ts +47 -12
- package/src/Binary.ts +174 -73
- package/src/Bits.ts +16 -6
- package/src/Compiler.ts +101 -10
- package/src/Expression.ts +94 -81
- package/src/Input.ts +99 -7
- package/src/Nom.ts +15 -5
- package/src/ParseError.ts +209 -52
- package/src/Parser.ts +684 -143
- package/src/ParserSchema.ts +1 -2
- package/src/ParserStream.ts +361 -68
- package/src/Syntax.ts +60 -36
- package/src/Text.ts +32 -27
- package/src/Token.ts +152 -34
- package/src/internal/compiler/closure.ts +371 -153
- package/src/internal/compiler/entries.ts +4 -2
- package/src/internal/compiler/first.ts +16 -0
- package/src/internal/compiler/graph.ts +96 -0
- package/src/internal/compiler/jit.ts +319 -169
- package/src/internal/compiler/registry.ts +10 -2
- package/src/internal/compiler/search.ts +68 -0
- package/src/internal/compiler/sets.ts +7 -3
- package/src/internal/compiler/wasm.ts +3276 -0
- package/src/internal/compiler/wasmAsm.ts +484 -0
- package/src/internal/entries.ts +8 -2
- package/src/internal/node.ts +103 -7
- package/src/internal/numbers.ts +3 -1
- package/src/internal/run.ts +73 -12
- package/src/internal/stack.ts +25 -1
- package/src/internal/syntax.ts +39 -18
- package/src/internal/text.ts +97 -9
- package/src/internal/tokens.ts +106 -14
- package/src/internal/tuple.ts +141 -0
|
@@ -24,11 +24,13 @@ export const pairOf = (p: ParserLike): EntryPair | undefined => {
|
|
|
24
24
|
return { parsers: node.parsers, key: output[0], value: output[1] }
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
/** Whether a node only changes diagnostics: its parser parses as its child does. */
|
|
28
|
+
export const isDiagnostic = (
|
|
28
29
|
node: Node
|
|
29
|
-
): node is NodeOf<"Context" | "Expected" | "MapError" | "OrFail" | "Trace" | "Syntax" | "RetryAfter"> =>
|
|
30
|
+
): node is NodeOf<"Context" | "Expected" | "Quiet" | "MapError" | "OrFail" | "Trace" | "Syntax" | "RetryAfter"> =>
|
|
30
31
|
Predicate.isTagged(node, "Context") ||
|
|
31
32
|
Predicate.isTagged(node, "Expected") ||
|
|
33
|
+
Predicate.isTagged(node, "Quiet") ||
|
|
32
34
|
Predicate.isTagged(node, "MapError") ||
|
|
33
35
|
Predicate.isTagged(node, "OrFail") ||
|
|
34
36
|
Predicate.isTagged(node, "Trace") ||
|
|
@@ -92,6 +92,7 @@ export class FirstSets {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
private compute(node: Node): First {
|
|
95
|
+
// SAFETY: `compute` is only called from `of`, which returns before it when `kind` is 2
|
|
95
96
|
const kind = this.kind as 0 | 1
|
|
96
97
|
switch (node._tag) {
|
|
97
98
|
case "Succeed":
|
|
@@ -111,6 +112,7 @@ export class FirstSets {
|
|
|
111
112
|
if (node.encoding !== undefined) {
|
|
112
113
|
if (kindOfEncoding(node.encoding) !== kind) return unknown
|
|
113
114
|
if (node.caseless) return isEmptyUnits(lit) ? unknown : anyUnit
|
|
115
|
+
// SAFETY: a `Literal` with an encoding is a `Text` literal, which records the string it encodes
|
|
114
116
|
const encoded = node.encoding.encode(lit as string)
|
|
115
117
|
if (isEmptyUnits(encoded)) return emptyOptional
|
|
116
118
|
return ofUnits([Predicate.isString(encoded) ? encoded.charCodeAt(0) : encoded[0]])
|
|
@@ -141,13 +143,18 @@ export class FirstSets {
|
|
|
141
143
|
case "Tap":
|
|
142
144
|
case "TryMap":
|
|
143
145
|
case "FilterMap":
|
|
146
|
+
case "MapOrFail":
|
|
144
147
|
case "Filter":
|
|
145
148
|
case "Taken":
|
|
146
149
|
case "WithTaken":
|
|
147
150
|
case "Span":
|
|
148
151
|
case "WithSpan":
|
|
152
|
+
case "MapSpan":
|
|
149
153
|
case "Context":
|
|
150
154
|
case "Expected":
|
|
155
|
+
case "Quiet":
|
|
156
|
+
// A fast path accepts what its grammar accepts (`Parser.withFastPath`)
|
|
157
|
+
case "FastPath":
|
|
151
158
|
case "MapError":
|
|
152
159
|
case "OrFail":
|
|
153
160
|
case "Peek":
|
|
@@ -166,6 +173,12 @@ export class FirstSets {
|
|
|
166
173
|
const f = this.of(node.self)
|
|
167
174
|
return node.from === "Backtrack" ? (f.mode === Unknown ? f : unknown) : f
|
|
168
175
|
}
|
|
176
|
+
// `not(p)` consumes nothing. Where `p`'s first set rules a unit out, `p`
|
|
177
|
+
// backtracks there, so `not(p)` succeeds without consuming: Optional
|
|
178
|
+
// over `p`'s units. On a unit in that set it may also fail, Cut
|
|
179
|
+
// included, which Optional leaves open. So `seq(not(p), q)` starts
|
|
180
|
+
// with `p`'s units and `q`'s, and an `alt` can skip it on any other (#84)
|
|
181
|
+
case "Not":
|
|
169
182
|
case "Opt":
|
|
170
183
|
case "OrElseSucceed": {
|
|
171
184
|
const f = this.of(node.self)
|
|
@@ -173,6 +186,7 @@ export class FirstSets {
|
|
|
173
186
|
}
|
|
174
187
|
case "Seq":
|
|
175
188
|
case "Struct":
|
|
189
|
+
case "SeqWith":
|
|
176
190
|
return this.sequence(node.parsers)
|
|
177
191
|
case "ZipWith":
|
|
178
192
|
return this.sequence([node.self, node.that])
|
|
@@ -198,6 +212,8 @@ export class FirstSets {
|
|
|
198
212
|
}
|
|
199
213
|
case "Suspend":
|
|
200
214
|
return this.of(node.resolve())
|
|
215
|
+
case "Fixed":
|
|
216
|
+
return kind !== 1 ? unknown : node.size === 0 ? emptyOptional : anyUnit
|
|
201
217
|
default:
|
|
202
218
|
return unknown
|
|
203
219
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shape of a grammar, as the code-generating compilers (the JIT and the
|
|
3
|
+
* WebAssembly compiler) see it: which parsers are leaves, which are shared
|
|
4
|
+
* and which are small enough to inline everywhere.
|
|
5
|
+
*/
|
|
6
|
+
import * as Predicate from "effect/Predicate"
|
|
7
|
+
import type { Node, ParserLike } from "../node.js"
|
|
8
|
+
import { children, nodeOf } from "../node.js"
|
|
9
|
+
|
|
10
|
+
/** Whether a node is a token or a constant, always generated inline. */
|
|
11
|
+
export const isLeaf = (node: Node): boolean =>
|
|
12
|
+
Predicate.isTagged(node, "Succeed") ||
|
|
13
|
+
Predicate.isTagged(node, "Fail") ||
|
|
14
|
+
Predicate.isTagged(node, "Eof") ||
|
|
15
|
+
Predicate.isTagged(node, "Location") ||
|
|
16
|
+
Predicate.isTagged(node, "RestLen") ||
|
|
17
|
+
Predicate.isTagged(node, "Any") ||
|
|
18
|
+
Predicate.isTagged(node, "Literal") ||
|
|
19
|
+
Predicate.isTagged(node, "OneOf") ||
|
|
20
|
+
Predicate.isTagged(node, "TakeWhile")
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* How many times each parser reachable from `root` is used: one for every
|
|
24
|
+
* place a parent mentions it. A parser used twice is generated once, as a
|
|
25
|
+
* rule the two places call (unless it is small, see {@link isSmall}).
|
|
26
|
+
*
|
|
27
|
+
* Uses are counted, not distinct parents: `seq(p, p)` uses `p` twice. When
|
|
28
|
+
* only parents were counted, such a `p` was inlined at both places, and a
|
|
29
|
+
* grammar that does it at every level (`p = seq(p, p)`, twenty times over)
|
|
30
|
+
* compiled to 2^20 copies: 160 MB of source from twenty lines (#122).
|
|
31
|
+
*/
|
|
32
|
+
export const countRefs = (root: ParserLike): WeakMap<ParserLike, number> => {
|
|
33
|
+
const refs = new WeakMap<ParserLike, number>()
|
|
34
|
+
const seen = new WeakSet<ParserLike>()
|
|
35
|
+
const stack = [root]
|
|
36
|
+
for (let p = stack.pop(); p !== undefined; p = stack.pop()) {
|
|
37
|
+
if (seen.has(p)) continue
|
|
38
|
+
seen.add(p)
|
|
39
|
+
for (const c of children(nodeOf(p))) {
|
|
40
|
+
refs.set(c, (refs.get(c) ?? 0) + 1)
|
|
41
|
+
stack.push(c)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return refs
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The most parsers a code-generating compiler inlines for one root. Sharing
|
|
49
|
+
* is what keeps generated code proportional to the grammar; a grammar that
|
|
50
|
+
* is itself enormous (a tree of distinct parsers built by a loop, a derived
|
|
51
|
+
* schema) would still take minutes and gigabytes to generate, so past this
|
|
52
|
+
* many the compiler gives up and the next one down takes over (the JIT for
|
|
53
|
+
* WebAssembly, the closure compiler for the JIT), which costs a closure per
|
|
54
|
+
* parser instead of source text. The largest grammars in this repository
|
|
55
|
+
* and the libraries built on it inline a few thousand.
|
|
56
|
+
*/
|
|
57
|
+
export const MaxInlined = 100_000
|
|
58
|
+
|
|
59
|
+
/** Counts a parser a compiler is about to inline, failing the compilation past {@link MaxInlined}. */
|
|
60
|
+
export const countInlined = (inlined: number): number => {
|
|
61
|
+
if (inlined >= MaxInlined) {
|
|
62
|
+
throw new RangeError(
|
|
63
|
+
`effect-parse: the grammar is too large to generate code for (over ${MaxInlined} parsers inlined)`
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
return inlined + 1
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Whether a parser is small enough to inline wherever it is used. */
|
|
70
|
+
export const isSmall = (p: ParserLike): boolean => {
|
|
71
|
+
let budget = 6
|
|
72
|
+
const visit = (q: ParserLike): boolean => {
|
|
73
|
+
const node = nodeOf(q)
|
|
74
|
+
if (
|
|
75
|
+
--budget < 0 ||
|
|
76
|
+
Predicate.isTagged(node, "Suspend") ||
|
|
77
|
+
Predicate.isTagged(node, "Escaped") ||
|
|
78
|
+
Predicate.isTagged(node, "Dispatch")
|
|
79
|
+
)
|
|
80
|
+
return false
|
|
81
|
+
return children(node).every(visit)
|
|
82
|
+
}
|
|
83
|
+
return visit(p)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** The node a `dispatch` selects on, and whether it is looked at with `peek`. */
|
|
87
|
+
export const selectorOf = (selector: ParserLike): { readonly node: Node; readonly peek: boolean } => {
|
|
88
|
+
const node = nodeOf(selector)
|
|
89
|
+
return Predicate.isTagged(node, "Peek") ? { node: nodeOf(node.self), peek: true } : { node, peek: false }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** ASCII lower case. */
|
|
93
|
+
export const lowerAscii = (c: number): number => (c >= 65 && c <= 90 ? c + 32 : c)
|
|
94
|
+
|
|
95
|
+
/** Whether a code is an ASCII letter. */
|
|
96
|
+
export const isLetter = (c: number): boolean => (c >= 65 && c <= 90) || (c >= 97 && c <= 122)
|