@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.
Files changed (157) hide show
  1. package/CHANGELOG.md +202 -0
  2. package/README.md +56 -14
  3. package/dist/Ascii.d.ts +11 -4
  4. package/dist/Ascii.d.ts.map +1 -1
  5. package/dist/Ascii.js +12 -4
  6. package/dist/Ascii.js.map +1 -1
  7. package/dist/Binary.d.ts +5 -2
  8. package/dist/Binary.d.ts.map +1 -1
  9. package/dist/Binary.js +83 -37
  10. package/dist/Binary.js.map +1 -1
  11. package/dist/Bits.d.ts.map +1 -1
  12. package/dist/Bits.js +11 -4
  13. package/dist/Bits.js.map +1 -1
  14. package/dist/Compiler.d.ts +74 -2
  15. package/dist/Compiler.d.ts.map +1 -1
  16. package/dist/Compiler.js +78 -1
  17. package/dist/Compiler.js.map +1 -1
  18. package/dist/Expression.d.ts.map +1 -1
  19. package/dist/Expression.js +86 -74
  20. package/dist/Expression.js.map +1 -1
  21. package/dist/Input.d.ts +56 -5
  22. package/dist/Input.d.ts.map +1 -1
  23. package/dist/Input.js +70 -1
  24. package/dist/Input.js.map +1 -1
  25. package/dist/Nom.d.ts +5 -15
  26. package/dist/Nom.d.ts.map +1 -1
  27. package/dist/Nom.js.map +1 -1
  28. package/dist/ParseError.d.ts +69 -11
  29. package/dist/ParseError.d.ts.map +1 -1
  30. package/dist/ParseError.js +129 -28
  31. package/dist/ParseError.js.map +1 -1
  32. package/dist/Parser.d.ts +375 -29
  33. package/dist/Parser.d.ts.map +1 -1
  34. package/dist/Parser.js +394 -37
  35. package/dist/Parser.js.map +1 -1
  36. package/dist/ParserSchema.d.ts.map +1 -1
  37. package/dist/ParserSchema.js +2 -3
  38. package/dist/ParserSchema.js.map +1 -1
  39. package/dist/ParserStream.d.ts +122 -14
  40. package/dist/ParserStream.d.ts.map +1 -1
  41. package/dist/ParserStream.js +293 -62
  42. package/dist/ParserStream.js.map +1 -1
  43. package/dist/Syntax.d.ts +23 -5
  44. package/dist/Syntax.d.ts.map +1 -1
  45. package/dist/Syntax.js +1 -1
  46. package/dist/Syntax.js.map +1 -1
  47. package/dist/Text.d.ts.map +1 -1
  48. package/dist/Text.js +21 -15
  49. package/dist/Text.js.map +1 -1
  50. package/dist/Token.d.ts +65 -11
  51. package/dist/Token.d.ts.map +1 -1
  52. package/dist/Token.js +73 -26
  53. package/dist/Token.js.map +1 -1
  54. package/dist/internal/compiler/closure.d.ts +1 -1
  55. package/dist/internal/compiler/closure.d.ts.map +1 -1
  56. package/dist/internal/compiler/closure.js +263 -113
  57. package/dist/internal/compiler/closure.js.map +1 -1
  58. package/dist/internal/compiler/entries.d.ts +3 -1
  59. package/dist/internal/compiler/entries.d.ts.map +1 -1
  60. package/dist/internal/compiler/entries.js +3 -2
  61. package/dist/internal/compiler/entries.js.map +1 -1
  62. package/dist/internal/compiler/first.d.ts.map +1 -1
  63. package/dist/internal/compiler/first.js +9 -2
  64. package/dist/internal/compiler/first.js.map +1 -1
  65. package/dist/internal/compiler/graph.d.ts +39 -0
  66. package/dist/internal/compiler/graph.d.ts.map +1 -0
  67. package/dist/internal/compiler/graph.js +78 -0
  68. package/dist/internal/compiler/graph.js.map +1 -0
  69. package/dist/internal/compiler/jit.d.ts +5 -0
  70. package/dist/internal/compiler/jit.d.ts.map +1 -1
  71. package/dist/internal/compiler/jit.js +208 -106
  72. package/dist/internal/compiler/jit.js.map +1 -1
  73. package/dist/internal/compiler/registry.d.ts +11 -3
  74. package/dist/internal/compiler/registry.d.ts.map +1 -1
  75. package/dist/internal/compiler/registry.js.map +1 -1
  76. package/dist/internal/compiler/search.d.ts +32 -0
  77. package/dist/internal/compiler/search.d.ts.map +1 -0
  78. package/dist/internal/compiler/search.js +68 -0
  79. package/dist/internal/compiler/search.js.map +1 -0
  80. package/dist/internal/compiler/sets.d.ts.map +1 -1
  81. package/dist/internal/compiler/sets.js +1 -1
  82. package/dist/internal/compiler/sets.js.map +1 -1
  83. package/dist/internal/compiler/wasm.d.ts +8 -0
  84. package/dist/internal/compiler/wasm.d.ts.map +1 -0
  85. package/dist/internal/compiler/wasm.js +2840 -0
  86. package/dist/internal/compiler/wasm.js.map +1 -0
  87. package/dist/internal/compiler/wasmAsm.d.ts +199 -0
  88. package/dist/internal/compiler/wasmAsm.d.ts.map +1 -0
  89. package/dist/internal/compiler/wasmAsm.js +427 -0
  90. package/dist/internal/compiler/wasmAsm.js.map +1 -0
  91. package/dist/internal/entries.d.ts +3 -1
  92. package/dist/internal/entries.d.ts.map +1 -1
  93. package/dist/internal/entries.js.map +1 -1
  94. package/dist/internal/node.d.ts +77 -3
  95. package/dist/internal/node.d.ts.map +1 -1
  96. package/dist/internal/node.js +54 -10
  97. package/dist/internal/node.js.map +1 -1
  98. package/dist/internal/numbers.d.ts.map +1 -1
  99. package/dist/internal/numbers.js.map +1 -1
  100. package/dist/internal/run.d.ts +14 -0
  101. package/dist/internal/run.d.ts.map +1 -1
  102. package/dist/internal/run.js +53 -11
  103. package/dist/internal/run.js.map +1 -1
  104. package/dist/internal/stack.d.ts +14 -0
  105. package/dist/internal/stack.d.ts.map +1 -1
  106. package/dist/internal/stack.js +24 -2
  107. package/dist/internal/stack.js.map +1 -1
  108. package/dist/internal/syntax.d.ts +4 -4
  109. package/dist/internal/syntax.d.ts.map +1 -1
  110. package/dist/internal/syntax.js +18 -10
  111. package/dist/internal/syntax.js.map +1 -1
  112. package/dist/internal/text.d.ts +32 -0
  113. package/dist/internal/text.d.ts.map +1 -1
  114. package/dist/internal/text.js +68 -14
  115. package/dist/internal/text.js.map +1 -1
  116. package/dist/internal/tokens.d.ts +20 -0
  117. package/dist/internal/tokens.d.ts.map +1 -1
  118. package/dist/internal/tokens.js +72 -39
  119. package/dist/internal/tokens.js.map +1 -1
  120. package/dist/internal/tuple.d.ts +6 -0
  121. package/dist/internal/tuple.d.ts.map +1 -0
  122. package/dist/internal/tuple.js +170 -0
  123. package/dist/internal/tuple.js.map +1 -0
  124. package/package.json +18 -10
  125. package/src/Ascii.ts +47 -12
  126. package/src/Binary.ts +174 -73
  127. package/src/Bits.ts +16 -6
  128. package/src/Compiler.ts +101 -10
  129. package/src/Expression.ts +94 -81
  130. package/src/Input.ts +99 -7
  131. package/src/Nom.ts +15 -5
  132. package/src/ParseError.ts +209 -52
  133. package/src/Parser.ts +684 -143
  134. package/src/ParserSchema.ts +1 -2
  135. package/src/ParserStream.ts +361 -68
  136. package/src/Syntax.ts +60 -36
  137. package/src/Text.ts +32 -27
  138. package/src/Token.ts +152 -34
  139. package/src/internal/compiler/closure.ts +371 -153
  140. package/src/internal/compiler/entries.ts +4 -2
  141. package/src/internal/compiler/first.ts +16 -0
  142. package/src/internal/compiler/graph.ts +96 -0
  143. package/src/internal/compiler/jit.ts +319 -169
  144. package/src/internal/compiler/registry.ts +10 -2
  145. package/src/internal/compiler/search.ts +68 -0
  146. package/src/internal/compiler/sets.ts +7 -3
  147. package/src/internal/compiler/wasm.ts +3276 -0
  148. package/src/internal/compiler/wasmAsm.ts +484 -0
  149. package/src/internal/entries.ts +8 -2
  150. package/src/internal/node.ts +103 -7
  151. package/src/internal/numbers.ts +3 -1
  152. package/src/internal/run.ts +73 -12
  153. package/src/internal/stack.ts +25 -1
  154. package/src/internal/syntax.ts +39 -18
  155. package/src/internal/text.ts +97 -9
  156. package/src/internal/tokens.ts +106 -14
  157. 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
+ }
@@ -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
- 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
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
 
@@ -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,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 as any)[NodeId] ?? { _tag: "Opaque", step: p.step }
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
- /** Normalizes a range, as `repeat`, `takeWhile`, ... do. */
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.isNumber(range)) return { min: range, max: range }
162
- return { min: range.min ?? 0, max: range.max ?? Infinity }
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 as any])
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