@thomasfosterau/effect-parse 0.4.0 → 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.
Files changed (148) hide show
  1. package/README.md +40 -14
  2. package/dist/Ascii.d.ts +11 -4
  3. package/dist/Ascii.d.ts.map +1 -1
  4. package/dist/Ascii.js +7 -1
  5. package/dist/Ascii.js.map +1 -1
  6. package/dist/Binary.d.ts.map +1 -1
  7. package/dist/Binary.js +64 -35
  8. package/dist/Binary.js.map +1 -1
  9. package/dist/Bits.d.ts.map +1 -1
  10. package/dist/Bits.js +7 -4
  11. package/dist/Bits.js.map +1 -1
  12. package/dist/Compiler.d.ts +74 -2
  13. package/dist/Compiler.d.ts.map +1 -1
  14. package/dist/Compiler.js +78 -1
  15. package/dist/Compiler.js.map +1 -1
  16. package/dist/Expression.d.ts.map +1 -1
  17. package/dist/Expression.js +86 -74
  18. package/dist/Expression.js.map +1 -1
  19. package/dist/Input.d.ts +37 -4
  20. package/dist/Input.d.ts.map +1 -1
  21. package/dist/Input.js +42 -0
  22. package/dist/Input.js.map +1 -1
  23. package/dist/Nom.d.ts +5 -15
  24. package/dist/Nom.d.ts.map +1 -1
  25. package/dist/Nom.js.map +1 -1
  26. package/dist/ParseError.d.ts +2 -2
  27. package/dist/ParseError.d.ts.map +1 -1
  28. package/dist/ParseError.js +1 -1
  29. package/dist/ParseError.js.map +1 -1
  30. package/dist/Parser.d.ts +370 -28
  31. package/dist/Parser.d.ts.map +1 -1
  32. package/dist/Parser.js +390 -19
  33. package/dist/Parser.js.map +1 -1
  34. package/dist/ParserSchema.js +2 -2
  35. package/dist/ParserSchema.js.map +1 -1
  36. package/dist/ParserStream.d.ts +3 -2
  37. package/dist/ParserStream.d.ts.map +1 -1
  38. package/dist/ParserStream.js +15 -10
  39. package/dist/ParserStream.js.map +1 -1
  40. package/dist/Syntax.d.ts +23 -5
  41. package/dist/Syntax.d.ts.map +1 -1
  42. package/dist/Syntax.js.map +1 -1
  43. package/dist/Text.d.ts.map +1 -1
  44. package/dist/Text.js +9 -9
  45. package/dist/Text.js.map +1 -1
  46. package/dist/Token.d.ts +55 -10
  47. package/dist/Token.d.ts.map +1 -1
  48. package/dist/Token.js +59 -21
  49. package/dist/Token.js.map +1 -1
  50. package/dist/internal/compiler/closure.d.ts.map +1 -1
  51. package/dist/internal/compiler/closure.js +257 -97
  52. package/dist/internal/compiler/closure.js.map +1 -1
  53. package/dist/internal/compiler/entries.d.ts +3 -1
  54. package/dist/internal/compiler/entries.d.ts.map +1 -1
  55. package/dist/internal/compiler/entries.js +3 -2
  56. package/dist/internal/compiler/entries.js.map +1 -1
  57. package/dist/internal/compiler/first.d.ts.map +1 -1
  58. package/dist/internal/compiler/first.js +7 -1
  59. package/dist/internal/compiler/first.js.map +1 -1
  60. package/dist/internal/compiler/graph.d.ts +17 -0
  61. package/dist/internal/compiler/graph.d.ts.map +1 -0
  62. package/dist/internal/compiler/graph.js +56 -0
  63. package/dist/internal/compiler/graph.js.map +1 -0
  64. package/dist/internal/compiler/jit.d.ts +5 -0
  65. package/dist/internal/compiler/jit.d.ts.map +1 -1
  66. package/dist/internal/compiler/jit.js +185 -96
  67. package/dist/internal/compiler/jit.js.map +1 -1
  68. package/dist/internal/compiler/registry.d.ts +10 -2
  69. package/dist/internal/compiler/registry.d.ts.map +1 -1
  70. package/dist/internal/compiler/registry.js.map +1 -1
  71. package/dist/internal/compiler/search.d.ts +32 -0
  72. package/dist/internal/compiler/search.d.ts.map +1 -0
  73. package/dist/internal/compiler/search.js +68 -0
  74. package/dist/internal/compiler/search.js.map +1 -0
  75. package/dist/internal/compiler/sets.d.ts.map +1 -1
  76. package/dist/internal/compiler/sets.js +1 -1
  77. package/dist/internal/compiler/sets.js.map +1 -1
  78. package/dist/internal/compiler/wasm.d.ts +8 -0
  79. package/dist/internal/compiler/wasm.d.ts.map +1 -0
  80. package/dist/internal/compiler/wasm.js +2769 -0
  81. package/dist/internal/compiler/wasm.js.map +1 -0
  82. package/dist/internal/compiler/wasmAsm.d.ts +199 -0
  83. package/dist/internal/compiler/wasmAsm.d.ts.map +1 -0
  84. package/dist/internal/compiler/wasmAsm.js +427 -0
  85. package/dist/internal/compiler/wasmAsm.js.map +1 -0
  86. package/dist/internal/entries.d.ts +3 -1
  87. package/dist/internal/entries.d.ts.map +1 -1
  88. package/dist/internal/entries.js +1 -2
  89. package/dist/internal/entries.js.map +1 -1
  90. package/dist/internal/node.d.ts +58 -2
  91. package/dist/internal/node.d.ts.map +1 -1
  92. package/dist/internal/node.js +13 -3
  93. package/dist/internal/node.js.map +1 -1
  94. package/dist/internal/numbers.d.ts.map +1 -1
  95. package/dist/internal/numbers.js.map +1 -1
  96. package/dist/internal/run.d.ts +12 -0
  97. package/dist/internal/run.d.ts.map +1 -1
  98. package/dist/internal/run.js +51 -11
  99. package/dist/internal/run.js.map +1 -1
  100. package/dist/internal/stack.d.ts +6 -0
  101. package/dist/internal/stack.d.ts.map +1 -1
  102. package/dist/internal/stack.js +8 -2
  103. package/dist/internal/stack.js.map +1 -1
  104. package/dist/internal/syntax.d.ts +4 -4
  105. package/dist/internal/syntax.d.ts.map +1 -1
  106. package/dist/internal/syntax.js +18 -10
  107. package/dist/internal/syntax.js.map +1 -1
  108. package/dist/internal/text.d.ts +32 -0
  109. package/dist/internal/text.d.ts.map +1 -1
  110. package/dist/internal/text.js +68 -14
  111. package/dist/internal/text.js.map +1 -1
  112. package/dist/internal/tokens.d.ts +13 -0
  113. package/dist/internal/tokens.d.ts.map +1 -1
  114. package/dist/internal/tokens.js +59 -39
  115. package/dist/internal/tokens.js.map +1 -1
  116. package/package.json +5 -4
  117. package/src/Ascii.ts +42 -9
  118. package/src/Binary.ts +151 -71
  119. package/src/Bits.ts +12 -6
  120. package/src/Compiler.ts +101 -10
  121. package/src/Expression.ts +94 -81
  122. package/src/Input.ts +65 -6
  123. package/src/Nom.ts +15 -5
  124. package/src/ParseError.ts +9 -4
  125. package/src/Parser.ts +682 -129
  126. package/src/ParserSchema.ts +1 -1
  127. package/src/ParserStream.ts +40 -19
  128. package/src/Syntax.ts +60 -36
  129. package/src/Text.ts +16 -15
  130. package/src/Token.ts +138 -29
  131. package/src/internal/compiler/closure.ts +362 -135
  132. package/src/internal/compiler/entries.ts +4 -2
  133. package/src/internal/compiler/first.ts +10 -0
  134. package/src/internal/compiler/graph.ts +68 -0
  135. package/src/internal/compiler/jit.ts +284 -154
  136. package/src/internal/compiler/registry.ts +10 -2
  137. package/src/internal/compiler/search.ts +68 -0
  138. package/src/internal/compiler/sets.ts +7 -3
  139. package/src/internal/compiler/wasm.ts +3203 -0
  140. package/src/internal/compiler/wasmAsm.ts +484 -0
  141. package/src/internal/entries.ts +10 -4
  142. package/src/internal/node.ts +54 -4
  143. package/src/internal/numbers.ts +3 -1
  144. package/src/internal/run.ts +71 -12
  145. package/src/internal/stack.ts +8 -1
  146. package/src/internal/syntax.ts +39 -18
  147. package/src/internal/text.ts +97 -9
  148. 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
+ }
@@ -6,12 +6,14 @@
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
- export const fromEntries: <K extends PropertyKey, V>(entries: Iterable<readonly [K, V]>) => { [P in K]: V } =
11
- Object.fromEntries as any
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
- const hasOwn = objectPrototype.hasOwnProperty
15
17
 
16
18
  /**
17
19
  * Whether `o[key] = value` on a new plain object creates the same property
@@ -19,9 +21,13 @@ const hasOwn = objectPrototype.hasOwnProperty
19
21
  * property): `key` is a string that `Object.prototype` has no property for,
20
22
  * so there is no setter (`__proto__`) or read-only property to run into.
21
23
  */
22
- export const isPlainKey = (key: unknown): key is string => Predicate.isString(key) && !hasOwn.call(objectPrototype, key)
24
+ export const isPlainKey = (key: unknown): key is string =>
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
 
@@ -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: (a: any) => unknown }
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: (a: any, b: any) => unknown }
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 as any)[NodeId] ?? { _tag: "Opaque", step: p.step }
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 as any])
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
@@ -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
- String.fromCharCode.apply(null, b.subarray(start, end) as unknown as Array<number>)
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))