@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
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small WebAssembly assembler for the WASM compiler: function bodies with
|
|
3
|
+
* structured labels, and the binary module around them. It writes only the
|
|
4
|
+
* instructions and sections that compiler uses.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** Value types. */
|
|
8
|
+
export const I32 = 0x7f
|
|
9
|
+
export const I64 = 0x7e
|
|
10
|
+
export const F64 = 0x7c
|
|
11
|
+
export const V128 = 0x7b
|
|
12
|
+
const Void = 0x40
|
|
13
|
+
|
|
14
|
+
/** Opcodes used by the compiler. */
|
|
15
|
+
export const Op = {
|
|
16
|
+
unreachable: 0x00,
|
|
17
|
+
block: 0x02,
|
|
18
|
+
loop: 0x03,
|
|
19
|
+
if: 0x04,
|
|
20
|
+
else: 0x05,
|
|
21
|
+
end: 0x0b,
|
|
22
|
+
br: 0x0c,
|
|
23
|
+
brIf: 0x0d,
|
|
24
|
+
brTable: 0x0e,
|
|
25
|
+
return: 0x0f,
|
|
26
|
+
call: 0x10,
|
|
27
|
+
drop: 0x1a,
|
|
28
|
+
select: 0x1b,
|
|
29
|
+
localGet: 0x20,
|
|
30
|
+
localSet: 0x21,
|
|
31
|
+
localTee: 0x22,
|
|
32
|
+
globalGet: 0x23,
|
|
33
|
+
globalSet: 0x24,
|
|
34
|
+
i32Load: 0x28,
|
|
35
|
+
i64Load: 0x29,
|
|
36
|
+
f64Load: 0x2b,
|
|
37
|
+
i32Load8U: 0x2d,
|
|
38
|
+
i32Load16U: 0x2f,
|
|
39
|
+
i32Store: 0x36,
|
|
40
|
+
i64Store: 0x37,
|
|
41
|
+
i32Store8: 0x3a,
|
|
42
|
+
i32Store16: 0x3b,
|
|
43
|
+
f64Store: 0x39,
|
|
44
|
+
i32Const: 0x41,
|
|
45
|
+
i64Const: 0x42,
|
|
46
|
+
f64Const: 0x44,
|
|
47
|
+
i32Eqz: 0x45,
|
|
48
|
+
i32Eq: 0x46,
|
|
49
|
+
i32Ne: 0x47,
|
|
50
|
+
i32LtS: 0x48,
|
|
51
|
+
i32LtU: 0x49,
|
|
52
|
+
i32GtS: 0x4a,
|
|
53
|
+
i32GtU: 0x4b,
|
|
54
|
+
i32LeS: 0x4c,
|
|
55
|
+
i32LeU: 0x4d,
|
|
56
|
+
i32GeS: 0x4e,
|
|
57
|
+
i32GeU: 0x4f,
|
|
58
|
+
i64Eqz: 0x50,
|
|
59
|
+
i64Ne: 0x52,
|
|
60
|
+
f64Eq: 0x61,
|
|
61
|
+
f64Ne: 0x62,
|
|
62
|
+
i32Add: 0x6a,
|
|
63
|
+
i32Sub: 0x6b,
|
|
64
|
+
i32Mul: 0x6c,
|
|
65
|
+
i32And: 0x71,
|
|
66
|
+
i32Or: 0x72,
|
|
67
|
+
i32Xor: 0x73,
|
|
68
|
+
i32Shl: 0x74,
|
|
69
|
+
i32ShrU: 0x76,
|
|
70
|
+
i32Ctz: 0x68,
|
|
71
|
+
i64And: 0x83,
|
|
72
|
+
i64Or: 0x84,
|
|
73
|
+
i64Shl: 0x86,
|
|
74
|
+
i64ShrU: 0x88,
|
|
75
|
+
f64Abs: 0x99,
|
|
76
|
+
f64Neg: 0x9a,
|
|
77
|
+
f64Add: 0xa0,
|
|
78
|
+
f64Sub: 0xa1,
|
|
79
|
+
f64Mul: 0xa2,
|
|
80
|
+
f64Div: 0xa3,
|
|
81
|
+
f64ConvertI32U: 0xb8
|
|
82
|
+
} as const
|
|
83
|
+
|
|
84
|
+
/** Unsigned LEB128. */
|
|
85
|
+
export const uleb = (n: number, out: Array<number> = []): Array<number> => {
|
|
86
|
+
let v = n >>> 0
|
|
87
|
+
do {
|
|
88
|
+
let b = v & 0x7f
|
|
89
|
+
v >>>= 7
|
|
90
|
+
if (v !== 0) b |= 0x80
|
|
91
|
+
out.push(b)
|
|
92
|
+
} while (v !== 0)
|
|
93
|
+
return out
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Signed LEB128 of a 32-bit integer. */
|
|
97
|
+
export const sleb = (n: number, out: Array<number> = []): Array<number> => {
|
|
98
|
+
let v = n | 0
|
|
99
|
+
for (;;) {
|
|
100
|
+
const b = v & 0x7f
|
|
101
|
+
v >>= 7
|
|
102
|
+
if ((v === 0 && (b & 0x40) === 0) || (v === -1 && (b & 0x40) !== 0)) {
|
|
103
|
+
out.push(b)
|
|
104
|
+
return out
|
|
105
|
+
}
|
|
106
|
+
out.push(b | 0x80)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** SIMD opcodes, after the `0xfd` prefix. */
|
|
111
|
+
export const Simd = {
|
|
112
|
+
v128Load: 0x00,
|
|
113
|
+
v128Store: 0x0b,
|
|
114
|
+
v128Const: 0x0c,
|
|
115
|
+
i8x16Swizzle: 0x0e,
|
|
116
|
+
i8x16Splat: 0x0f,
|
|
117
|
+
i8x16Eq: 0x23,
|
|
118
|
+
i8x16Ne: 0x24,
|
|
119
|
+
v128Not: 0x4d,
|
|
120
|
+
v128And: 0x4e,
|
|
121
|
+
v128Or: 0x50,
|
|
122
|
+
v128AnyTrue: 0x53,
|
|
123
|
+
i8x16AllTrue: 0x63,
|
|
124
|
+
i8x16Bitmask: 0x64,
|
|
125
|
+
i8x16ShrU: 0x6d,
|
|
126
|
+
i8x16RelaxedSwizzle: 0x100,
|
|
127
|
+
i16x8ExtendLowI8x16U: 0x89,
|
|
128
|
+
i16x8ExtendHighI8x16U: 0x8a
|
|
129
|
+
} as const
|
|
130
|
+
|
|
131
|
+
/** Signed LEB128 of a 64-bit integer. */
|
|
132
|
+
const sleb64 = (n: bigint, out: Array<number>): Array<number> => {
|
|
133
|
+
let v = BigInt.asIntN(64, n)
|
|
134
|
+
for (;;) {
|
|
135
|
+
const b = Number(v & 0x7fn)
|
|
136
|
+
v >>= 7n
|
|
137
|
+
if ((v === 0n && (b & 0x40) === 0) || (v === -1n && (b & 0x40) !== 0)) {
|
|
138
|
+
out.push(b)
|
|
139
|
+
return out
|
|
140
|
+
}
|
|
141
|
+
out.push(b | 0x80)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** A branch target: a block, a loop or an `if`. */
|
|
146
|
+
export class Label {
|
|
147
|
+
readonly _label = true
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* The body of a function being written: its code, its locals (after the
|
|
152
|
+
* parameters) and the stack of labels that branches refer to.
|
|
153
|
+
*/
|
|
154
|
+
export class Body {
|
|
155
|
+
readonly code: Array<number> = []
|
|
156
|
+
/** The types of the locals after the parameters. */
|
|
157
|
+
private readonly types: Array<number> = []
|
|
158
|
+
private readonly labels: Array<Label> = []
|
|
159
|
+
private next: number
|
|
160
|
+
/** Trace writes since the last check that dominates the code that follows (see the compiler). */
|
|
161
|
+
writes = 0
|
|
162
|
+
/** The local holding the trace pointer, in the compiler's rule functions. */
|
|
163
|
+
tp = -1
|
|
164
|
+
/** The local holding the end of the input, in the compiler's rule functions (-1: read the global). */
|
|
165
|
+
endLocal = -1
|
|
166
|
+
|
|
167
|
+
constructor(readonly params: number) {
|
|
168
|
+
this.next = params
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** A new local of type `i32`. */
|
|
172
|
+
local(): number {
|
|
173
|
+
this.types.push(I32)
|
|
174
|
+
return this.next++
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** A new local of type `i64`. */
|
|
178
|
+
localI64(): number {
|
|
179
|
+
this.types.push(I64)
|
|
180
|
+
return this.next++
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** A new local of type `v128`. */
|
|
184
|
+
localV128(): number {
|
|
185
|
+
this.types.push(V128)
|
|
186
|
+
return this.next++
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** A new local of type `f64`. */
|
|
190
|
+
localF64(): number {
|
|
191
|
+
this.types.push(F64)
|
|
192
|
+
return this.next++
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** The locals declaration, in index order (runs of one type each). */
|
|
196
|
+
locals(): Array<number> {
|
|
197
|
+
const groups: Array<[number, number]> = []
|
|
198
|
+
for (const type of this.types) {
|
|
199
|
+
const last = groups[groups.length - 1]
|
|
200
|
+
if (last !== undefined && last[1] === type) last[0]++
|
|
201
|
+
else groups.push([1, type])
|
|
202
|
+
}
|
|
203
|
+
const out = uleb(groups.length)
|
|
204
|
+
for (const [n, type] of groups) {
|
|
205
|
+
uleb(n, out)
|
|
206
|
+
out.push(type)
|
|
207
|
+
}
|
|
208
|
+
return out
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
op(...bytes: ReadonlyArray<number>): this {
|
|
212
|
+
for (const b of bytes) this.code.push(b)
|
|
213
|
+
return this
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
i32(n: number): this {
|
|
217
|
+
this.code.push(Op.i32Const)
|
|
218
|
+
sleb(n, this.code)
|
|
219
|
+
return this
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
i64(n: bigint): this {
|
|
223
|
+
this.code.push(Op.i64Const)
|
|
224
|
+
sleb64(n, this.code)
|
|
225
|
+
return this
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
f64(x: number): this {
|
|
229
|
+
this.code.push(Op.f64Const)
|
|
230
|
+
const bytes = new Uint8Array(new Float64Array([x]).buffer)
|
|
231
|
+
for (const b of bytes) this.code.push(b)
|
|
232
|
+
return this
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
get(l: number): this {
|
|
236
|
+
this.code.push(Op.localGet)
|
|
237
|
+
uleb(l, this.code)
|
|
238
|
+
return this
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
set(l: number): this {
|
|
242
|
+
this.code.push(Op.localSet)
|
|
243
|
+
uleb(l, this.code)
|
|
244
|
+
return this
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
tee(l: number): this {
|
|
248
|
+
this.code.push(Op.localTee)
|
|
249
|
+
uleb(l, this.code)
|
|
250
|
+
return this
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
gget(g: number): this {
|
|
254
|
+
this.code.push(Op.globalGet)
|
|
255
|
+
uleb(g, this.code)
|
|
256
|
+
return this
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
gset(g: number): this {
|
|
260
|
+
this.code.push(Op.globalSet)
|
|
261
|
+
uleb(g, this.code)
|
|
262
|
+
return this
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
call(f: number): this {
|
|
266
|
+
this.code.push(Op.call)
|
|
267
|
+
uleb(f, this.code)
|
|
268
|
+
return this
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** `v128.const` of sixteen bytes. */
|
|
272
|
+
v128(bytes: ArrayLike<number>): this {
|
|
273
|
+
this.code.push(0xfd)
|
|
274
|
+
uleb(Simd.v128Const, this.code)
|
|
275
|
+
for (let i = 0; i < 16; i++) this.code.push((bytes[i] ?? 0) & 0xff)
|
|
276
|
+
return this
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** A SIMD instruction, with a memory access's alignment and offset for loads and stores. */
|
|
280
|
+
simd(op: number, align = -1, offset = 0): this {
|
|
281
|
+
this.code.push(0xfd)
|
|
282
|
+
uleb(op, this.code)
|
|
283
|
+
if (align >= 0) {
|
|
284
|
+
uleb(align, this.code)
|
|
285
|
+
uleb(offset, this.code)
|
|
286
|
+
}
|
|
287
|
+
return this
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** A memory access with an alignment exponent and a constant offset. */
|
|
291
|
+
mem(op: number, align: number, offset: number): this {
|
|
292
|
+
this.code.push(op)
|
|
293
|
+
uleb(align, this.code)
|
|
294
|
+
uleb(offset, this.code)
|
|
295
|
+
return this
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** `block ... end`: a branch to its label exits the block. */
|
|
299
|
+
block(body: (exit: Label) => void): this {
|
|
300
|
+
return this.structured(Op.block, body)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** `loop ... end`: a branch to its label restarts the loop. */
|
|
304
|
+
loop(body: (again: Label) => void): this {
|
|
305
|
+
return this.structured(Op.loop, body)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** `if ... (else ...) end` on the `i32` at the top of the stack. */
|
|
309
|
+
if(then: () => void, otherwise?: () => void): this {
|
|
310
|
+
this.code.push(Op.if, Void)
|
|
311
|
+
this.labels.push(new Label())
|
|
312
|
+
then()
|
|
313
|
+
if (otherwise !== undefined) {
|
|
314
|
+
this.code.push(Op.else)
|
|
315
|
+
otherwise()
|
|
316
|
+
}
|
|
317
|
+
this.labels.pop()
|
|
318
|
+
this.code.push(Op.end)
|
|
319
|
+
return this
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** `if (result i32) ... else ... end`. */
|
|
323
|
+
ifI32(then: () => void, otherwise: () => void): this {
|
|
324
|
+
this.code.push(Op.if, I32)
|
|
325
|
+
this.labels.push(new Label())
|
|
326
|
+
then()
|
|
327
|
+
this.code.push(Op.else)
|
|
328
|
+
otherwise()
|
|
329
|
+
this.labels.pop()
|
|
330
|
+
this.code.push(Op.end)
|
|
331
|
+
return this
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
private structured(op: number, body: (label: Label) => void): this {
|
|
335
|
+
const label = new Label()
|
|
336
|
+
this.code.push(op, Void)
|
|
337
|
+
this.labels.push(label)
|
|
338
|
+
body(label)
|
|
339
|
+
this.labels.pop()
|
|
340
|
+
this.code.push(Op.end)
|
|
341
|
+
return this
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Whether a branch went to a label out of scope: a bug in the compiler,
|
|
346
|
+
* which then refuses the function (see the WebAssembly compiler).
|
|
347
|
+
*/
|
|
348
|
+
invalid = false
|
|
349
|
+
|
|
350
|
+
private depth(label: Label): number {
|
|
351
|
+
const i = this.labels.lastIndexOf(label)
|
|
352
|
+
if (i < 0) this.invalid = true
|
|
353
|
+
return this.labels.length - 1 - i
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
br(label: Label): this {
|
|
357
|
+
this.code.push(Op.br)
|
|
358
|
+
uleb(this.depth(label), this.code)
|
|
359
|
+
return this
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
brIf(label: Label): this {
|
|
363
|
+
this.code.push(Op.brIf)
|
|
364
|
+
uleb(this.depth(label), this.code)
|
|
365
|
+
return this
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/** `br_table`: `targets[i]` for the index at the top of the stack, `otherwise` out of range. */
|
|
369
|
+
brTable(targets: ReadonlyArray<Label>, otherwise: Label): this {
|
|
370
|
+
this.code.push(Op.brTable)
|
|
371
|
+
uleb(targets.length, this.code)
|
|
372
|
+
for (const t of targets) uleb(this.depth(t), this.code)
|
|
373
|
+
uleb(this.depth(otherwise), this.code)
|
|
374
|
+
return this
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/** A function type: parameter and result types. */
|
|
379
|
+
export interface FuncType {
|
|
380
|
+
readonly params: ReadonlyArray<number>
|
|
381
|
+
readonly results: ReadonlyArray<number>
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/** A global: its type, whether it is mutable, and its initial value. */
|
|
385
|
+
export interface Global {
|
|
386
|
+
readonly type: number
|
|
387
|
+
readonly init: number
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** What a module is made of. */
|
|
391
|
+
export interface ModuleSpec {
|
|
392
|
+
readonly types: ReadonlyArray<FuncType>
|
|
393
|
+
/** Imported functions: module, name, type index. */
|
|
394
|
+
readonly imports: ReadonlyArray<readonly [string, string, number]>
|
|
395
|
+
/** The imported memory (module "env", name "mem"). */
|
|
396
|
+
readonly memoryPages: number
|
|
397
|
+
/** Defined functions: type index and body. */
|
|
398
|
+
readonly functions: ReadonlyArray<readonly [number, Body]>
|
|
399
|
+
readonly globals: ReadonlyArray<Global>
|
|
400
|
+
/** Exported functions and globals: name, kind (0 function, 3 global), index. */
|
|
401
|
+
readonly exports: ReadonlyArray<readonly [string, 0 | 3, number]>
|
|
402
|
+
/** Active data segments: offset and bytes. */
|
|
403
|
+
readonly data: ReadonlyArray<readonly [number, Uint8Array]>
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const utf8Encoder = /*#__PURE__*/ new TextEncoder()
|
|
407
|
+
|
|
408
|
+
/** Appends `bytes` to `out`. */
|
|
409
|
+
const append = (out: Array<number>, bytes: ArrayLike<number>): void => {
|
|
410
|
+
for (let i = 0; i < bytes.length; i++) out.push(bytes[i])
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/** Appends a name: its length and its UTF-8 bytes. */
|
|
414
|
+
const name = (out: Array<number>, s: string): void => {
|
|
415
|
+
const bytes = utf8Encoder.encode(s)
|
|
416
|
+
uleb(bytes.length, out)
|
|
417
|
+
append(out, bytes)
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/** Appends a section: its id, its size, the number of its items and the items `write` appends. */
|
|
421
|
+
const section = <A>(
|
|
422
|
+
out: Array<number>,
|
|
423
|
+
id: number,
|
|
424
|
+
items: ReadonlyArray<A>,
|
|
425
|
+
write: (body: Array<number>, item: A) => void
|
|
426
|
+
) => {
|
|
427
|
+
const body = uleb(items.length)
|
|
428
|
+
for (const item of items) write(body, item)
|
|
429
|
+
out.push(id)
|
|
430
|
+
uleb(body.length, out)
|
|
431
|
+
append(out, body)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const f64Bytes = (x: number): Uint8Array => new Uint8Array(new Float64Array([x]).buffer)
|
|
435
|
+
|
|
436
|
+
/** The binary module. */
|
|
437
|
+
export const encodeModule = (spec: ModuleSpec): Uint8Array<ArrayBuffer> => {
|
|
438
|
+
const out = [0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]
|
|
439
|
+
section(out, 1, spec.types, (body, t) => {
|
|
440
|
+
body.push(0x60)
|
|
441
|
+
uleb(t.params.length, body)
|
|
442
|
+
append(body, t.params)
|
|
443
|
+
uleb(t.results.length, body)
|
|
444
|
+
append(body, t.results)
|
|
445
|
+
})
|
|
446
|
+
const memory: readonly [string, string, number] = ["env", "mem", -1]
|
|
447
|
+
section(out, 2, [...spec.imports, memory], (body, [m, n, t]) => {
|
|
448
|
+
name(body, m)
|
|
449
|
+
name(body, n)
|
|
450
|
+
if (t < 0) body.push(0x02, 0x00, ...uleb(spec.memoryPages))
|
|
451
|
+
else body.push(0x00, ...uleb(t))
|
|
452
|
+
})
|
|
453
|
+
section(out, 3, spec.functions, (body, [t]) => uleb(t, body))
|
|
454
|
+
section(out, 6, spec.globals, (body, g) => {
|
|
455
|
+
if (g.type === F64) {
|
|
456
|
+
body.push(F64, 0x01, Op.f64Const)
|
|
457
|
+
append(body, f64Bytes(g.init))
|
|
458
|
+
} else {
|
|
459
|
+
body.push(I32, 0x01, Op.i32Const)
|
|
460
|
+
sleb(g.init, body)
|
|
461
|
+
}
|
|
462
|
+
body.push(Op.end)
|
|
463
|
+
})
|
|
464
|
+
section(out, 7, spec.exports, (body, [n, kind, i]) => {
|
|
465
|
+
name(body, n)
|
|
466
|
+
body.push(kind)
|
|
467
|
+
uleb(i, body)
|
|
468
|
+
})
|
|
469
|
+
section(out, 10, spec.functions, (body, [, fn]) => {
|
|
470
|
+
const locals = fn.locals()
|
|
471
|
+
uleb(locals.length + fn.code.length + 1, body)
|
|
472
|
+
append(body, locals)
|
|
473
|
+
append(body, fn.code)
|
|
474
|
+
body.push(Op.end)
|
|
475
|
+
})
|
|
476
|
+
section(out, 11, spec.data, (body, [offset, bytes]) => {
|
|
477
|
+
body.push(0x00, Op.i32Const)
|
|
478
|
+
sleb(offset, body)
|
|
479
|
+
body.push(Op.end)
|
|
480
|
+
uleb(bytes.length, body)
|
|
481
|
+
append(body, bytes)
|
|
482
|
+
})
|
|
483
|
+
return new Uint8Array(out)
|
|
484
|
+
}
|
package/src/internal/entries.ts
CHANGED
|
@@ -6,9 +6,12 @@
|
|
|
6
6
|
|
|
7
7
|
import * as Predicate from "effect/Predicate"
|
|
8
8
|
|
|
9
|
+
type FromEntries = <K extends PropertyKey, V>(entries: Iterable<readonly [K, V]>) => { [P in K]: V }
|
|
10
|
+
|
|
9
11
|
/** The built-in `Object.fromEntries`, which compiled parsers recognize. */
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
// SAFETY: `Object.fromEntries` gives each `[key, value]` entry an own
|
|
13
|
+
// property `key` holding `value`, so entries keyed by `K` build `{ [P in K]: V }`
|
|
14
|
+
export const fromEntries: FromEntries = Object.fromEntries as FromEntries
|
|
12
15
|
|
|
13
16
|
const objectPrototype = Object.prototype
|
|
14
17
|
|
|
@@ -22,6 +25,9 @@ export const isPlainKey = (key: unknown): key is string =>
|
|
|
22
25
|
Predicate.isString(key) && !Object.hasOwn(objectPrototype, key)
|
|
23
26
|
|
|
24
27
|
const define = (o: object, key: unknown, value: unknown): void => {
|
|
28
|
+
// SAFETY: `Object.defineProperty` converts any key to a property key
|
|
29
|
+
// (ToPropertyKey), as `Object.fromEntries` does; its `PropertyKey`
|
|
30
|
+
// parameter type is narrower than what it accepts
|
|
25
31
|
Object.defineProperty(o, key as PropertyKey, { value, writable: true, enumerable: true, configurable: true })
|
|
26
32
|
}
|
|
27
33
|
|
package/src/internal/node.ts
CHANGED
|
@@ -34,6 +34,13 @@ interface Bounds {
|
|
|
34
34
|
*/
|
|
35
35
|
type Enc = Encoding<any> | undefined
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* A user callback building a value from parser outputs: `map`, `mapOrFail`
|
|
39
|
+
* (one output), `mapSpan` (an output and its span), `zipWith` and `seqWith`
|
|
40
|
+
* (one argument per parser).
|
|
41
|
+
*/
|
|
42
|
+
type Mapper = (...values: Array<any>) => unknown
|
|
43
|
+
|
|
37
44
|
export type Node =
|
|
38
45
|
// Constants
|
|
39
46
|
| { readonly _tag: "Opaque"; readonly step: Step }
|
|
@@ -50,7 +57,14 @@ export type Node =
|
|
|
50
57
|
| { readonly _tag: "Take"; readonly count: number; readonly encoding: Enc }
|
|
51
58
|
| ({ readonly _tag: "TakeWhile"; readonly set: unknown; readonly negate: boolean; readonly encoding: Enc } & Bounds)
|
|
52
59
|
| ({ readonly _tag: "TakeUntil"; readonly literal: Input; readonly encoding: Enc } & Bounds)
|
|
60
|
+
| ({ readonly _tag: "TakeUntilAny"; readonly literals: ReadonlyArray<Input>; readonly encoding: Enc } & Bounds)
|
|
53
61
|
| { readonly _tag: "Rest"; readonly encoding: Enc }
|
|
62
|
+
// `Binary`'s fixed-size numbers: `read` reads `size` bytes at an offset of the state's bytes
|
|
63
|
+
| {
|
|
64
|
+
readonly _tag: "Fixed"
|
|
65
|
+
readonly size: number
|
|
66
|
+
readonly read: (bytes: Uint8Array, offset: number, state: State<Uint8Array>) => unknown
|
|
67
|
+
}
|
|
54
68
|
| { readonly _tag: "RestLen" }
|
|
55
69
|
| { readonly _tag: "TillLineEnding"; readonly encoding: Enc }
|
|
56
70
|
| { readonly _tag: "Identifier"; readonly encoding: Enc }
|
|
@@ -66,11 +80,13 @@ export type Node =
|
|
|
66
80
|
readonly encoding: Enc
|
|
67
81
|
}
|
|
68
82
|
// Mapping
|
|
69
|
-
| { readonly _tag: "Map"; readonly self: P; readonly f:
|
|
83
|
+
| { readonly _tag: "Map"; readonly self: P; readonly f: Mapper }
|
|
70
84
|
| { readonly _tag: "As"; readonly self: P; readonly value: unknown }
|
|
71
85
|
| { readonly _tag: "Tap"; readonly self: P; readonly f: (a: any) => void }
|
|
72
86
|
| { readonly _tag: "TryMap"; readonly self: P; readonly f: (a: any) => Result.Result<unknown, unknown> }
|
|
73
87
|
| { readonly _tag: "FilterMap"; readonly self: P; readonly f: (a: any) => Option.Option<unknown> }
|
|
88
|
+
/** `mapOrFail`: `f` returns `Fail` to reject the value. */
|
|
89
|
+
| { readonly _tag: "MapOrFail"; readonly self: P; readonly f: Mapper }
|
|
74
90
|
| { readonly _tag: "Filter"; readonly self: P; readonly predicate: (a: any) => boolean }
|
|
75
91
|
| { readonly _tag: "FlatMap"; readonly self: P; readonly f: (a: any) => P }
|
|
76
92
|
// Consumed input
|
|
@@ -78,10 +94,15 @@ export type Node =
|
|
|
78
94
|
| { readonly _tag: "WithTaken"; readonly self: P }
|
|
79
95
|
| { readonly _tag: "Span"; readonly self: P }
|
|
80
96
|
| { readonly _tag: "WithSpan"; readonly self: P }
|
|
97
|
+
/** `mapSpan`: `f(output, start, end)`. */
|
|
98
|
+
| { readonly _tag: "MapSpan"; readonly self: P; readonly f: Mapper }
|
|
99
|
+
/** `withFastPath`: `fast` runs first in compiled code; `Defer` runs `self`. */
|
|
100
|
+
| { readonly _tag: "FastPath"; readonly self: P; readonly fast: Step }
|
|
81
101
|
// Errors
|
|
82
102
|
| { readonly _tag: "Mode"; readonly self: P; readonly from: ErrMode; readonly to: ErrMode }
|
|
83
103
|
| { readonly _tag: "Context"; readonly self: P; readonly label: string }
|
|
84
104
|
| { readonly _tag: "Expected"; readonly self: P; readonly description: string }
|
|
105
|
+
| { readonly _tag: "Quiet"; readonly self: P }
|
|
85
106
|
| { readonly _tag: "MapError"; readonly self: P }
|
|
86
107
|
| { readonly _tag: "OrFail"; readonly self: P }
|
|
87
108
|
// Control
|
|
@@ -97,7 +118,9 @@ export type Node =
|
|
|
97
118
|
readonly output: "tuple" | number | readonly [number, number]
|
|
98
119
|
}
|
|
99
120
|
| { readonly _tag: "Struct"; readonly keys: ReadonlyArray<string>; readonly parsers: ReadonlyArray<P> }
|
|
100
|
-
| { readonly _tag: "ZipWith"; readonly self: P; readonly that: P; readonly f:
|
|
121
|
+
| { readonly _tag: "ZipWith"; readonly self: P; readonly that: P; readonly f: Mapper }
|
|
122
|
+
/** `seqWith`: `f` receives one output per parser. */
|
|
123
|
+
| { readonly _tag: "SeqWith"; readonly parsers: ReadonlyArray<P>; readonly f: Mapper }
|
|
101
124
|
// Choice
|
|
102
125
|
| { readonly _tag: "Alt"; readonly parsers: ReadonlyArray<P> }
|
|
103
126
|
| {
|
|
@@ -128,6 +151,17 @@ export type Node =
|
|
|
128
151
|
readonly op: (l: any, s: any, r: any) => unknown
|
|
129
152
|
readonly right: boolean
|
|
130
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* A combinator whose algorithm the compilers don't know (`Expression.make`,
|
|
156
|
+
* `permutation`): `build` makes its step from the steps of `parsers`. The
|
|
157
|
+
* interpreter passes their own steps; the closure compiler (and the JIT,
|
|
158
|
+
* through it) passes their compiled steps, so the parsers inside compile.
|
|
159
|
+
*/
|
|
160
|
+
| {
|
|
161
|
+
readonly _tag: "Combinator"
|
|
162
|
+
readonly parsers: ReadonlyArray<P>
|
|
163
|
+
readonly build: (steps: ReadonlyArray<Step>) => Step
|
|
164
|
+
}
|
|
131
165
|
// Services, recovery, debugging
|
|
132
166
|
| { readonly _tag: "Service" }
|
|
133
167
|
| { readonly _tag: "Provide"; readonly self: P }
|
|
@@ -147,19 +181,75 @@ export type Tag = Node["_tag"]
|
|
|
147
181
|
|
|
148
182
|
export type NodeOf<T extends Tag> = Extract<Node, { readonly _tag: T }>
|
|
149
183
|
|
|
184
|
+
/** What a fast path returns to leave the input to its grammar (`Parser.Defer`). */
|
|
185
|
+
export const Defer: unique symbol = /*#__PURE__*/ Symbol.for("effect-parse/Parser/Defer")
|
|
186
|
+
|
|
150
187
|
/** Where a parser's description is stored. */
|
|
151
188
|
export const NodeId: unique symbol = /*#__PURE__*/ Symbol.for("effect-parse/Parser/node")
|
|
152
189
|
|
|
190
|
+
/** The description recorded for a parser, if it has one. */
|
|
191
|
+
export const describedNode = (p: ParserLike): Node | undefined =>
|
|
192
|
+
// SAFETY: `Parser.described` stores a parser's `Node` under `NodeId`;
|
|
193
|
+
// reading it from any other parser (or one built by `Parser.make`, which
|
|
194
|
+
// stores `undefined`) yields `undefined`
|
|
195
|
+
(p as ParserLike & { readonly [NodeId]?: Node })[NodeId]
|
|
196
|
+
|
|
153
197
|
/** The description of a parser: `Opaque` when it has none. */
|
|
154
|
-
export const nodeOf = (p: ParserLike): Node => (p
|
|
198
|
+
export const nodeOf = (p: ParserLike): Node => describedNode(p) ?? { _tag: "Opaque", step: p.step }
|
|
199
|
+
|
|
200
|
+
/** How a rejected argument reads in an error: never the argument's own text. */
|
|
201
|
+
const describeArgument = (u: unknown): string =>
|
|
202
|
+
Predicate.isNumber(u) || Predicate.isBigInt(u) || u === undefined || u === null
|
|
203
|
+
? String(u)
|
|
204
|
+
: Predicate.isString(u)
|
|
205
|
+
? "a string"
|
|
206
|
+
: "a value that is not a number"
|
|
155
207
|
|
|
156
|
-
/**
|
|
208
|
+
/**
|
|
209
|
+
* `count` as given, which must be a non-negative safe integer, else a
|
|
210
|
+
* `RangeError`. A count is a `number` only in the types, and each backend
|
|
211
|
+
* read a negative, fractional, `NaN` or non-number count its own way: the
|
|
212
|
+
* interpreter moved the cursor backwards, and the JIT wrote the value into
|
|
213
|
+
* the source it compiles.
|
|
214
|
+
*/
|
|
215
|
+
export const checkCount = (count: number, what: string = "count"): number => {
|
|
216
|
+
if (!Number.isSafeInteger(count) || count < 0)
|
|
217
|
+
throw new RangeError(`invalid ${what}: expected a non-negative integer, got ${describeArgument(count)}`)
|
|
218
|
+
return count
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Normalizes a range, as `repeat`, `takeWhile`, ... do. Throws a
|
|
223
|
+
* `RangeError` unless both bounds are non-negative integers (`max` may be
|
|
224
|
+
* `Infinity`) with `min <= max` (see {@link checkCount}).
|
|
225
|
+
*/
|
|
157
226
|
export const bounds = (
|
|
158
227
|
range: number | { readonly min?: number | undefined; readonly max?: number | undefined } | undefined
|
|
159
228
|
): Bounds => {
|
|
160
229
|
if (range === undefined) return { min: 0, max: Infinity }
|
|
161
|
-
if (Predicate.
|
|
162
|
-
|
|
230
|
+
if (!Predicate.isObject(range)) {
|
|
231
|
+
const count = checkCount(range)
|
|
232
|
+
return { min: count, max: count }
|
|
233
|
+
}
|
|
234
|
+
const min = checkCount(range.min ?? 0, "range: min")
|
|
235
|
+
const max = range.max ?? Infinity
|
|
236
|
+
if (max !== Infinity) checkCount(max, "range: max")
|
|
237
|
+
if (min > max) throw new RangeError(`invalid range: min (${min}) > max (${max})`)
|
|
238
|
+
return { min, max }
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* The code of a control character given as a one-character string or a
|
|
243
|
+
* code: a non-negative integer, else a `RangeError` (it is compared with
|
|
244
|
+
* input units, and the JIT writes it into the source it compiles).
|
|
245
|
+
*/
|
|
246
|
+
export const controlCode = (control: string | number, read: (s: string) => number): number => {
|
|
247
|
+
const code = Predicate.isString(control) ? read(control) : control
|
|
248
|
+
if (!Number.isSafeInteger(code) || code < 0)
|
|
249
|
+
throw new RangeError(
|
|
250
|
+
`invalid control character: expected a character or its code, got ${describeArgument(control)}`
|
|
251
|
+
)
|
|
252
|
+
return code
|
|
163
253
|
}
|
|
164
254
|
|
|
165
255
|
/**
|
|
@@ -187,15 +277,19 @@ export const children = (node: Node): ReadonlyArray<P> => {
|
|
|
187
277
|
case "Tap":
|
|
188
278
|
case "TryMap":
|
|
189
279
|
case "FilterMap":
|
|
280
|
+
case "MapOrFail":
|
|
190
281
|
case "Filter":
|
|
191
282
|
case "FlatMap":
|
|
192
283
|
case "Taken":
|
|
193
284
|
case "WithTaken":
|
|
194
285
|
case "Span":
|
|
195
286
|
case "WithSpan":
|
|
287
|
+
case "MapSpan":
|
|
196
288
|
case "Mode":
|
|
197
289
|
case "Context":
|
|
198
290
|
case "Expected":
|
|
291
|
+
case "Quiet":
|
|
292
|
+
case "FastPath":
|
|
199
293
|
case "MapError":
|
|
200
294
|
case "OrFail":
|
|
201
295
|
case "Opt":
|
|
@@ -213,14 +307,16 @@ export const children = (node: Node): ReadonlyArray<P> => {
|
|
|
213
307
|
return [node.self, node.recover]
|
|
214
308
|
case "Seq":
|
|
215
309
|
case "Struct":
|
|
310
|
+
case "SeqWith":
|
|
216
311
|
case "Alt":
|
|
312
|
+
case "Combinator":
|
|
217
313
|
return node.parsers
|
|
218
314
|
case "ZipWith":
|
|
219
315
|
return [node.self, node.that]
|
|
220
316
|
case "Dispatch": {
|
|
221
317
|
const out: Array<P> = [node.selector]
|
|
222
318
|
if (node.cases !== undefined) {
|
|
223
|
-
for (const key of Reflect.ownKeys(node.cases)) out.push(node.cases[key
|
|
319
|
+
for (const key of Reflect.ownKeys(node.cases)) out.push(node.cases[key])
|
|
224
320
|
}
|
|
225
321
|
if (node.otherwise !== undefined) out.push(node.otherwise)
|
|
226
322
|
return out
|