moonscratch 0.1.0 → 0.1.2

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 (150) hide show
  1. package/dist/chunk-DQk6qfdC.mjs +18 -0
  2. package/dist/index.d.mts +1173 -0
  3. package/dist/index.mjs +27135 -0
  4. package/package.json +6 -1
  5. package/.agents/skills/moonbit-agent-guide/LICENSE +0 -202
  6. package/.agents/skills/moonbit-agent-guide/SKILL.mbt.md +0 -1126
  7. package/.agents/skills/moonbit-agent-guide/SKILL.md +0 -1126
  8. package/.agents/skills/moonbit-agent-guide/ide.md +0 -116
  9. package/.agents/skills/moonbit-agent-guide/references/advanced-moonbit-build.md +0 -106
  10. package/.agents/skills/moonbit-agent-guide/references/moonbit-language-fundamentals.mbt.md +0 -422
  11. package/.agents/skills/moonbit-agent-guide/references/moonbit-language-fundamentals.md +0 -422
  12. package/.agents/skills/moonbit-practice/SKILL.md +0 -258
  13. package/.agents/skills/moonbit-practice/assets/ci.yaml +0 -25
  14. package/.agents/skills/moonbit-practice/reference/agents.md +0 -1469
  15. package/.agents/skills/moonbit-practice/reference/configuration.md +0 -228
  16. package/.agents/skills/moonbit-practice/reference/ffi.md +0 -229
  17. package/.agents/skills/moonbit-practice/reference/ide.md +0 -189
  18. package/.agents/skills/moonbit-practice/reference/performance.md +0 -217
  19. package/.agents/skills/moonbit-practice/reference/refactor.md +0 -154
  20. package/.agents/skills/moonbit-practice/reference/stdlib.md +0 -351
  21. package/.agents/skills/moonbit-practice/reference/testing.md +0 -228
  22. package/.agents/skills/moonbit-refactoring/LICENSE +0 -21
  23. package/.agents/skills/moonbit-refactoring/SKILL.md +0 -323
  24. package/.githooks/README.md +0 -23
  25. package/.githooks/pre-commit +0 -3
  26. package/.github/workflows/copilot-setup-steps.yml +0 -40
  27. package/.turbo/turbo-typecheck.log +0 -2
  28. package/AGENTS.md +0 -91
  29. package/PLAN.md +0 -64
  30. package/TODO.md +0 -120
  31. package/benchmarks/calc.bench.ts +0 -144
  32. package/benchmarks/draw.bench.ts +0 -215
  33. package/benchmarks/load.bench.ts +0 -28
  34. package/benchmarks/render.bench.ts +0 -53
  35. package/benchmarks/run.bench.ts +0 -8
  36. package/benchmarks/types.d.ts +0 -15
  37. package/docs/scratch-vm-specs/eventloop.md +0 -103
  38. package/docs/scratch-vm-specs/moonscratch-time-separation.md +0 -50
  39. package/index.html +0 -91
  40. package/js/AGENTS.md +0 -5
  41. package/js/a.ts +0 -52
  42. package/js/assets/AGENTS.md +0 -5
  43. package/js/assets/base64.test.ts +0 -14
  44. package/js/assets/base64.ts +0 -21
  45. package/js/assets/build-asset.test.ts +0 -26
  46. package/js/assets/build-asset.ts +0 -28
  47. package/js/assets/create.test.ts +0 -142
  48. package/js/assets/create.ts +0 -122
  49. package/js/assets/index.test.ts +0 -15
  50. package/js/assets/index.ts +0 -2
  51. package/js/assets/types.ts +0 -26
  52. package/js/assets/validation.test.ts +0 -34
  53. package/js/assets/validation.ts +0 -25
  54. package/js/assets.test.ts +0 -14
  55. package/js/assets.ts +0 -1
  56. package/js/index.test.ts +0 -26
  57. package/js/index.ts +0 -3
  58. package/js/render/index.test.ts +0 -65
  59. package/js/render/index.ts +0 -13
  60. package/js/render/sharp.ts +0 -87
  61. package/js/render/svg.ts +0 -68
  62. package/js/render/types.ts +0 -35
  63. package/js/render/utils.ts +0 -108
  64. package/js/render/webgl.ts +0 -274
  65. package/js/sharp-optional.d.ts +0 -16
  66. package/js/test/helpers.ts +0 -116
  67. package/js/test/hikkaku-sample.test.ts +0 -37
  68. package/js/test/rubik-components.input-motion.test.ts +0 -60
  69. package/js/test/rubik-components.lists.test.ts +0 -49
  70. package/js/test/rubik-components.operators.test.ts +0 -104
  71. package/js/test/rubik-components.pen.test.ts +0 -112
  72. package/js/test/rubik-components.procedures-loops.test.ts +0 -72
  73. package/js/test/rubik-components.variables-branches.test.ts +0 -57
  74. package/js/test/rubik-components.visibility-entry.test.ts +0 -31
  75. package/js/test/test-projects.ts +0 -598
  76. package/js/test/variable.ts +0 -200
  77. package/js/test/warp.test.ts +0 -59
  78. package/js/vm/AGENTS.md +0 -6
  79. package/js/vm/README.md +0 -183
  80. package/js/vm/bindings.test.ts +0 -13
  81. package/js/vm/bindings.ts +0 -5
  82. package/js/vm/compare-operators.test.ts +0 -145
  83. package/js/vm/constants.test.ts +0 -11
  84. package/js/vm/constants.ts +0 -4
  85. package/js/vm/effect-guards.test.ts +0 -68
  86. package/js/vm/effect-guards.ts +0 -44
  87. package/js/vm/factory.test.ts +0 -486
  88. package/js/vm/factory.ts +0 -615
  89. package/js/vm/headless-vm.test.ts +0 -131
  90. package/js/vm/headless-vm.ts +0 -342
  91. package/js/vm/index.test.ts +0 -28
  92. package/js/vm/index.ts +0 -5
  93. package/js/vm/internal-types.ts +0 -32
  94. package/js/vm/json.test.ts +0 -40
  95. package/js/vm/json.ts +0 -273
  96. package/js/vm/normalize.test.ts +0 -48
  97. package/js/vm/normalize.ts +0 -65
  98. package/js/vm/options.test.ts +0 -30
  99. package/js/vm/options.ts +0 -55
  100. package/js/vm/pen-transparency.test.ts +0 -115
  101. package/js/vm/program-wasm.ts +0 -322
  102. package/js/vm/scheduler-render.test.ts +0 -401
  103. package/js/vm/scratch-assets.test.ts +0 -136
  104. package/js/vm/scratch-assets.ts +0 -202
  105. package/js/vm/types.ts +0 -358
  106. package/js/vm/value-guards.test.ts +0 -25
  107. package/js/vm/value-guards.ts +0 -18
  108. package/moon.mod.json +0 -10
  109. package/scripts/preinstall.ts +0 -4
  110. package/src/AGENTS.md +0 -6
  111. package/src/api.mbt +0 -161
  112. package/src/api_aot_commands.mbt +0 -184
  113. package/src/api_effects_json.mbt +0 -72
  114. package/src/api_options.mbt +0 -60
  115. package/src/api_program_wasm.mbt +0 -1647
  116. package/src/api_program_wat.mbt +0 -2206
  117. package/src/api_snapshot_json.mbt +0 -44
  118. package/src/cmd/AGENTS.md +0 -5
  119. package/src/cmd/main/AGENTS.md +0 -5
  120. package/src/cmd/main/main.mbt +0 -29
  121. package/src/cmd/main/moon.pkg +0 -7
  122. package/src/cmd/main/pkg.generated.mbti +0 -13
  123. package/src/json_helpers.mbt +0 -176
  124. package/src/moon.pkg +0 -65
  125. package/src/moonscratch.mbt +0 -3
  126. package/src/moonscratch_wbtest.mbt +0 -40
  127. package/src/parser_sb3.mbt +0 -890
  128. package/src/pkg.generated.mbti +0 -479
  129. package/src/runtime_eval.mbt +0 -2844
  130. package/src/runtime_exec.mbt +0 -3850
  131. package/src/runtime_render.mbt +0 -2550
  132. package/src/runtime_state.mbt +0 -870
  133. package/src/test/AGENTS.md +0 -3
  134. package/src/test/projects/AGENTS.md +0 -6
  135. package/src/test/projects/moon.pkg +0 -4
  136. package/src/test/projects/moonscratch_compat_test.mbt +0 -642
  137. package/src/test/projects/moonscratch_core_test.mbt +0 -1332
  138. package/src/test/projects/moonscratch_runtime_test.mbt +0 -1087
  139. package/src/test/projects/pkg.generated.mbti +0 -13
  140. package/src/test/projects/test_support.mbt +0 -35
  141. package/src/types_effects.mbt +0 -20
  142. package/src/types_error.mbt +0 -4
  143. package/src/types_options.mbt +0 -31
  144. package/src/types_runtime_structs.mbt +0 -254
  145. package/src/types_vm.mbt +0 -109
  146. package/tsconfig.json +0 -29
  147. package/viewer/index.ts +0 -399
  148. package/viewer/vite.d.ts +0 -1
  149. package/viewer/worker.ts +0 -161
  150. package/vite.config.ts +0 -11
@@ -1,1647 +0,0 @@
1
- ///|
2
- const IMPORT_GET_VAR_NUM_INDEX : Int = 0
3
-
4
- ///|
5
- const IMPORT_SET_VAR_NUM_INDEX : Int = 1
6
-
7
- ///|
8
- const IMPORT_SET_VAR_JSON_INDEX : Int = 2
9
-
10
- ///|
11
- const IMPORT_MOD_INDEX : Int = 3
12
-
13
- ///|
14
- const IMPORT_EXEC_OPCODE_INDEX : Int = 4
15
-
16
- ///|
17
- const IMPORT_EXEC_TAIL_INDEX : Int = 5
18
-
19
- ///|
20
- const IMPORT_EXEC_DRAW_OPCODE_INDEX : Int = 6
21
-
22
- ///|
23
- const LOOP_COUNTER_LOCAL : Int = 0
24
-
25
- ///|
26
- const LOOP_GUARD_LOCAL : Int = 1
27
-
28
- ///|
29
- const LOOP_GUARD_MAX : Int = 10000
30
-
31
- ///|
32
- const RUNNER_CACHED_LOCAL_START : Int = 2
33
-
34
- ///|
35
- const RUNNER_CACHE_BASE_USE_THRESHOLD : Int = 4
36
-
37
- ///|
38
- const RUNNER_CACHE_MAX_VARIABLES : Int = 32
39
-
40
- ///|
41
- priv struct RunnerLiteralRef {
42
- ptr : Int
43
- len : Int
44
- }
45
-
46
- ///|
47
- priv struct WasmDataSegment {
48
- offset : Int
49
- bytes : Array[Byte]
50
- }
51
-
52
- ///|
53
- priv struct RunnerCachedVar {
54
- key : String
55
- target : Int
56
- variable_id : String
57
- local_index : Int
58
- use_count : Int
59
- }
60
-
61
- ///|
62
- priv enum RunnerNumExpr {
63
- Num(Double)
64
- VarNum(Int, String)
65
- BinNum(String, RunnerNumExpr, RunnerNumExpr)
66
- UnaryNum(String, RunnerNumExpr)
67
- }
68
-
69
- ///|
70
- priv enum RunnerBoolExpr {
71
- Bool(Bool)
72
- CmpNum(String, RunnerNumExpr, RunnerNumExpr)
73
- BinBool(String, RunnerBoolExpr, RunnerBoolExpr)
74
- Not(RunnerBoolExpr)
75
- }
76
-
77
- ///|
78
- priv enum RunnerCommand {
79
- SetNumExpr(Int, String, RunnerNumExpr)
80
- SetJsonConst(Int, String, String)
81
- ChangeNumExpr(Int, String, RunnerNumExpr)
82
- DrawOpcode(Int, String, RunnerNumExpr, RunnerNumExpr, Int)
83
- If(RunnerBoolExpr, Array[RunnerCommand])
84
- IfElse(RunnerBoolExpr, Array[RunnerCommand], Array[RunnerCommand])
85
- Repeat(RunnerNumExpr, Array[RunnerCommand])
86
- RepeatUntil(RunnerBoolExpr, Array[RunnerCommand])
87
- While(RunnerBoolExpr, Array[RunnerCommand])
88
- HostOpcode(Int, Int)
89
- HostTail(Int, Int)
90
- }
91
-
92
- ///|
93
- fn bytes_from_ints(values : Array[Int]) -> Array[Byte] {
94
- let out = []
95
- for value in values {
96
- out.push(value.to_byte())
97
- }
98
- out
99
- }
100
-
101
- ///|
102
- fn append_bytes(target : Array[Byte], source : Array[Byte]) -> Unit {
103
- for value in source {
104
- target.push(value)
105
- }
106
- }
107
-
108
- ///|
109
- fn encode_utf8_bytes(value : String) -> Array[Byte] {
110
- @utf8.encode(value.view()).to_array()
111
- }
112
-
113
- ///|
114
- fn encode_u32_leb_step(value : Int, out : Array[Byte]) -> Unit {
115
- let mut byte = value & 0x7f
116
- let next = value >> 7
117
- if next != 0 {
118
- byte = byte | 0x80
119
- }
120
- out.push(byte.to_byte())
121
- if next != 0 {
122
- encode_u32_leb_step(next, out)
123
- }
124
- }
125
-
126
- ///|
127
- fn encode_u32_leb(input : Int) -> Array[Byte] {
128
- if input < 0 {
129
- return []
130
- }
131
- let out = []
132
- encode_u32_leb_step(input, out)
133
- out
134
- }
135
-
136
- ///|
137
- fn encode_i32_leb_step(value : Int, out : Array[Byte]) -> Unit {
138
- let mut byte = value & 0x7f
139
- let next = value >> 7
140
- let sign_bit = (byte & 0x40) != 0
141
- let done = (next == 0 && !sign_bit) || (next == -1 && sign_bit)
142
- if !done {
143
- byte = byte | 0x80
144
- }
145
- out.push(byte.to_byte())
146
- if !done {
147
- encode_i32_leb_step(next, out)
148
- }
149
- }
150
-
151
- ///|
152
- fn encode_i32_leb(input : Int) -> Array[Byte] {
153
- let out = []
154
- encode_i32_leb_step(input, out)
155
- out
156
- }
157
-
158
- ///|
159
- fn encode_name(value : String) -> Array[Byte] {
160
- let bytes = encode_utf8_bytes(value)
161
- let out = []
162
- append_bytes(out, encode_u32_leb(bytes.length()))
163
- append_bytes(out, bytes)
164
- out
165
- }
166
-
167
- ///|
168
- fn encode_section(id : Int, payload : Array[Byte]) -> Array[Byte] {
169
- let out = [id.to_byte()]
170
- append_bytes(out, encode_u32_leb(payload.length()))
171
- append_bytes(out, payload)
172
- out
173
- }
174
-
175
- ///|
176
- fn push_i32_const(out : Array[Byte], input : Int) -> Unit {
177
- out.push((0x41).to_byte())
178
- append_bytes(out, encode_i32_leb(input))
179
- }
180
-
181
- ///|
182
- fn push_f64_const(out : Array[Byte], input : Double) -> Unit {
183
- out.push((0x44).to_byte())
184
- append_bytes(out, input.reinterpret_as_uint64().to_le_bytes().to_array())
185
- }
186
-
187
- ///|
188
- fn push_call(out : Array[Byte], function_index : Int) -> Unit {
189
- out.push((0x10).to_byte())
190
- append_bytes(out, encode_u32_leb(function_index))
191
- }
192
-
193
- ///|
194
- fn push_local_get(out : Array[Byte], local_index : Int) -> Unit {
195
- out.push((0x20).to_byte())
196
- append_bytes(out, encode_u32_leb(local_index))
197
- }
198
-
199
- ///|
200
- fn push_local_set(out : Array[Byte], local_index : Int) -> Unit {
201
- out.push((0x21).to_byte())
202
- append_bytes(out, encode_u32_leb(local_index))
203
- }
204
-
205
- ///|
206
- fn push_br(out : Array[Byte], depth : Int) -> Unit {
207
- out.push((0x0c).to_byte())
208
- append_bytes(out, encode_u32_leb(depth))
209
- }
210
-
211
- ///|
212
- fn push_br_if(out : Array[Byte], depth : Int) -> Unit {
213
- out.push((0x0d).to_byte())
214
- append_bytes(out, encode_u32_leb(depth))
215
- }
216
-
217
- ///|
218
- fn runner_variable_key(target : Int, variable_id : String) -> String {
219
- "\{target}:\{variable_id}"
220
- }
221
-
222
- ///|
223
- fn json_to_non_negative_int(raw : Json?) -> Int? {
224
- match raw {
225
- Some(Number(n, ..)) =>
226
- if n.is_nan() || n.is_inf() {
227
- None
228
- } else {
229
- let out = n.to_int()
230
- if out < 0 {
231
- None
232
- } else {
233
- Some(out)
234
- }
235
- }
236
- _ => None
237
- }
238
- }
239
-
240
- ///|
241
- fn parse_runner_num_expr(raw : Json, depth : Int) -> RunnerNumExpr? {
242
- if depth > 64 {
243
- return None
244
- }
245
- let obj = match raw {
246
- Object(value) => value
247
- _ => return None
248
- }
249
- let kind = object_get_string_or(obj, "kind", "")
250
- if kind == "num" {
251
- match obj.get("value") {
252
- Some(Number(value, ..)) =>
253
- if value.is_nan() || value.is_inf() {
254
- None
255
- } else {
256
- Some(RunnerNumExpr::Num(value))
257
- }
258
- _ => None
259
- }
260
- } else if kind == "var_num" {
261
- let target = json_to_non_negative_int(obj.get("target"))
262
- let variable_id = object_get_string_or(obj, "id", "")
263
- match target {
264
- Some(value) if variable_id != "" =>
265
- Some(RunnerNumExpr::VarNum(value, variable_id))
266
- _ => None
267
- }
268
- } else if kind == "bin_num" {
269
- let op = object_get_string_or(obj, "op", "")
270
- if op != "add" && op != "sub" && op != "mul" && op != "div" && op != "mod" {
271
- return None
272
- }
273
- match
274
- (
275
- parse_runner_num_expr(
276
- object_get_or(obj, "left", Json::null()),
277
- depth + 1,
278
- ),
279
- parse_runner_num_expr(
280
- object_get_or(obj, "right", Json::null()),
281
- depth + 1,
282
- ),
283
- ) {
284
- (Some(left), Some(right)) => Some(RunnerNumExpr::BinNum(op, left, right))
285
- _ => None
286
- }
287
- } else if kind == "unary_num" {
288
- let op = object_get_string_or(obj, "op", "")
289
- if op != "round" &&
290
- op != "abs" &&
291
- op != "floor" &&
292
- op != "ceil" &&
293
- op != "sqrt" {
294
- return None
295
- }
296
- match
297
- parse_runner_num_expr(
298
- object_get_or(obj, "value", Json::null()),
299
- depth + 1,
300
- ) {
301
- Some(value) => Some(RunnerNumExpr::UnaryNum(op, value))
302
- None => None
303
- }
304
- } else {
305
- None
306
- }
307
- }
308
-
309
- ///|
310
- fn parse_runner_bool_expr(raw : Json, depth : Int) -> RunnerBoolExpr? {
311
- if depth > 64 {
312
- return None
313
- }
314
- let obj = match raw {
315
- Object(value) => value
316
- _ => return None
317
- }
318
- let kind = object_get_string_or(obj, "kind", "")
319
- if kind == "bool" {
320
- match obj.get("value") {
321
- Some(True) => Some(RunnerBoolExpr::Bool(true))
322
- Some(False) => Some(RunnerBoolExpr::Bool(false))
323
- _ => None
324
- }
325
- } else if kind == "cmp_num" {
326
- let op = object_get_string_or(obj, "op", "")
327
- if op != "lt" && op != "gt" && op != "eq" && op != "ne" {
328
- return None
329
- }
330
- match
331
- (
332
- parse_runner_num_expr(
333
- object_get_or(obj, "left", Json::null()),
334
- depth + 1,
335
- ),
336
- parse_runner_num_expr(
337
- object_get_or(obj, "right", Json::null()),
338
- depth + 1,
339
- ),
340
- ) {
341
- (Some(left), Some(right)) => Some(RunnerBoolExpr::CmpNum(op, left, right))
342
- _ => None
343
- }
344
- } else if kind == "bin_bool" {
345
- let op = object_get_string_or(obj, "op", "")
346
- if op != "and" && op != "or" {
347
- return None
348
- }
349
- match
350
- (
351
- parse_runner_bool_expr(
352
- object_get_or(obj, "left", Json::null()),
353
- depth + 1,
354
- ),
355
- parse_runner_bool_expr(
356
- object_get_or(obj, "right", Json::null()),
357
- depth + 1,
358
- ),
359
- ) {
360
- (Some(left), Some(right)) =>
361
- Some(RunnerBoolExpr::BinBool(op, left, right))
362
- _ => None
363
- }
364
- } else if kind == "not" {
365
- match
366
- parse_runner_bool_expr(
367
- object_get_or(obj, "value", Json::null()),
368
- depth + 1,
369
- ) {
370
- Some(value) => Some(RunnerBoolExpr::Not(value))
371
- None => None
372
- }
373
- } else {
374
- None
375
- }
376
- }
377
-
378
- ///|
379
- fn parse_runner_command_array(
380
- raw : Array[Json],
381
- depth : Int,
382
- ) -> Array[RunnerCommand]? {
383
- let out = []
384
- for item in raw {
385
- match parse_runner_command(item, depth + 1) {
386
- Some(parsed) => out.push(parsed)
387
- None => return None
388
- }
389
- }
390
- Some(out)
391
- }
392
-
393
- ///|
394
- fn parse_runner_command(raw : Json, depth : Int) -> RunnerCommand? {
395
- if depth > 64 {
396
- return None
397
- }
398
- let obj = match raw {
399
- Object(value) => value
400
- _ => return None
401
- }
402
- let op = object_get_string_or(obj, "op", "")
403
- let target = json_to_non_negative_int(obj.get("target"))
404
- let variable_id = object_get_string_or(obj, "id", "")
405
-
406
- if op == "set_var" || op == "set_var_num_expr" || op == "set_var_json_const" {
407
- match target {
408
- Some(target_value) if variable_id != "" =>
409
- if op == "set_var_num_expr" {
410
- match
411
- parse_runner_num_expr(
412
- object_get_or(obj, "expr", Json::null()),
413
- depth + 1,
414
- ) {
415
- Some(expr) =>
416
- Some(RunnerCommand::SetNumExpr(target_value, variable_id, expr))
417
- None => None
418
- }
419
- } else if op == "set_var_json_const" {
420
- let value_json = object_get_or(obj, "value", Json::null()).stringify()
421
- Some(
422
- RunnerCommand::SetJsonConst(target_value, variable_id, value_json),
423
- )
424
- } else {
425
- match obj.get("value") {
426
- Some(Number(value, ..)) if !value.is_nan() && !value.is_inf() =>
427
- Some(
428
- RunnerCommand::SetNumExpr(
429
- target_value,
430
- variable_id,
431
- RunnerNumExpr::Num(value),
432
- ),
433
- )
434
- _ =>
435
- Some(
436
- RunnerCommand::SetJsonConst(
437
- target_value,
438
- variable_id,
439
- object_get_or(obj, "value", Json::null()).stringify(),
440
- ),
441
- )
442
- }
443
- }
444
- _ => None
445
- }
446
- } else if op == "change_var" || op == "change_var_num_expr" {
447
- match target {
448
- Some(target_value) if variable_id != "" =>
449
- if op == "change_var_num_expr" {
450
- match
451
- parse_runner_num_expr(
452
- object_get_or(obj, "expr", Json::null()),
453
- depth + 1,
454
- ) {
455
- Some(expr) =>
456
- Some(
457
- RunnerCommand::ChangeNumExpr(target_value, variable_id, expr),
458
- )
459
- None => None
460
- }
461
- } else {
462
- let delta = match obj.get("delta") {
463
- Some(Number(value, ..)) if !value.is_nan() && !value.is_inf() =>
464
- value
465
- _ => 0.0
466
- }
467
- Some(
468
- RunnerCommand::ChangeNumExpr(
469
- target_value,
470
- variable_id,
471
- RunnerNumExpr::Num(delta),
472
- ),
473
- )
474
- }
475
- _ => None
476
- }
477
- } else if op == "draw_opcode" {
478
- match target {
479
- Some(target_value) => {
480
- let draw_opcode = object_get_string_or(obj, "opcode", "")
481
- let extra = match obj.get("extra") {
482
- Some(Number(value, ..)) if !value.is_nan() && !value.is_inf() =>
483
- value.to_int()
484
- _ => 0
485
- }
486
- if draw_opcode == "" {
487
- None
488
- } else {
489
- let arg0 = match
490
- parse_runner_num_expr(
491
- object_get_or(obj, "arg0", Json::null()),
492
- depth + 1,
493
- ) {
494
- Some(value) => value
495
- None => RunnerNumExpr::Num(0.0)
496
- }
497
- let arg1 = match
498
- parse_runner_num_expr(
499
- object_get_or(obj, "arg1", Json::null()),
500
- depth + 1,
501
- ) {
502
- Some(value) => value
503
- None => RunnerNumExpr::Num(0.0)
504
- }
505
- Some(
506
- RunnerCommand::DrawOpcode(
507
- target_value, draw_opcode, arg0, arg1, extra,
508
- ),
509
- )
510
- }
511
- }
512
- None => None
513
- }
514
- } else if op == "if" {
515
- match
516
- (
517
- parse_runner_bool_expr(
518
- object_get_or(obj, "cond", Json::null()),
519
- depth + 1,
520
- ),
521
- match obj.get("then") {
522
- Some(Array(values)) => parse_runner_command_array(values, depth + 1)
523
- _ => None
524
- },
525
- ) {
526
- (Some(cond), Some(then_commands)) =>
527
- Some(RunnerCommand::If(cond, then_commands))
528
- _ => None
529
- }
530
- } else if op == "if_else" {
531
- match
532
- (
533
- parse_runner_bool_expr(
534
- object_get_or(obj, "cond", Json::null()),
535
- depth + 1,
536
- ),
537
- match obj.get("then") {
538
- Some(Array(values)) => parse_runner_command_array(values, depth + 1)
539
- _ => None
540
- },
541
- match obj.get("else") {
542
- Some(Array(values)) => parse_runner_command_array(values, depth + 1)
543
- _ => None
544
- },
545
- ) {
546
- (Some(cond), Some(then_commands), Some(else_commands)) =>
547
- Some(RunnerCommand::IfElse(cond, then_commands, else_commands))
548
- _ => None
549
- }
550
- } else if op == "repeat" {
551
- match
552
- (
553
- parse_runner_num_expr(
554
- object_get_or(obj, "times", Json::null()),
555
- depth + 1,
556
- ),
557
- match obj.get("body") {
558
- Some(Array(values)) => parse_runner_command_array(values, depth + 1)
559
- _ => None
560
- },
561
- ) {
562
- (Some(times), Some(body)) => Some(RunnerCommand::Repeat(times, body))
563
- _ => None
564
- }
565
- } else if op == "repeat_until" {
566
- match
567
- (
568
- parse_runner_bool_expr(
569
- object_get_or(obj, "cond", Json::null()),
570
- depth + 1,
571
- ),
572
- match obj.get("body") {
573
- Some(Array(values)) => parse_runner_command_array(values, depth + 1)
574
- _ => None
575
- },
576
- ) {
577
- (Some(cond), Some(body)) => Some(RunnerCommand::RepeatUntil(cond, body))
578
- _ => None
579
- }
580
- } else if op == "while" {
581
- match
582
- (
583
- parse_runner_bool_expr(
584
- object_get_or(obj, "cond", Json::null()),
585
- depth + 1,
586
- ),
587
- match obj.get("body") {
588
- Some(Array(values)) => parse_runner_command_array(values, depth + 1)
589
- _ => None
590
- },
591
- ) {
592
- (Some(cond), Some(body)) => Some(RunnerCommand::While(cond, body))
593
- _ => None
594
- }
595
- } else if op == "host_tail" {
596
- match (target, json_to_non_negative_int(obj.get("pc"))) {
597
- (Some(target_value), Some(start_pc)) =>
598
- Some(RunnerCommand::HostTail(target_value, start_pc))
599
- _ => None
600
- }
601
- } else if op == "host_opcode" {
602
- match (target, json_to_non_negative_int(obj.get("pc"))) {
603
- (Some(target_value), Some(pc)) =>
604
- Some(RunnerCommand::HostOpcode(target_value, pc))
605
- _ => None
606
- }
607
- } else {
608
- None
609
- }
610
- }
611
-
612
- ///|
613
- fn parse_runner_commands_json(commands_json : String) -> Array[RunnerCommand] {
614
- if commands_json.trim().is_empty() {
615
- return []
616
- }
617
- let parsed = try? @json.parse(commands_json)
618
- let commands_raw = match parsed {
619
- Ok(Array(values)) => values
620
- Ok(Object(obj)) =>
621
- match obj.get("exec") {
622
- Some(Array(values)) => values
623
- _ => []
624
- }
625
- _ => []
626
- }
627
- match parse_runner_command_array(commands_raw, 0) {
628
- Some(commands) => commands
629
- None => []
630
- }
631
- }
632
-
633
- ///|
634
- fn runner_cached_var_usage_touch(
635
- usage : Map[String, RunnerCachedVar],
636
- order : Array[String],
637
- target : Int,
638
- variable_id : String,
639
- weight : Int,
640
- ) -> Unit {
641
- let normalized_weight = if weight <= 0 { 1 } else { weight }
642
- let key = runner_variable_key(target, variable_id)
643
- match usage.get(key) {
644
- Some(current) =>
645
- usage[key] = {
646
- key: current.key,
647
- target: current.target,
648
- variable_id: current.variable_id,
649
- local_index: current.local_index,
650
- use_count: current.use_count + normalized_weight,
651
- }
652
- None => {
653
- usage[key] = {
654
- key,
655
- target,
656
- variable_id,
657
- local_index: -1,
658
- use_count: normalized_weight,
659
- }
660
- order.push(key)
661
- }
662
- }
663
- }
664
-
665
- ///|
666
- fn runner_usage_weight(loop_depth : Int) -> Int {
667
- let capped = if loop_depth < 0 {
668
- 0
669
- } else if loop_depth > 4 {
670
- 4
671
- } else {
672
- loop_depth
673
- }
674
- 1 << capped
675
- }
676
-
677
- ///|
678
- fn runner_collect_num_expr_usage(
679
- expr : RunnerNumExpr,
680
- usage : Map[String, RunnerCachedVar],
681
- order : Array[String],
682
- weight : Int,
683
- ) -> Unit {
684
- match expr {
685
- Num(_) => ()
686
- VarNum(target, variable_id) =>
687
- runner_cached_var_usage_touch(usage, order, target, variable_id, weight)
688
- BinNum(_, left, right) => {
689
- runner_collect_num_expr_usage(left, usage, order, weight)
690
- runner_collect_num_expr_usage(right, usage, order, weight)
691
- }
692
- UnaryNum(_, value) =>
693
- runner_collect_num_expr_usage(value, usage, order, weight)
694
- }
695
- }
696
-
697
- ///|
698
- fn runner_collect_bool_expr_usage(
699
- expr : RunnerBoolExpr,
700
- usage : Map[String, RunnerCachedVar],
701
- order : Array[String],
702
- weight : Int,
703
- ) -> Unit {
704
- match expr {
705
- Bool(_) => ()
706
- CmpNum(_, left, right) => {
707
- runner_collect_num_expr_usage(left, usage, order, weight)
708
- runner_collect_num_expr_usage(right, usage, order, weight)
709
- }
710
- BinBool(_, left, right) => {
711
- runner_collect_bool_expr_usage(left, usage, order, weight)
712
- runner_collect_bool_expr_usage(right, usage, order, weight)
713
- }
714
- Not(value) => runner_collect_bool_expr_usage(value, usage, order, weight)
715
- }
716
- }
717
-
718
- ///|
719
- fn runner_collect_usage_from_commands(
720
- commands : Array[RunnerCommand],
721
- usage : Map[String, RunnerCachedVar],
722
- order : Array[String],
723
- blocked : Map[String, Bool],
724
- loop_depth : Int,
725
- ) -> Int {
726
- let current_weight = runner_usage_weight(loop_depth)
727
- let mut host_barrier_count = 0
728
- for command in commands {
729
- host_barrier_count += match command {
730
- SetNumExpr(target, variable_id, expr) => {
731
- runner_cached_var_usage_touch(
732
- usage, order, target, variable_id, current_weight,
733
- )
734
- runner_collect_num_expr_usage(expr, usage, order, current_weight)
735
- 0
736
- }
737
- SetJsonConst(target, variable_id, _) => {
738
- blocked[runner_variable_key(target, variable_id)] = true
739
- 0
740
- }
741
- ChangeNumExpr(target, variable_id, expr) => {
742
- runner_cached_var_usage_touch(
743
- usage, order, target, variable_id, current_weight,
744
- )
745
- runner_collect_num_expr_usage(expr, usage, order, current_weight)
746
- 0
747
- }
748
- DrawOpcode(_, _, arg0, arg1, _) => {
749
- runner_collect_num_expr_usage(arg0, usage, order, current_weight)
750
- runner_collect_num_expr_usage(arg1, usage, order, current_weight)
751
- 0
752
- }
753
- If(cond, then_commands) => {
754
- runner_collect_bool_expr_usage(cond, usage, order, current_weight)
755
- runner_collect_usage_from_commands(
756
- then_commands, usage, order, blocked, loop_depth,
757
- )
758
- }
759
- IfElse(cond, then_commands, else_commands) => {
760
- runner_collect_bool_expr_usage(cond, usage, order, current_weight)
761
- runner_collect_usage_from_commands(
762
- then_commands, usage, order, blocked, loop_depth,
763
- ) +
764
- runner_collect_usage_from_commands(
765
- else_commands, usage, order, blocked, loop_depth,
766
- )
767
- }
768
- Repeat(times, body) => {
769
- runner_collect_num_expr_usage(times, usage, order, current_weight)
770
- runner_collect_usage_from_commands(
771
- body,
772
- usage,
773
- order,
774
- blocked,
775
- loop_depth + 1,
776
- )
777
- }
778
- RepeatUntil(cond, body) => {
779
- runner_collect_bool_expr_usage(
780
- cond,
781
- usage,
782
- order,
783
- runner_usage_weight(loop_depth + 1),
784
- )
785
- runner_collect_usage_from_commands(
786
- body,
787
- usage,
788
- order,
789
- blocked,
790
- loop_depth + 1,
791
- )
792
- }
793
- While(cond, body) => {
794
- runner_collect_bool_expr_usage(
795
- cond,
796
- usage,
797
- order,
798
- runner_usage_weight(loop_depth + 1),
799
- )
800
- runner_collect_usage_from_commands(
801
- body,
802
- usage,
803
- order,
804
- blocked,
805
- loop_depth + 1,
806
- )
807
- }
808
- HostOpcode(_, _) | HostTail(_, _) => current_weight
809
- }
810
- }
811
- host_barrier_count
812
- }
813
-
814
- ///|
815
- fn runner_cache_min_use_count(host_barrier_count : Int) -> Int {
816
- let _ignored = host_barrier_count
817
- RUNNER_CACHE_BASE_USE_THRESHOLD
818
- }
819
-
820
- ///|
821
- fn build_runner_cached_vars(
822
- commands : Array[RunnerCommand],
823
- ) -> (Map[String, RunnerCachedVar], Array[RunnerCachedVar]) {
824
- let usage : Map[String, RunnerCachedVar] = {}
825
- let order = []
826
- let blocked : Map[String, Bool] = {}
827
- let host_barrier_count = runner_collect_usage_from_commands(
828
- commands, usage, order, blocked, 0,
829
- )
830
- let min_use_count = runner_cache_min_use_count(host_barrier_count)
831
- let cached_by_key : Map[String, RunnerCachedVar] = {}
832
- let cached_vars = []
833
- let mut next_local_index = RUNNER_CACHED_LOCAL_START
834
- for key in order {
835
- if cached_vars.length() < RUNNER_CACHE_MAX_VARIABLES &&
836
- !blocked.contains(key) {
837
- match usage.get(key) {
838
- Some(candidate) =>
839
- if candidate.use_count >= min_use_count {
840
- let cached = {
841
- key: candidate.key,
842
- target: candidate.target,
843
- variable_id: candidate.variable_id,
844
- local_index: next_local_index,
845
- use_count: candidate.use_count,
846
- }
847
- cached_by_key[key] = cached
848
- cached_vars.push(cached)
849
- next_local_index += 1
850
- }
851
- None => ()
852
- }
853
- }
854
- }
855
- (cached_by_key, cached_vars)
856
- }
857
-
858
- ///|
859
- fn runner_cached_var_lookup(
860
- cached_by_key : Map[String, RunnerCachedVar],
861
- target : Int,
862
- variable_id : String,
863
- ) -> RunnerCachedVar? {
864
- cached_by_key.get(runner_variable_key(target, variable_id))
865
- }
866
-
867
- ///|
868
- fn emit_runner_load_cached_var(
869
- cached : RunnerCachedVar,
870
- refs : Map[String, RunnerLiteralRef],
871
- out : Array[Byte],
872
- ) -> Bool {
873
- match refs.get("id:\{cached.variable_id}") {
874
- Some(id_ref) => {
875
- push_i32_const(out, cached.target)
876
- push_i32_const(out, id_ref.ptr)
877
- push_i32_const(out, id_ref.len)
878
- push_call(out, IMPORT_GET_VAR_NUM_INDEX)
879
- push_local_set(out, cached.local_index)
880
- true
881
- }
882
- None => false
883
- }
884
- }
885
-
886
- ///|
887
- fn emit_runner_flush_cached_var(
888
- cached : RunnerCachedVar,
889
- refs : Map[String, RunnerLiteralRef],
890
- out : Array[Byte],
891
- ) -> Bool {
892
- match refs.get("id:\{cached.variable_id}") {
893
- Some(id_ref) => {
894
- push_i32_const(out, cached.target)
895
- push_i32_const(out, id_ref.ptr)
896
- push_i32_const(out, id_ref.len)
897
- push_local_get(out, cached.local_index)
898
- push_call(out, IMPORT_SET_VAR_NUM_INDEX)
899
- true
900
- }
901
- None => false
902
- }
903
- }
904
-
905
- ///|
906
- fn emit_runner_sync_cached_vars(
907
- cached_vars : Array[RunnerCachedVar],
908
- refs : Map[String, RunnerLiteralRef],
909
- out : Array[Byte],
910
- load_mode : Bool,
911
- ) -> Bool {
912
- for cached in cached_vars {
913
- let ok = if load_mode {
914
- emit_runner_load_cached_var(cached, refs, out)
915
- } else {
916
- emit_runner_flush_cached_var(cached, refs, out)
917
- }
918
- if !ok {
919
- return false
920
- }
921
- }
922
- true
923
- }
924
-
925
- ///|
926
- fn runner_register_literal(
927
- refs : Map[String, RunnerLiteralRef],
928
- bytes : Array[Byte],
929
- base_ptr : Int,
930
- key : String,
931
- literal : String,
932
- ) -> Unit {
933
- match refs.get(key) {
934
- Some(_) => return
935
- None => ()
936
- }
937
- let encoded = encode_utf8_bytes(literal)
938
- refs[key] = { ptr: base_ptr + bytes.length(), len: encoded.length() }
939
- append_bytes(bytes, encoded)
940
- }
941
-
942
- ///|
943
- fn collect_num_expr_literals(
944
- expr : RunnerNumExpr,
945
- refs : Map[String, RunnerLiteralRef],
946
- bytes : Array[Byte],
947
- base_ptr : Int,
948
- ) -> Unit {
949
- match expr {
950
- Num(_) => ()
951
- VarNum(_, variable_id) =>
952
- runner_register_literal(
953
- refs,
954
- bytes,
955
- base_ptr,
956
- "id:\{variable_id}",
957
- variable_id,
958
- )
959
- BinNum(_, left, right) => {
960
- collect_num_expr_literals(left, refs, bytes, base_ptr)
961
- collect_num_expr_literals(right, refs, bytes, base_ptr)
962
- }
963
- UnaryNum(_, value) =>
964
- collect_num_expr_literals(value, refs, bytes, base_ptr)
965
- }
966
- }
967
-
968
- ///|
969
- fn collect_bool_expr_literals(
970
- expr : RunnerBoolExpr,
971
- refs : Map[String, RunnerLiteralRef],
972
- bytes : Array[Byte],
973
- base_ptr : Int,
974
- ) -> Unit {
975
- match expr {
976
- Bool(_) => ()
977
- CmpNum(_, left, right) => {
978
- collect_num_expr_literals(left, refs, bytes, base_ptr)
979
- collect_num_expr_literals(right, refs, bytes, base_ptr)
980
- }
981
- BinBool(_, left, right) => {
982
- collect_bool_expr_literals(left, refs, bytes, base_ptr)
983
- collect_bool_expr_literals(right, refs, bytes, base_ptr)
984
- }
985
- Not(value) => collect_bool_expr_literals(value, refs, bytes, base_ptr)
986
- }
987
- }
988
-
989
- ///|
990
- fn collect_command_literals(
991
- command : RunnerCommand,
992
- refs : Map[String, RunnerLiteralRef],
993
- bytes : Array[Byte],
994
- base_ptr : Int,
995
- ) -> Unit {
996
- match command {
997
- SetNumExpr(_, variable_id, expr) => {
998
- runner_register_literal(
999
- refs,
1000
- bytes,
1001
- base_ptr,
1002
- "id:\{variable_id}",
1003
- variable_id,
1004
- )
1005
- collect_num_expr_literals(expr, refs, bytes, base_ptr)
1006
- }
1007
- SetJsonConst(_, variable_id, value_json) => {
1008
- runner_register_literal(
1009
- refs,
1010
- bytes,
1011
- base_ptr,
1012
- "id:\{variable_id}",
1013
- variable_id,
1014
- )
1015
- runner_register_literal(
1016
- refs,
1017
- bytes,
1018
- base_ptr,
1019
- "json:\{value_json}",
1020
- value_json,
1021
- )
1022
- }
1023
- ChangeNumExpr(_, variable_id, expr) => {
1024
- runner_register_literal(
1025
- refs,
1026
- bytes,
1027
- base_ptr,
1028
- "id:\{variable_id}",
1029
- variable_id,
1030
- )
1031
- collect_num_expr_literals(expr, refs, bytes, base_ptr)
1032
- }
1033
- DrawOpcode(_, opcode, arg0, arg1, _) => {
1034
- runner_register_literal(refs, bytes, base_ptr, "opcode:\{opcode}", opcode)
1035
- collect_num_expr_literals(arg0, refs, bytes, base_ptr)
1036
- collect_num_expr_literals(arg1, refs, bytes, base_ptr)
1037
- }
1038
- If(cond, then_commands) => {
1039
- collect_bool_expr_literals(cond, refs, bytes, base_ptr)
1040
- for child in then_commands {
1041
- collect_command_literals(child, refs, bytes, base_ptr)
1042
- }
1043
- }
1044
- IfElse(cond, then_commands, else_commands) => {
1045
- collect_bool_expr_literals(cond, refs, bytes, base_ptr)
1046
- for child in then_commands {
1047
- collect_command_literals(child, refs, bytes, base_ptr)
1048
- }
1049
- for child in else_commands {
1050
- collect_command_literals(child, refs, bytes, base_ptr)
1051
- }
1052
- }
1053
- Repeat(times, body) => {
1054
- collect_num_expr_literals(times, refs, bytes, base_ptr)
1055
- for child in body {
1056
- collect_command_literals(child, refs, bytes, base_ptr)
1057
- }
1058
- }
1059
- RepeatUntil(cond, body) => {
1060
- collect_bool_expr_literals(cond, refs, bytes, base_ptr)
1061
- for child in body {
1062
- collect_command_literals(child, refs, bytes, base_ptr)
1063
- }
1064
- }
1065
- While(cond, body) => {
1066
- collect_bool_expr_literals(cond, refs, bytes, base_ptr)
1067
- for child in body {
1068
- collect_command_literals(child, refs, bytes, base_ptr)
1069
- }
1070
- }
1071
- HostOpcode(_, _) | HostTail(_, _) => ()
1072
- }
1073
- }
1074
-
1075
- ///|
1076
- fn build_runner_literal_pool(
1077
- commands : Array[RunnerCommand],
1078
- base_ptr : Int,
1079
- ) -> (Array[Byte], Map[String, RunnerLiteralRef]) {
1080
- let refs : Map[String, RunnerLiteralRef] = {}
1081
- let bytes = []
1082
- for command in commands {
1083
- collect_command_literals(command, refs, bytes, base_ptr)
1084
- }
1085
- (bytes, refs)
1086
- }
1087
-
1088
- ///|
1089
- fn emit_runner_num_expr(
1090
- expr : RunnerNumExpr,
1091
- refs : Map[String, RunnerLiteralRef],
1092
- cached_by_key : Map[String, RunnerCachedVar],
1093
- out : Array[Byte],
1094
- ) -> Bool {
1095
- match expr {
1096
- Num(value) => {
1097
- push_f64_const(out, value)
1098
- true
1099
- }
1100
- VarNum(target, variable_id) =>
1101
- match runner_cached_var_lookup(cached_by_key, target, variable_id) {
1102
- Some(cached) => {
1103
- push_local_get(out, cached.local_index)
1104
- true
1105
- }
1106
- None =>
1107
- match refs.get("id:\{variable_id}") {
1108
- Some(id_ref) => {
1109
- push_i32_const(out, target)
1110
- push_i32_const(out, id_ref.ptr)
1111
- push_i32_const(out, id_ref.len)
1112
- push_call(out, IMPORT_GET_VAR_NUM_INDEX)
1113
- true
1114
- }
1115
- None => false
1116
- }
1117
- }
1118
- BinNum(op, left, right) => {
1119
- if !emit_runner_num_expr(left, refs, cached_by_key, out) ||
1120
- !emit_runner_num_expr(right, refs, cached_by_key, out) {
1121
- return false
1122
- }
1123
- if op == "add" {
1124
- out.push((0xa0).to_byte())
1125
- } else if op == "sub" {
1126
- out.push((0xa1).to_byte())
1127
- } else if op == "mul" {
1128
- out.push((0xa2).to_byte())
1129
- } else if op == "div" {
1130
- out.push((0xa3).to_byte())
1131
- } else {
1132
- push_call(out, IMPORT_MOD_INDEX)
1133
- }
1134
- true
1135
- }
1136
- UnaryNum(op, value) => {
1137
- if !emit_runner_num_expr(value, refs, cached_by_key, out) {
1138
- return false
1139
- }
1140
- if op == "round" {
1141
- out.push((0x9e).to_byte())
1142
- } else if op == "abs" {
1143
- out.push((0x99).to_byte())
1144
- } else if op == "floor" {
1145
- out.push((0x9c).to_byte())
1146
- } else if op == "ceil" {
1147
- out.push((0x9b).to_byte())
1148
- } else {
1149
- out.push((0x9f).to_byte())
1150
- }
1151
- true
1152
- }
1153
- }
1154
- }
1155
-
1156
- ///|
1157
- fn emit_runner_bool_expr(
1158
- expr : RunnerBoolExpr,
1159
- refs : Map[String, RunnerLiteralRef],
1160
- cached_by_key : Map[String, RunnerCachedVar],
1161
- out : Array[Byte],
1162
- ) -> Bool {
1163
- match expr {
1164
- Bool(value) => {
1165
- push_i32_const(out, if value { 1 } else { 0 })
1166
- true
1167
- }
1168
- CmpNum(op, left, right) => {
1169
- if !emit_runner_num_expr(left, refs, cached_by_key, out) ||
1170
- !emit_runner_num_expr(right, refs, cached_by_key, out) {
1171
- return false
1172
- }
1173
- if op == "lt" {
1174
- out.push((0x63).to_byte())
1175
- } else if op == "gt" {
1176
- out.push((0x64).to_byte())
1177
- } else if op == "eq" {
1178
- out.push((0x61).to_byte())
1179
- } else {
1180
- out.push((0x61).to_byte())
1181
- out.push((0x45).to_byte())
1182
- }
1183
- true
1184
- }
1185
- BinBool(op, left, right) => {
1186
- if !emit_runner_bool_expr(left, refs, cached_by_key, out) ||
1187
- !emit_runner_bool_expr(right, refs, cached_by_key, out) {
1188
- return false
1189
- }
1190
- out.push(if op == "and" { (0x71).to_byte() } else { (0x72).to_byte() })
1191
- true
1192
- }
1193
- Not(value) => {
1194
- if !emit_runner_bool_expr(value, refs, cached_by_key, out) {
1195
- return false
1196
- }
1197
- out.push((0x45).to_byte())
1198
- true
1199
- }
1200
- }
1201
- }
1202
-
1203
- ///|
1204
- fn emit_runner_commands(
1205
- commands : Array[RunnerCommand],
1206
- refs : Map[String, RunnerLiteralRef],
1207
- cached_by_key : Map[String, RunnerCachedVar],
1208
- cached_vars : Array[RunnerCachedVar],
1209
- out : Array[Byte],
1210
- ) -> Bool {
1211
- for command in commands {
1212
- match command {
1213
- SetNumExpr(target, variable_id, expr) =>
1214
- match runner_cached_var_lookup(cached_by_key, target, variable_id) {
1215
- Some(cached) => {
1216
- if !emit_runner_num_expr(expr, refs, cached_by_key, out) {
1217
- return false
1218
- }
1219
- push_local_set(out, cached.local_index)
1220
- }
1221
- None =>
1222
- match refs.get("id:\{variable_id}") {
1223
- Some(id_ref) => {
1224
- push_i32_const(out, target)
1225
- push_i32_const(out, id_ref.ptr)
1226
- push_i32_const(out, id_ref.len)
1227
- if !emit_runner_num_expr(expr, refs, cached_by_key, out) {
1228
- return false
1229
- }
1230
- push_call(out, IMPORT_SET_VAR_NUM_INDEX)
1231
- }
1232
- None => return false
1233
- }
1234
- }
1235
- SetJsonConst(target, variable_id, value_json) =>
1236
- match (refs.get("id:\{variable_id}"), refs.get("json:\{value_json}")) {
1237
- (Some(id_ref), Some(json_ref)) => {
1238
- push_i32_const(out, target)
1239
- push_i32_const(out, id_ref.ptr)
1240
- push_i32_const(out, id_ref.len)
1241
- push_i32_const(out, json_ref.ptr)
1242
- push_i32_const(out, json_ref.len)
1243
- push_call(out, IMPORT_SET_VAR_JSON_INDEX)
1244
- }
1245
- _ => return false
1246
- }
1247
- ChangeNumExpr(target, variable_id, expr) =>
1248
- match runner_cached_var_lookup(cached_by_key, target, variable_id) {
1249
- Some(cached) => {
1250
- push_local_get(out, cached.local_index)
1251
- if !emit_runner_num_expr(expr, refs, cached_by_key, out) {
1252
- return false
1253
- }
1254
- out.push((0xa0).to_byte())
1255
- push_local_set(out, cached.local_index)
1256
- }
1257
- None =>
1258
- match refs.get("id:\{variable_id}") {
1259
- Some(id_ref) => {
1260
- push_i32_const(out, target)
1261
- push_i32_const(out, id_ref.ptr)
1262
- push_i32_const(out, id_ref.len)
1263
- push_i32_const(out, target)
1264
- push_i32_const(out, id_ref.ptr)
1265
- push_i32_const(out, id_ref.len)
1266
- push_call(out, IMPORT_GET_VAR_NUM_INDEX)
1267
- if !emit_runner_num_expr(expr, refs, cached_by_key, out) {
1268
- return false
1269
- }
1270
- out.push((0xa0).to_byte())
1271
- push_call(out, IMPORT_SET_VAR_NUM_INDEX)
1272
- }
1273
- None => return false
1274
- }
1275
- }
1276
- DrawOpcode(target, opcode, arg0, arg1, extra) =>
1277
- match refs.get("opcode:\{opcode}") {
1278
- Some(opcode_ref) => {
1279
- push_i32_const(out, target)
1280
- push_i32_const(out, opcode_ref.ptr)
1281
- push_i32_const(out, opcode_ref.len)
1282
- if !emit_runner_num_expr(arg0, refs, cached_by_key, out) ||
1283
- !emit_runner_num_expr(arg1, refs, cached_by_key, out) {
1284
- return false
1285
- }
1286
- push_i32_const(out, extra)
1287
- push_call(out, IMPORT_EXEC_DRAW_OPCODE_INDEX)
1288
- out.push((0x1a).to_byte())
1289
- }
1290
- None => return false
1291
- }
1292
- If(cond, then_commands) => {
1293
- if !emit_runner_bool_expr(cond, refs, cached_by_key, out) {
1294
- return false
1295
- }
1296
- out.push((0x04).to_byte())
1297
- out.push((0x40).to_byte())
1298
- if !emit_runner_commands(
1299
- then_commands, refs, cached_by_key, cached_vars, out,
1300
- ) {
1301
- return false
1302
- }
1303
- out.push((0x0b).to_byte())
1304
- }
1305
- IfElse(cond, then_commands, else_commands) => {
1306
- if !emit_runner_bool_expr(cond, refs, cached_by_key, out) {
1307
- return false
1308
- }
1309
- out.push((0x04).to_byte())
1310
- out.push((0x40).to_byte())
1311
- if !emit_runner_commands(
1312
- then_commands, refs, cached_by_key, cached_vars, out,
1313
- ) {
1314
- return false
1315
- }
1316
- out.push((0x05).to_byte())
1317
- if !emit_runner_commands(
1318
- else_commands, refs, cached_by_key, cached_vars, out,
1319
- ) {
1320
- return false
1321
- }
1322
- out.push((0x0b).to_byte())
1323
- }
1324
- Repeat(times, body) => {
1325
- if !emit_runner_num_expr(times, refs, cached_by_key, out) {
1326
- return false
1327
- }
1328
- push_local_set(out, LOOP_COUNTER_LOCAL)
1329
- push_i32_const(out, LOOP_GUARD_MAX)
1330
- push_local_set(out, LOOP_GUARD_LOCAL)
1331
- out.push((0x02).to_byte())
1332
- out.push((0x40).to_byte())
1333
- out.push((0x03).to_byte())
1334
- out.push((0x40).to_byte())
1335
- push_local_get(out, LOOP_GUARD_LOCAL)
1336
- out.push((0x45).to_byte())
1337
- push_br_if(out, 1)
1338
- push_local_get(out, LOOP_COUNTER_LOCAL)
1339
- push_f64_const(out, 0.0)
1340
- out.push((0x65).to_byte())
1341
- push_br_if(out, 1)
1342
- if !emit_runner_commands(body, refs, cached_by_key, cached_vars, out) {
1343
- return false
1344
- }
1345
- push_local_get(out, LOOP_COUNTER_LOCAL)
1346
- push_f64_const(out, 1.0)
1347
- out.push((0xa1).to_byte())
1348
- push_local_set(out, LOOP_COUNTER_LOCAL)
1349
- push_local_get(out, LOOP_GUARD_LOCAL)
1350
- push_i32_const(out, 1)
1351
- out.push((0x6b).to_byte())
1352
- push_local_set(out, LOOP_GUARD_LOCAL)
1353
- push_br(out, 0)
1354
- out.push((0x0b).to_byte())
1355
- out.push((0x0b).to_byte())
1356
- }
1357
- RepeatUntil(cond, body) => {
1358
- push_i32_const(out, LOOP_GUARD_MAX)
1359
- push_local_set(out, LOOP_GUARD_LOCAL)
1360
- out.push((0x02).to_byte())
1361
- out.push((0x40).to_byte())
1362
- out.push((0x03).to_byte())
1363
- out.push((0x40).to_byte())
1364
- push_local_get(out, LOOP_GUARD_LOCAL)
1365
- out.push((0x45).to_byte())
1366
- push_br_if(out, 1)
1367
- if !emit_runner_bool_expr(cond, refs, cached_by_key, out) {
1368
- return false
1369
- }
1370
- push_br_if(out, 1)
1371
- if !emit_runner_commands(body, refs, cached_by_key, cached_vars, out) {
1372
- return false
1373
- }
1374
- push_local_get(out, LOOP_GUARD_LOCAL)
1375
- push_i32_const(out, 1)
1376
- out.push((0x6b).to_byte())
1377
- push_local_set(out, LOOP_GUARD_LOCAL)
1378
- push_br(out, 0)
1379
- out.push((0x0b).to_byte())
1380
- out.push((0x0b).to_byte())
1381
- }
1382
- While(cond, body) => {
1383
- push_i32_const(out, LOOP_GUARD_MAX)
1384
- push_local_set(out, LOOP_GUARD_LOCAL)
1385
- out.push((0x02).to_byte())
1386
- out.push((0x40).to_byte())
1387
- out.push((0x03).to_byte())
1388
- out.push((0x40).to_byte())
1389
- push_local_get(out, LOOP_GUARD_LOCAL)
1390
- out.push((0x45).to_byte())
1391
- push_br_if(out, 1)
1392
- if !emit_runner_bool_expr(cond, refs, cached_by_key, out) {
1393
- return false
1394
- }
1395
- out.push((0x45).to_byte())
1396
- push_br_if(out, 1)
1397
- if !emit_runner_commands(body, refs, cached_by_key, cached_vars, out) {
1398
- return false
1399
- }
1400
- push_local_get(out, LOOP_GUARD_LOCAL)
1401
- push_i32_const(out, 1)
1402
- out.push((0x6b).to_byte())
1403
- push_local_set(out, LOOP_GUARD_LOCAL)
1404
- push_br(out, 0)
1405
- out.push((0x0b).to_byte())
1406
- out.push((0x0b).to_byte())
1407
- }
1408
- HostTail(target, start_pc) => {
1409
- if !emit_runner_sync_cached_vars(cached_vars, refs, out, false) {
1410
- return false
1411
- }
1412
- push_i32_const(out, target)
1413
- push_i32_const(out, start_pc)
1414
- push_call(out, IMPORT_EXEC_TAIL_INDEX)
1415
- out.push((0x1a).to_byte())
1416
- if !emit_runner_sync_cached_vars(cached_vars, refs, out, true) {
1417
- return false
1418
- }
1419
- }
1420
- HostOpcode(target, pc) => {
1421
- if !emit_runner_sync_cached_vars(cached_vars, refs, out, false) {
1422
- return false
1423
- }
1424
- push_i32_const(out, target)
1425
- push_i32_const(out, pc)
1426
- push_call(out, IMPORT_EXEC_OPCODE_INDEX)
1427
- out.push((0x1a).to_byte())
1428
- if !emit_runner_sync_cached_vars(cached_vars, refs, out, true) {
1429
- return false
1430
- }
1431
- }
1432
- }
1433
- }
1434
- true
1435
- }
1436
-
1437
- ///|
1438
- fn append_runner_exec_locals(
1439
- body : Array[Byte],
1440
- cached_var_count : Int,
1441
- ) -> Unit {
1442
- if cached_var_count > 0 {
1443
- body.push((0x03).to_byte())
1444
- body.push((0x01).to_byte())
1445
- body.push((0x7c).to_byte())
1446
- body.push((0x01).to_byte())
1447
- body.push((0x7f).to_byte())
1448
- append_bytes(body, encode_u32_leb(cached_var_count))
1449
- body.push((0x7c).to_byte())
1450
- } else {
1451
- body.push((0x02).to_byte())
1452
- body.push((0x01).to_byte())
1453
- body.push((0x7c).to_byte())
1454
- body.push((0x01).to_byte())
1455
- body.push((0x7f).to_byte())
1456
- }
1457
- }
1458
-
1459
- ///|
1460
- fn build_runner_exec_body(
1461
- commands : Array[RunnerCommand],
1462
- refs : Map[String, RunnerLiteralRef],
1463
- ) -> Array[Byte] {
1464
- let (cached_by_key, cached_vars) = build_runner_cached_vars(commands)
1465
- let body = []
1466
- append_runner_exec_locals(body, cached_vars.length())
1467
- if !emit_runner_sync_cached_vars(cached_vars, refs, body, true) ||
1468
- !emit_runner_commands(commands, refs, cached_by_key, cached_vars, body) ||
1469
- !emit_runner_sync_cached_vars(cached_vars, refs, body, false) {
1470
- push_i32_const(body, 0)
1471
- body.push((0x0b).to_byte())
1472
- return body
1473
- }
1474
- push_i32_const(body, commands.length())
1475
- body.push((0x0b).to_byte())
1476
- body
1477
- }
1478
-
1479
- ///|
1480
- fn append_const_i32_function_body(
1481
- code_payload : Array[Byte],
1482
- value : Int,
1483
- ) -> Unit {
1484
- let body = bytes_from_ints([0x00, 0x41])
1485
- append_bytes(body, encode_i32_leb(value))
1486
- body.push((0x0b).to_byte())
1487
- append_bytes(code_payload, encode_u32_leb(body.length()))
1488
- append_bytes(code_payload, body)
1489
- }
1490
-
1491
- ///|
1492
- fn build_program_wasm_binary(
1493
- abi_version : Int,
1494
- project_base64 : String,
1495
- assets_base64 : String,
1496
- commands_base64 : String,
1497
- runner_commands : Array[RunnerCommand],
1498
- ) -> Array[Byte] {
1499
- let project_bytes = encode_utf8_bytes(project_base64)
1500
- let assets_bytes = encode_utf8_bytes(assets_base64)
1501
- let commands_bytes = encode_utf8_bytes(commands_base64)
1502
- let project_len = project_bytes.length()
1503
- let assets_ptr = project_len
1504
- let assets_len = assets_bytes.length()
1505
- let commands_ptr = assets_ptr + assets_len
1506
- let commands_len = commands_bytes.length()
1507
- let runner_literal_pool_ptr = commands_ptr + commands_len
1508
- let (runner_literal_pool_bytes, runner_literal_pool_refs) = build_runner_literal_pool(
1509
- runner_commands, runner_literal_pool_ptr,
1510
- )
1511
- let total_bytes = project_len +
1512
- assets_len +
1513
- commands_len +
1514
- runner_literal_pool_bytes.length()
1515
- let memory_pages = page_count_for_bytes(total_bytes)
1516
- let has_exec_runner = if runner_commands.is_empty() { 0 } else { 1 }
1517
-
1518
- let module_bytes = bytes_from_ints([
1519
- 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
1520
- ])
1521
-
1522
- let type_payload = bytes_from_ints([
1523
- 0x07, 0x60, 0x00, 0x01, 0x7f, 0x60, 0x03, 0x7f, 0x7f, 0x7f, 0x01, 0x7c, 0x60,
1524
- 0x04, 0x7f, 0x7f, 0x7f, 0x7c, 0x00, 0x60, 0x05, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
1525
- 0x00, 0x60, 0x02, 0x7c, 0x7c, 0x01, 0x7c, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f,
1526
- 0x60, 0x06, 0x7f, 0x7f, 0x7f, 0x7c, 0x7c, 0x7f, 0x01, 0x7f,
1527
- ])
1528
- append_bytes(module_bytes, encode_section(1, type_payload))
1529
-
1530
- let import_payload = []
1531
- append_bytes(import_payload, encode_u32_leb(7))
1532
- let imports : Array[(String, String, Int)] = [
1533
- ("env", "ms_get_var_num", 1),
1534
- ("env", "ms_set_var_num", 2),
1535
- ("env", "ms_set_var_json", 3),
1536
- ("env", "ms_mod", 4),
1537
- ("env", "ms_exec_opcode", 5),
1538
- ("env", "ms_exec_tail", 5),
1539
- ("env", "ms_exec_draw_opcode", 6),
1540
- ]
1541
- for item in imports {
1542
- append_bytes(import_payload, encode_name(item.0))
1543
- append_bytes(import_payload, encode_name(item.1))
1544
- import_payload.push((0x00).to_byte())
1545
- append_bytes(import_payload, encode_u32_leb(item.2))
1546
- }
1547
- append_bytes(module_bytes, encode_section(2, import_payload))
1548
-
1549
- let function_payload = []
1550
- append_bytes(function_payload, encode_u32_leb(9))
1551
- append_bytes(function_payload, bytes_from_ints([0, 0, 0, 0, 0, 0, 0, 0, 0]))
1552
- append_bytes(module_bytes, encode_section(3, function_payload))
1553
-
1554
- let memory_payload = [(0x01).to_byte(), (0x00).to_byte()]
1555
- append_bytes(memory_payload, encode_u32_leb(memory_pages))
1556
- append_bytes(module_bytes, encode_section(5, memory_payload))
1557
-
1558
- let exports_payload = []
1559
- let defined_function_base = imports.length()
1560
- let exports : Array[(String, Int, Int)] = [
1561
- ("memory", 0x02, 0),
1562
- ("ms_abi_version", 0x00, defined_function_base + 0),
1563
- ("ms_project_ptr", 0x00, defined_function_base + 1),
1564
- ("ms_project_len", 0x00, defined_function_base + 2),
1565
- ("ms_assets_ptr", 0x00, defined_function_base + 3),
1566
- ("ms_assets_len", 0x00, defined_function_base + 4),
1567
- ("ms_commands_ptr", 0x00, defined_function_base + 5),
1568
- ("ms_commands_len", 0x00, defined_function_base + 6),
1569
- ("ms_has_exec_runner", 0x00, defined_function_base + 7),
1570
- ("ms_exec_green_flag", 0x00, defined_function_base + 8),
1571
- ]
1572
- append_bytes(exports_payload, encode_u32_leb(exports.length()))
1573
- for item in exports {
1574
- append_bytes(exports_payload, encode_name(item.0))
1575
- exports_payload.push(item.1.to_byte())
1576
- append_bytes(exports_payload, encode_u32_leb(item.2))
1577
- }
1578
- append_bytes(module_bytes, encode_section(7, exports_payload))
1579
-
1580
- let code_payload = []
1581
- let function_consts = [
1582
- abi_version, 0, project_len, assets_ptr, assets_len, commands_ptr, commands_len,
1583
- has_exec_runner,
1584
- ]
1585
- append_bytes(code_payload, encode_u32_leb(function_consts.length() + 1))
1586
- for value in function_consts {
1587
- append_const_i32_function_body(code_payload, value)
1588
- }
1589
- let exec_body = build_runner_exec_body(
1590
- runner_commands, runner_literal_pool_refs,
1591
- )
1592
- append_bytes(code_payload, encode_u32_leb(exec_body.length()))
1593
- append_bytes(code_payload, exec_body)
1594
- append_bytes(module_bytes, encode_section(10, code_payload))
1595
-
1596
- let segments : Array[WasmDataSegment] = [
1597
- { offset: 0, bytes: project_bytes },
1598
- { offset: assets_ptr, bytes: assets_bytes },
1599
- { offset: commands_ptr, bytes: commands_bytes },
1600
- ]
1601
- if !runner_literal_pool_bytes.is_empty() {
1602
- segments.push({
1603
- offset: runner_literal_pool_ptr,
1604
- bytes: runner_literal_pool_bytes,
1605
- })
1606
- }
1607
- let data_payload = []
1608
- append_bytes(data_payload, encode_u32_leb(segments.length()))
1609
- for segment in segments {
1610
- data_payload.push((0x00).to_byte())
1611
- data_payload.push((0x41).to_byte())
1612
- append_bytes(data_payload, encode_i32_leb(segment.offset))
1613
- data_payload.push((0x0b).to_byte())
1614
- append_bytes(data_payload, encode_u32_leb(segment.bytes.length()))
1615
- append_bytes(data_payload, segment.bytes)
1616
- }
1617
- append_bytes(module_bytes, encode_section(11, data_payload))
1618
-
1619
- module_bytes
1620
- }
1621
-
1622
- ///|
1623
- pub fn vm_compile_project_to_wasm(
1624
- project_json : String,
1625
- assets_json? : String,
1626
- ) -> String raise VmError {
1627
- let bundle = parse_bundle(project_json, assets_json)
1628
- let (targets, _) = parse_project_targets(bundle.project_json, bundle.assets)
1629
- let project_base64 = base64_utf8(bundle.project_json)
1630
- let assets_raw = json_object(bundle.assets).stringify()
1631
- let assets_base64 = base64_utf8(assets_raw)
1632
- let commands_json = match compile_aot_commands_json(targets) {
1633
- Some(commands) => commands
1634
- None => ""
1635
- }
1636
- let commands_base64 = base64_utf8(commands_json)
1637
- let runner_commands = parse_runner_commands_json(commands_json)
1638
- let wasm_bytes = build_program_wasm_binary(
1639
- PROGRAM_ABI_VERSION,
1640
- project_base64,
1641
- assets_base64,
1642
- commands_base64,
1643
- runner_commands,
1644
- )
1645
- let wasm = Bytes::from_array(wasm_bytes[:])
1646
- @base64.encode(wasm[:])
1647
- }