@thomasfosterau/effect-parse 0.4.1 → 0.5.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/README.md +37 -13
- package/dist/Ascii.d.ts +11 -4
- package/dist/Ascii.d.ts.map +1 -1
- package/dist/Ascii.js +7 -1
- package/dist/Ascii.js.map +1 -1
- package/dist/Binary.d.ts.map +1 -1
- package/dist/Binary.js +64 -35
- package/dist/Binary.js.map +1 -1
- package/dist/Bits.d.ts.map +1 -1
- package/dist/Bits.js +7 -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 +37 -4
- package/dist/Input.d.ts.map +1 -1
- package/dist/Input.js +42 -0
- 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.map +1 -1
- package/dist/ParseError.js.map +1 -1
- package/dist/Parser.d.ts +366 -26
- package/dist/Parser.d.ts.map +1 -1
- package/dist/Parser.js +390 -19
- package/dist/Parser.js.map +1 -1
- package/dist/ParserSchema.js +1 -1
- package/dist/ParserStream.d.ts +3 -2
- package/dist/ParserStream.d.ts.map +1 -1
- package/dist/ParserStream.js +13 -9
- 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.map +1 -1
- package/dist/Text.d.ts.map +1 -1
- package/dist/Text.js +9 -9
- package/dist/Text.js.map +1 -1
- package/dist/Token.d.ts +55 -10
- package/dist/Token.d.ts.map +1 -1
- package/dist/Token.js +59 -21
- package/dist/Token.js.map +1 -1
- package/dist/internal/compiler/closure.d.ts.map +1 -1
- package/dist/internal/compiler/closure.js +257 -97
- 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 +7 -1
- package/dist/internal/compiler/first.js.map +1 -1
- package/dist/internal/compiler/graph.d.ts +17 -0
- package/dist/internal/compiler/graph.d.ts.map +1 -0
- package/dist/internal/compiler/graph.js +56 -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 +185 -96
- package/dist/internal/compiler/jit.js.map +1 -1
- package/dist/internal/compiler/registry.d.ts +10 -2
- 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 +2769 -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 +58 -2
- package/dist/internal/node.d.ts.map +1 -1
- package/dist/internal/node.js +13 -3
- 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 +12 -0
- package/dist/internal/run.d.ts.map +1 -1
- package/dist/internal/run.js +51 -11
- package/dist/internal/run.js.map +1 -1
- package/dist/internal/stack.d.ts +6 -0
- package/dist/internal/stack.d.ts.map +1 -1
- package/dist/internal/stack.js +8 -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 +13 -0
- package/dist/internal/tokens.d.ts.map +1 -1
- package/dist/internal/tokens.js +59 -39
- package/dist/internal/tokens.js.map +1 -1
- package/package.json +5 -4
- package/src/Ascii.ts +42 -9
- package/src/Binary.ts +151 -71
- package/src/Bits.ts +12 -6
- package/src/Compiler.ts +101 -10
- package/src/Expression.ts +94 -81
- package/src/Input.ts +65 -6
- package/src/Nom.ts +15 -5
- package/src/ParseError.ts +3 -0
- package/src/Parser.ts +678 -127
- package/src/ParserStream.ts +37 -18
- package/src/Syntax.ts +60 -36
- package/src/Text.ts +16 -15
- package/src/Token.ts +138 -29
- package/src/internal/compiler/closure.ts +362 -135
- package/src/internal/compiler/entries.ts +4 -2
- package/src/internal/compiler/first.ts +10 -0
- package/src/internal/compiler/graph.ts +68 -0
- package/src/internal/compiler/jit.ts +284 -154
- 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 +3203 -0
- package/src/internal/compiler/wasmAsm.ts +484 -0
- package/src/internal/entries.ts +8 -2
- package/src/internal/node.ts +54 -4
- package/src/internal/numbers.ts +3 -1
- package/src/internal/run.ts +71 -12
- package/src/internal/stack.ts +8 -1
- package/src/internal/syntax.ts +39 -18
- package/src/internal/text.ts +97 -9
- package/src/internal/tokens.ts +96 -14
|
@@ -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,11 +181,21 @@ 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 }
|
|
155
199
|
|
|
156
200
|
/** Normalizes a range, as `repeat`, `takeWhile`, ... do. */
|
|
157
201
|
export const bounds = (
|
|
@@ -187,15 +231,19 @@ export const children = (node: Node): ReadonlyArray<P> => {
|
|
|
187
231
|
case "Tap":
|
|
188
232
|
case "TryMap":
|
|
189
233
|
case "FilterMap":
|
|
234
|
+
case "MapOrFail":
|
|
190
235
|
case "Filter":
|
|
191
236
|
case "FlatMap":
|
|
192
237
|
case "Taken":
|
|
193
238
|
case "WithTaken":
|
|
194
239
|
case "Span":
|
|
195
240
|
case "WithSpan":
|
|
241
|
+
case "MapSpan":
|
|
196
242
|
case "Mode":
|
|
197
243
|
case "Context":
|
|
198
244
|
case "Expected":
|
|
245
|
+
case "Quiet":
|
|
246
|
+
case "FastPath":
|
|
199
247
|
case "MapError":
|
|
200
248
|
case "OrFail":
|
|
201
249
|
case "Opt":
|
|
@@ -213,14 +261,16 @@ export const children = (node: Node): ReadonlyArray<P> => {
|
|
|
213
261
|
return [node.self, node.recover]
|
|
214
262
|
case "Seq":
|
|
215
263
|
case "Struct":
|
|
264
|
+
case "SeqWith":
|
|
216
265
|
case "Alt":
|
|
266
|
+
case "Combinator":
|
|
217
267
|
return node.parsers
|
|
218
268
|
case "ZipWith":
|
|
219
269
|
return [node.self, node.that]
|
|
220
270
|
case "Dispatch": {
|
|
221
271
|
const out: Array<P> = [node.selector]
|
|
222
272
|
if (node.cases !== undefined) {
|
|
223
|
-
for (const key of Reflect.ownKeys(node.cases)) out.push(node.cases[key
|
|
273
|
+
for (const key of Reflect.ownKeys(node.cases)) out.push(node.cases[key])
|
|
224
274
|
}
|
|
225
275
|
if (node.otherwise !== undefined) out.push(node.otherwise)
|
|
226
276
|
return out
|
package/src/internal/numbers.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import * as Predicate from "effect/Predicate"
|
|
6
6
|
import type { Input } from "../Input.js"
|
|
7
7
|
import { Fail } from "../Input.js"
|
|
8
|
+
import type { FromCharCodes } from "./text.js"
|
|
8
9
|
import { latin1 } from "./text.js"
|
|
9
10
|
|
|
10
11
|
/** The text of an input slice: as is for a string, Latin-1 for bytes, joined for tokens. */
|
|
@@ -290,4 +291,5 @@ export const scanFloatBytes = (b: Uint8Array, i: number, end: number): number |
|
|
|
290
291
|
|
|
291
292
|
/** The text of ASCII bytes. */
|
|
292
293
|
const asciiText = (b: Uint8Array, start: number, end: number): string =>
|
|
293
|
-
|
|
294
|
+
// SAFETY: `apply` takes any array-like argument list, so each byte of the view is passed as a char code
|
|
295
|
+
(String.fromCharCode as FromCharCodes).apply(null, b.subarray(start, end))
|