moonscratch 0.1.0-alpha.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 (151) hide show
  1. package/.agents/skills/moonbit-agent-guide/LICENSE +202 -0
  2. package/.agents/skills/moonbit-agent-guide/SKILL.mbt.md +1126 -0
  3. package/.agents/skills/moonbit-agent-guide/SKILL.md +1126 -0
  4. package/.agents/skills/moonbit-agent-guide/ide.md +116 -0
  5. package/.agents/skills/moonbit-agent-guide/references/advanced-moonbit-build.md +106 -0
  6. package/.agents/skills/moonbit-agent-guide/references/moonbit-language-fundamentals.mbt.md +422 -0
  7. package/.agents/skills/moonbit-agent-guide/references/moonbit-language-fundamentals.md +422 -0
  8. package/.agents/skills/moonbit-practice/SKILL.md +258 -0
  9. package/.agents/skills/moonbit-practice/assets/ci.yaml +25 -0
  10. package/.agents/skills/moonbit-practice/reference/agents.md +1469 -0
  11. package/.agents/skills/moonbit-practice/reference/configuration.md +228 -0
  12. package/.agents/skills/moonbit-practice/reference/ffi.md +229 -0
  13. package/.agents/skills/moonbit-practice/reference/ide.md +189 -0
  14. package/.agents/skills/moonbit-practice/reference/performance.md +217 -0
  15. package/.agents/skills/moonbit-practice/reference/refactor.md +154 -0
  16. package/.agents/skills/moonbit-practice/reference/stdlib.md +351 -0
  17. package/.agents/skills/moonbit-practice/reference/testing.md +228 -0
  18. package/.agents/skills/moonbit-refactoring/LICENSE +21 -0
  19. package/.agents/skills/moonbit-refactoring/SKILL.md +323 -0
  20. package/.githooks/README.md +23 -0
  21. package/.githooks/pre-commit +3 -0
  22. package/.github/workflows/copilot-setup-steps.yml +40 -0
  23. package/.turbo/turbo-typecheck.log +2 -0
  24. package/AGENTS.md +91 -0
  25. package/LICENSE +21 -0
  26. package/PLAN.md +64 -0
  27. package/README.mbt.md +77 -0
  28. package/README.md +84 -0
  29. package/TODO.md +120 -0
  30. package/a.png +0 -0
  31. package/benchmarks/calc.bench.ts +144 -0
  32. package/benchmarks/draw.bench.ts +215 -0
  33. package/benchmarks/load.bench.ts +28 -0
  34. package/benchmarks/render.bench.ts +53 -0
  35. package/benchmarks/run.bench.ts +8 -0
  36. package/benchmarks/types.d.ts +15 -0
  37. package/docs/scratch-vm-specs/eventloop.md +103 -0
  38. package/docs/scratch-vm-specs/moonscratch-time-separation.md +50 -0
  39. package/index.html +91 -0
  40. package/js/AGENTS.md +5 -0
  41. package/js/a.ts +52 -0
  42. package/js/assets/AGENTS.md +5 -0
  43. package/js/assets/base64.test.ts +14 -0
  44. package/js/assets/base64.ts +21 -0
  45. package/js/assets/build-asset.test.ts +26 -0
  46. package/js/assets/build-asset.ts +28 -0
  47. package/js/assets/create.test.ts +142 -0
  48. package/js/assets/create.ts +122 -0
  49. package/js/assets/index.test.ts +15 -0
  50. package/js/assets/index.ts +2 -0
  51. package/js/assets/types.ts +26 -0
  52. package/js/assets/validation.test.ts +34 -0
  53. package/js/assets/validation.ts +25 -0
  54. package/js/assets.test.ts +14 -0
  55. package/js/assets.ts +1 -0
  56. package/js/index.test.ts +26 -0
  57. package/js/index.ts +3 -0
  58. package/js/render/index.test.ts +65 -0
  59. package/js/render/index.ts +13 -0
  60. package/js/render/sharp.ts +87 -0
  61. package/js/render/svg.ts +68 -0
  62. package/js/render/types.ts +35 -0
  63. package/js/render/utils.ts +108 -0
  64. package/js/render/webgl.ts +274 -0
  65. package/js/sharp-optional.d.ts +16 -0
  66. package/js/test/helpers.ts +116 -0
  67. package/js/test/hikkaku-sample.test.ts +37 -0
  68. package/js/test/rubik-components.input-motion.test.ts +60 -0
  69. package/js/test/rubik-components.lists.test.ts +49 -0
  70. package/js/test/rubik-components.operators.test.ts +104 -0
  71. package/js/test/rubik-components.pen.test.ts +112 -0
  72. package/js/test/rubik-components.procedures-loops.test.ts +72 -0
  73. package/js/test/rubik-components.variables-branches.test.ts +57 -0
  74. package/js/test/rubik-components.visibility-entry.test.ts +31 -0
  75. package/js/test/test-projects.ts +598 -0
  76. package/js/test/variable.ts +200 -0
  77. package/js/test/warp.test.ts +59 -0
  78. package/js/vm/AGENTS.md +6 -0
  79. package/js/vm/README.md +183 -0
  80. package/js/vm/bindings.test.ts +13 -0
  81. package/js/vm/bindings.ts +5 -0
  82. package/js/vm/compare-operators.test.ts +145 -0
  83. package/js/vm/constants.test.ts +11 -0
  84. package/js/vm/constants.ts +4 -0
  85. package/js/vm/effect-guards.test.ts +68 -0
  86. package/js/vm/effect-guards.ts +44 -0
  87. package/js/vm/factory.test.ts +486 -0
  88. package/js/vm/factory.ts +615 -0
  89. package/js/vm/headless-vm.test.ts +131 -0
  90. package/js/vm/headless-vm.ts +342 -0
  91. package/js/vm/index.test.ts +28 -0
  92. package/js/vm/index.ts +5 -0
  93. package/js/vm/internal-types.ts +32 -0
  94. package/js/vm/json.test.ts +40 -0
  95. package/js/vm/json.ts +273 -0
  96. package/js/vm/normalize.test.ts +48 -0
  97. package/js/vm/normalize.ts +65 -0
  98. package/js/vm/options.test.ts +30 -0
  99. package/js/vm/options.ts +55 -0
  100. package/js/vm/pen-transparency.test.ts +115 -0
  101. package/js/vm/program-wasm.ts +322 -0
  102. package/js/vm/scheduler-render.test.ts +401 -0
  103. package/js/vm/scratch-assets.test.ts +136 -0
  104. package/js/vm/scratch-assets.ts +202 -0
  105. package/js/vm/types.ts +358 -0
  106. package/js/vm/value-guards.test.ts +25 -0
  107. package/js/vm/value-guards.ts +18 -0
  108. package/moon.mod.json +10 -0
  109. package/package.json +33 -0
  110. package/scripts/preinstall.ts +4 -0
  111. package/src/AGENTS.md +6 -0
  112. package/src/api.mbt +161 -0
  113. package/src/api_aot_commands.mbt +184 -0
  114. package/src/api_effects_json.mbt +72 -0
  115. package/src/api_options.mbt +60 -0
  116. package/src/api_program_wasm.mbt +1647 -0
  117. package/src/api_program_wat.mbt +2206 -0
  118. package/src/api_snapshot_json.mbt +44 -0
  119. package/src/cmd/AGENTS.md +5 -0
  120. package/src/cmd/main/AGENTS.md +5 -0
  121. package/src/cmd/main/main.mbt +29 -0
  122. package/src/cmd/main/moon.pkg +7 -0
  123. package/src/cmd/main/pkg.generated.mbti +13 -0
  124. package/src/json_helpers.mbt +176 -0
  125. package/src/moon.pkg +65 -0
  126. package/src/moonscratch.mbt +3 -0
  127. package/src/moonscratch_wbtest.mbt +40 -0
  128. package/src/parser_sb3.mbt +890 -0
  129. package/src/pkg.generated.mbti +479 -0
  130. package/src/runtime_eval.mbt +2844 -0
  131. package/src/runtime_exec.mbt +3850 -0
  132. package/src/runtime_render.mbt +2550 -0
  133. package/src/runtime_state.mbt +870 -0
  134. package/src/test/AGENTS.md +3 -0
  135. package/src/test/projects/AGENTS.md +6 -0
  136. package/src/test/projects/moon.pkg +4 -0
  137. package/src/test/projects/moonscratch_compat_test.mbt +642 -0
  138. package/src/test/projects/moonscratch_core_test.mbt +1332 -0
  139. package/src/test/projects/moonscratch_runtime_test.mbt +1087 -0
  140. package/src/test/projects/pkg.generated.mbti +13 -0
  141. package/src/test/projects/test_support.mbt +35 -0
  142. package/src/types_effects.mbt +20 -0
  143. package/src/types_error.mbt +4 -0
  144. package/src/types_options.mbt +31 -0
  145. package/src/types_runtime_structs.mbt +254 -0
  146. package/src/types_vm.mbt +109 -0
  147. package/tsconfig.json +29 -0
  148. package/viewer/index.ts +399 -0
  149. package/viewer/vite.d.ts +1 -0
  150. package/viewer/worker.ts +161 -0
  151. package/vite.config.ts +11 -0
@@ -0,0 +1,2206 @@
1
+ ///|
2
+ const PROGRAM_ABI_VERSION : Int = 2
3
+
4
+ ///|
5
+ fn page_count_for_bytes(total_bytes : Int) -> Int {
6
+ if total_bytes <= 0 {
7
+ 1
8
+ } else {
9
+ (total_bytes + 65535) / 65536
10
+ }
11
+ }
12
+
13
+ ///|
14
+ fn base64_utf8(raw : String) -> String {
15
+ let encoded = @utf8.encode(raw.view())
16
+ @base64.encode(encoded[:])
17
+ }
18
+
19
+ ///|
20
+ fn json_number_or_null(value : Int?) -> Json {
21
+ match value {
22
+ Some(v) => json_number(Double::from_int(v))
23
+ None => Json::null()
24
+ }
25
+ }
26
+
27
+ ///|
28
+ fn command_json_opcode_catalog(
29
+ target_index : Int,
30
+ pc : Int,
31
+ block : ScratchBlock,
32
+ meta : BlockFastMeta,
33
+ ) -> Json {
34
+ json_object({
35
+ "op": json_string("opcode"),
36
+ "target": json_number(Double::from_int(target_index)),
37
+ "pc": json_number(Double::from_int(pc)),
38
+ "id": json_string(block.id),
39
+ "opcode": json_string(block.opcode),
40
+ "top_level": json_bool(block.top_level),
41
+ "next_pc": json_number_or_null(meta.next_pc),
42
+ "substack_pc": json_number_or_null(meta.substack_pc),
43
+ "substack2_pc": json_number_or_null(meta.substack2_pc),
44
+ })
45
+ }
46
+
47
+ ///|
48
+ fn command_json_full_green_flag_start_entry(
49
+ target_index : Int,
50
+ start_pcs : Array[Int],
51
+ ) -> Json {
52
+ let pcs = start_pcs.map(fn(pc) { json_number(Double::from_int(pc)) })
53
+ json_object({
54
+ "target": json_number(Double::from_int(target_index)),
55
+ "pcs": json_array(pcs),
56
+ })
57
+ }
58
+
59
+ ///|
60
+ fn target_is_aot_eligible(target : TargetState) -> Bool {
61
+ target.top_level_hats.length() == target.green_flag_starts.length() &&
62
+ target.stage_clicked_starts.is_empty() &&
63
+ target.sprite_clicked_starts.is_empty() &&
64
+ target.clone_start_starts.is_empty() &&
65
+ target.key_pressed_hats.is_empty() &&
66
+ target.broadcast_hats.is_empty() &&
67
+ target.backdrop_hats.is_empty() &&
68
+ target.predicate_hats.is_empty()
69
+ }
70
+
71
+ ///|
72
+ priv enum AotCompileStmtResult {
73
+ Noop
74
+ Emit(Json)
75
+ EmitAndStop(Json)
76
+ }
77
+
78
+ ///|
79
+ fn expr_json_num(value : Double) -> Json {
80
+ json_object({ "kind": json_string("num"), "value": json_number(value) })
81
+ }
82
+
83
+ ///|
84
+ fn expr_json_var_num(target_index : Int, variable_id : String) -> Json {
85
+ json_object({
86
+ "kind": json_string("var_num"),
87
+ "target": json_number(Double::from_int(target_index)),
88
+ "id": json_string(variable_id),
89
+ })
90
+ }
91
+
92
+ ///|
93
+ fn expr_json_bin_num(op : String, left : Json, right : Json) -> Json {
94
+ json_object({
95
+ "kind": json_string("bin_num"),
96
+ "op": json_string(op),
97
+ "left": left,
98
+ "right": right,
99
+ })
100
+ }
101
+
102
+ ///|
103
+ fn expr_json_unary_num(op : String, value : Json) -> Json {
104
+ json_object({
105
+ "kind": json_string("unary_num"),
106
+ "op": json_string(op),
107
+ "value": value,
108
+ })
109
+ }
110
+
111
+ ///|
112
+ fn expr_json_bool(value : Bool) -> Json {
113
+ json_object({ "kind": json_string("bool"), "value": json_bool(value) })
114
+ }
115
+
116
+ ///|
117
+ fn expr_json_cmp_num(op : String, left : Json, right : Json) -> Json {
118
+ json_object({
119
+ "kind": json_string("cmp_num"),
120
+ "op": json_string(op),
121
+ "left": left,
122
+ "right": right,
123
+ })
124
+ }
125
+
126
+ ///|
127
+ fn expr_json_bin_bool(op : String, left : Json, right : Json) -> Json {
128
+ json_object({
129
+ "kind": json_string("bin_bool"),
130
+ "op": json_string(op),
131
+ "left": left,
132
+ "right": right,
133
+ })
134
+ }
135
+
136
+ ///|
137
+ fn expr_json_not(value : Json) -> Json {
138
+ json_object({ "kind": json_string("not"), "value": value })
139
+ }
140
+
141
+ ///|
142
+ fn expr_json_bool_from_number(expr : Json) -> Json {
143
+ expr_json_cmp_num("ne", expr, expr_json_num(0.0))
144
+ }
145
+
146
+ ///|
147
+ fn command_json_set_var_num_expr(
148
+ target_index : Int,
149
+ variable_id : String,
150
+ expr : Json,
151
+ ) -> Json {
152
+ json_object({
153
+ "op": json_string("set_var_num_expr"),
154
+ "target": json_number(Double::from_int(target_index)),
155
+ "id": json_string(variable_id),
156
+ "expr": expr,
157
+ })
158
+ }
159
+
160
+ ///|
161
+ fn command_json_set_var_json_const(
162
+ target_index : Int,
163
+ variable_id : String,
164
+ value : Json,
165
+ ) -> Json {
166
+ json_object({
167
+ "op": json_string("set_var_json_const"),
168
+ "target": json_number(Double::from_int(target_index)),
169
+ "id": json_string(variable_id),
170
+ "value": value,
171
+ })
172
+ }
173
+
174
+ ///|
175
+ fn command_json_change_var_num_expr(
176
+ target_index : Int,
177
+ variable_id : String,
178
+ expr : Json,
179
+ ) -> Json {
180
+ json_object({
181
+ "op": json_string("change_var_num_expr"),
182
+ "target": json_number(Double::from_int(target_index)),
183
+ "id": json_string(variable_id),
184
+ "expr": expr,
185
+ })
186
+ }
187
+
188
+ ///|
189
+ fn command_json_if(cond : Json, then_commands : Array[Json]) -> Json {
190
+ json_object({
191
+ "op": json_string("if"),
192
+ "cond": cond,
193
+ "then": json_array(then_commands),
194
+ })
195
+ }
196
+
197
+ ///|
198
+ fn command_json_if_else(
199
+ cond : Json,
200
+ then_commands : Array[Json],
201
+ else_commands : Array[Json],
202
+ ) -> Json {
203
+ json_object({
204
+ "op": json_string("if_else"),
205
+ "cond": cond,
206
+ "then": json_array(then_commands),
207
+ "else": json_array(else_commands),
208
+ })
209
+ }
210
+
211
+ ///|
212
+ fn command_json_repeat(times : Json, body_commands : Array[Json]) -> Json {
213
+ json_object({
214
+ "op": json_string("repeat"),
215
+ "times": times,
216
+ "body": json_array(body_commands),
217
+ })
218
+ }
219
+
220
+ ///|
221
+ fn command_json_repeat_until(cond : Json, body_commands : Array[Json]) -> Json {
222
+ json_object({
223
+ "op": json_string("repeat_until"),
224
+ "cond": cond,
225
+ "body": json_array(body_commands),
226
+ })
227
+ }
228
+
229
+ ///|
230
+ fn command_json_while(cond : Json, body_commands : Array[Json]) -> Json {
231
+ json_object({
232
+ "op": json_string("while"),
233
+ "cond": cond,
234
+ "body": json_array(body_commands),
235
+ })
236
+ }
237
+
238
+ ///|
239
+ fn command_json_host_tail(target_index : Int, start_pc : Int) -> Json {
240
+ json_object({
241
+ "op": json_string("host_tail"),
242
+ "target": json_number(Double::from_int(target_index)),
243
+ "pc": json_number(Double::from_int(start_pc)),
244
+ })
245
+ }
246
+
247
+ ///|
248
+ fn command_json_host_opcode(target_index : Int, pc : Int) -> Json {
249
+ json_object({
250
+ "op": json_string("host_opcode"),
251
+ "target": json_number(Double::from_int(target_index)),
252
+ "pc": json_number(Double::from_int(pc)),
253
+ })
254
+ }
255
+
256
+ ///|
257
+ fn command_json_draw_opcode(
258
+ target_index : Int,
259
+ opcode : String,
260
+ arg0 : Json,
261
+ arg1 : Json,
262
+ extra : Int,
263
+ ) -> Json {
264
+ json_object({
265
+ "op": json_string("draw_opcode"),
266
+ "target": json_number(Double::from_int(target_index)),
267
+ "opcode": json_string(opcode),
268
+ "arg0": arg0,
269
+ "arg1": arg1,
270
+ "extra": json_number(Double::from_int(extra)),
271
+ })
272
+ }
273
+
274
+ ///|
275
+ fn normalize_looks_effect_name_for_draw(raw : String) -> Int? {
276
+ match raw.trim().to_lower() {
277
+ "color" => Some(1)
278
+ "fisheye" => Some(2)
279
+ "whirl" => Some(3)
280
+ "pixelate" => Some(4)
281
+ "mosaic" => Some(5)
282
+ "brightness" => Some(6)
283
+ "ghost" => Some(7)
284
+ _ => None
285
+ }
286
+ }
287
+
288
+ ///|
289
+ fn normalize_pen_color_param_for_draw(raw : String) -> Int? {
290
+ match raw.trim().to_lower() {
291
+ "color" => Some(1)
292
+ "saturation" => Some(2)
293
+ "brightness" => Some(3)
294
+ "transparency" => Some(4)
295
+ _ => None
296
+ }
297
+ }
298
+
299
+ ///|
300
+ fn literal_string_from_input_or_field(
301
+ block : ScratchBlock,
302
+ input_name : String,
303
+ field_name : String,
304
+ ) -> String? {
305
+ let from_input = match block.input_block_ids.get(input_name) {
306
+ Some(_) => None
307
+ None =>
308
+ match block.inputs.get(input_name) {
309
+ Some(raw_input) =>
310
+ match input_payload(raw_input) {
311
+ Some(payload) =>
312
+ match literal_value_from_payload(payload) {
313
+ Some(value) => {
314
+ let text = json_to_string_value(value).trim().to_string()
315
+ if text == "" {
316
+ None
317
+ } else {
318
+ Some(text)
319
+ }
320
+ }
321
+ None => None
322
+ }
323
+ None => None
324
+ }
325
+ None => None
326
+ }
327
+ }
328
+ match from_input {
329
+ Some(value) => Some(value)
330
+ None =>
331
+ match field_value(block, field_name) {
332
+ Some((raw, _)) => {
333
+ let text = raw.trim().to_string()
334
+ if text == "" {
335
+ None
336
+ } else {
337
+ Some(text)
338
+ }
339
+ }
340
+ None => None
341
+ }
342
+ }
343
+ }
344
+
345
+ ///|
346
+ fn command_is_host_tail(command : Json) -> Bool {
347
+ match command {
348
+ Object(obj) =>
349
+ match obj.get("op") {
350
+ Some(String("host_tail")) => true
351
+ Some(String("if")) =>
352
+ match obj.get("then") {
353
+ Some(Array(children)) => command_list_contains_host_tail(children)
354
+ _ => false
355
+ }
356
+ Some(String("if_else")) =>
357
+ match (obj.get("then"), obj.get("else")) {
358
+ (Some(Array(then_children)), Some(Array(else_children))) =>
359
+ command_list_contains_host_tail(then_children) ||
360
+ command_list_contains_host_tail(else_children)
361
+ _ => false
362
+ }
363
+ Some(String("repeat"))
364
+ | Some(String("repeat_until"))
365
+ | Some(String("while")) =>
366
+ match obj.get("body") {
367
+ Some(Array(children)) => command_list_contains_host_tail(children)
368
+ _ => false
369
+ }
370
+ _ => false
371
+ }
372
+ _ => false
373
+ }
374
+ }
375
+
376
+ ///|
377
+ fn command_list_contains_host_tail(commands : Array[Json]) -> Bool {
378
+ for command in commands {
379
+ if command_is_host_tail(command) {
380
+ return true
381
+ }
382
+ }
383
+ false
384
+ }
385
+
386
+ ///|
387
+ fn opcode_uses_safe_host_opcode(opcode : String) -> Bool {
388
+ match opcode {
389
+ "music_setInstrument"
390
+ | "music_midiSetInstrument"
391
+ | "music_setTempo"
392
+ | "music_changeTempo"
393
+ | "text2speech_setVoice"
394
+ | "text2speech_setLanguage"
395
+ | "sound_play"
396
+ | "sound_playuntildone"
397
+ | "sound_stopallsounds"
398
+ | "sound_setvolumeto"
399
+ | "sound_changevolumeby"
400
+ | "sound_seteffectto"
401
+ | "sound_changeeffectby"
402
+ | "sound_cleareffects"
403
+ | "data_setvariableto"
404
+ | "data_changevariableby"
405
+ | "data_addtolist"
406
+ | "data_deleteoflist"
407
+ | "data_deletealloflist"
408
+ | "data_insertatlist"
409
+ | "data_replaceitemoflist"
410
+ | "data_showvariable"
411
+ | "data_hidevariable"
412
+ | "data_showlist"
413
+ | "data_hidelist"
414
+ | "event_broadcast"
415
+ | "sensing_resettimer"
416
+ | "sensing_setdragmode"
417
+ | "control_clear_counter"
418
+ | "control_incr_counter"
419
+ | "motion_movesteps"
420
+ | "motion_turnright"
421
+ | "motion_turnleft"
422
+ | "motion_pointindirection"
423
+ | "motion_pointtowards"
424
+ | "motion_changexby"
425
+ | "motion_changeyby"
426
+ | "motion_setx"
427
+ | "motion_sety"
428
+ | "motion_gotoxy"
429
+ | "motion_goto"
430
+ | "motion_ifonedgebounce"
431
+ | "motion_setrotationstyle"
432
+ | "motion_align_scene"
433
+ | "motion_scroll_right"
434
+ | "motion_scroll_up"
435
+ | "looks_show"
436
+ | "looks_hide"
437
+ | "looks_hideallsprites"
438
+ | "looks_switchcostumeto"
439
+ | "looks_nextcostume"
440
+ | "looks_switchbackdropto"
441
+ | "looks_nextbackdrop"
442
+ | "looks_changeeffectby"
443
+ | "looks_seteffectto"
444
+ | "looks_cleargraphiceffects"
445
+ | "looks_changestretchby"
446
+ | "looks_setstretchto"
447
+ | "looks_gotofrontback"
448
+ | "looks_goforwardbackwardlayers"
449
+ | "looks_changesizeby"
450
+ | "looks_setsizeto"
451
+ | "looks_say"
452
+ | "looks_think"
453
+ | "pen_clear"
454
+ | "pen_stamp"
455
+ | "pen_penDown"
456
+ | "pen_penUp"
457
+ | "pen_setPenColorToColor"
458
+ | "pen_changePenColorParamBy"
459
+ | "pen_setPenColorParamTo"
460
+ | "pen_changePenSizeBy"
461
+ | "pen_setPenSizeTo"
462
+ | "pen_setPenHueToNumber"
463
+ | "pen_changePenHueBy"
464
+ | "pen_setPenShadeToNumber"
465
+ | "pen_changePenShadeBy" => true
466
+ _ => false
467
+ }
468
+ }
469
+
470
+ ///|
471
+ fn opcode_fallback_stmt(
472
+ opcode : String,
473
+ target_index : Int,
474
+ pc : Int,
475
+ ) -> AotCompileStmtResult {
476
+ if opcode_uses_safe_host_opcode(opcode) {
477
+ AotCompileStmtResult::Emit(command_json_host_opcode(target_index, pc))
478
+ } else {
479
+ AotCompileStmtResult::EmitAndStop(command_json_host_tail(target_index, pc))
480
+ }
481
+ }
482
+
483
+ ///|
484
+ fn variable_id_from_field(block : ScratchBlock) -> String? {
485
+ match field_value(block, "VARIABLE") {
486
+ Some((_, Some(id))) if id != "" => Some(id)
487
+ _ => None
488
+ }
489
+ }
490
+
491
+ ///|
492
+ fn literal_value_from_payload(payload : Json) -> Json? {
493
+ match payload {
494
+ Array(primitive) =>
495
+ if primitive.length() >= 2 {
496
+ let kind = json_to_number_value(primitive[0]).to_int()
497
+ if kind >= 4 && kind <= 8 {
498
+ Some(json_number(json_to_number_value(primitive[1])))
499
+ } else if kind == 10 || kind == 11 {
500
+ Some(json_string(json_to_string_value(primitive[1])))
501
+ } else if kind == 12 || kind == 13 {
502
+ None
503
+ } else {
504
+ match primitive[1] {
505
+ String(_) | Number(_, ..) | True | False | Null =>
506
+ Some(primitive[1])
507
+ _ => None
508
+ }
509
+ }
510
+ } else {
511
+ None
512
+ }
513
+ String(_) | Number(_, ..) | True | False | Null => Some(payload)
514
+ _ => None
515
+ }
516
+ }
517
+
518
+ ///|
519
+ fn literal_value_from_input(block : ScratchBlock, input_name : String) -> Json? {
520
+ match block.input_block_ids.get(input_name) {
521
+ Some(_) => None
522
+ None =>
523
+ match block.inputs.get(input_name) {
524
+ Some(raw_input) =>
525
+ match input_payload(raw_input) {
526
+ Some(payload) => literal_value_from_payload(payload)
527
+ None => None
528
+ }
529
+ None => None
530
+ }
531
+ }
532
+ }
533
+
534
+ ///|
535
+ fn json_finite_number_or_none(value : Json) -> Double? {
536
+ match value {
537
+ Number(n, ..) => if n.is_nan() || n.is_inf() { None } else { Some(n) }
538
+ _ => None
539
+ }
540
+ }
541
+
542
+ ///|
543
+ fn resolve_reporter_block_id(
544
+ block : ScratchBlock,
545
+ input_name : String,
546
+ ) -> String? {
547
+ match block.input_block_ids.get(input_name) {
548
+ Some(id) => Some(id)
549
+ None =>
550
+ match block.inputs.get(input_name) {
551
+ Some(raw_input) =>
552
+ match input_payload(raw_input) {
553
+ Some(String(id)) => Some(id)
554
+ _ => None
555
+ }
556
+ None => None
557
+ }
558
+ }
559
+ }
560
+
561
+ ///|
562
+ fn variable_id_from_primitive(primitive : Array[Json]) -> String? {
563
+ if primitive.length() >= 3 {
564
+ let id = json_to_string_value(primitive[2])
565
+ if id != "" {
566
+ return Some(id)
567
+ }
568
+ }
569
+ None
570
+ }
571
+
572
+ ///|
573
+ fn json_is_numeric_comparable_for_aot(value : Json) -> Bool {
574
+ match value {
575
+ Number(n, ..) => n == n
576
+ String(s) =>
577
+ match parse_double_or_none(s) {
578
+ Some(number) => number == number
579
+ None => false
580
+ }
581
+ True => true
582
+ False => true
583
+ _ => false
584
+ }
585
+ }
586
+
587
+ ///|
588
+ fn build_aot_variable_numeric_map(
589
+ targets : Array[TargetState],
590
+ ) -> Map[String, Bool] {
591
+ let out = {}
592
+ for target in targets {
593
+ target.variables.each((variable_id, value) => {
594
+ out[variable_id] = json_is_numeric_comparable_for_aot(value)
595
+ })
596
+ }
597
+ out
598
+ }
599
+
600
+ ///|
601
+ fn number_expr_uses_only_numeric_variables(
602
+ expr : Json,
603
+ variable_numeric_by_id : Map[String, Bool],
604
+ depth : Int,
605
+ ) -> Bool {
606
+ if depth > 64 {
607
+ return false
608
+ }
609
+ match expr {
610
+ Object(obj) =>
611
+ match obj.get("kind") {
612
+ Some(String("num")) => true
613
+ Some(String("var_num")) =>
614
+ match obj.get("id") {
615
+ Some(String(variable_id)) =>
616
+ match variable_numeric_by_id.get(variable_id) {
617
+ Some(is_numeric) => is_numeric
618
+ None => false
619
+ }
620
+ _ => false
621
+ }
622
+ Some(String("bin_num")) =>
623
+ match (obj.get("left"), obj.get("right")) {
624
+ (Some(left), Some(right)) =>
625
+ number_expr_uses_only_numeric_variables(
626
+ left,
627
+ variable_numeric_by_id,
628
+ depth + 1,
629
+ ) &&
630
+ number_expr_uses_only_numeric_variables(
631
+ right,
632
+ variable_numeric_by_id,
633
+ depth + 1,
634
+ )
635
+ _ => false
636
+ }
637
+ Some(String("unary_num")) =>
638
+ match obj.get("value") {
639
+ Some(value) =>
640
+ number_expr_uses_only_numeric_variables(
641
+ value,
642
+ variable_numeric_by_id,
643
+ depth + 1,
644
+ )
645
+ None => false
646
+ }
647
+ _ => false
648
+ }
649
+ _ => false
650
+ }
651
+ }
652
+
653
+ ///|
654
+ fn compile_number_expr_from_payload(
655
+ target_index : Int,
656
+ payload : Json,
657
+ allow_variable : Bool,
658
+ ) -> Json? {
659
+ match payload {
660
+ Array(primitive) =>
661
+ if primitive.length() >= 2 {
662
+ let kind = json_to_number_value(primitive[0]).to_int()
663
+ if kind >= 4 && kind <= 8 {
664
+ Some(expr_json_num(json_to_number_value(primitive[1])))
665
+ } else if kind == 10 || kind == 11 {
666
+ None
667
+ } else if kind == 12 {
668
+ if !allow_variable {
669
+ None
670
+ } else {
671
+ match variable_id_from_primitive(primitive) {
672
+ Some(id) => Some(expr_json_var_num(target_index, id))
673
+ None => None
674
+ }
675
+ }
676
+ } else {
677
+ match primitive[1] {
678
+ String(_) => None
679
+ _ => Some(expr_json_num(json_to_number_value(primitive[1])))
680
+ }
681
+ }
682
+ } else {
683
+ None
684
+ }
685
+ String(_) => None
686
+ _ => Some(expr_json_num(json_to_number_value(payload)))
687
+ }
688
+ }
689
+
690
+ ///|
691
+ fn compile_bool_expr_from_payload(target_index : Int, payload : Json) -> Json? {
692
+ match payload {
693
+ Array(primitive) =>
694
+ if primitive.length() >= 2 {
695
+ let kind = json_to_number_value(primitive[0]).to_int()
696
+ if kind == 12 {
697
+ match variable_id_from_primitive(primitive) {
698
+ Some(id) =>
699
+ Some(
700
+ expr_json_bool_from_number(expr_json_var_num(target_index, id)),
701
+ )
702
+ None => None
703
+ }
704
+ } else {
705
+ Some(expr_json_bool(json_to_bool_value(primitive[1])))
706
+ }
707
+ } else {
708
+ None
709
+ }
710
+ _ => Some(expr_json_bool(json_to_bool_value(payload)))
711
+ }
712
+ }
713
+
714
+ ///|
715
+ fn compile_number_expr_from_block(
716
+ target : TargetState,
717
+ target_index : Int,
718
+ block_id : String,
719
+ depth : Int,
720
+ allow_variable : Bool,
721
+ ) -> Json? {
722
+ if depth > 64 {
723
+ return None
724
+ }
725
+ let block = match target.blocks.get(block_id) {
726
+ Some(value) => value
727
+ None => return None
728
+ }
729
+ match block.opcode {
730
+ "math_number"
731
+ | "math_integer"
732
+ | "math_whole_number"
733
+ | "math_positive_number"
734
+ | "math_angle" =>
735
+ match field_value(block, "NUM") {
736
+ Some((raw, _)) =>
737
+ Some(
738
+ expr_json_num(
739
+ match parse_double_or_none(raw) {
740
+ Some(value) => value
741
+ None => 0.0
742
+ },
743
+ ),
744
+ )
745
+ None => Some(expr_json_num(0.0))
746
+ }
747
+ "data_variable" =>
748
+ if !allow_variable {
749
+ None
750
+ } else {
751
+ match field_value(block, "VARIABLE") {
752
+ Some((_, Some(id))) if id != "" =>
753
+ Some(expr_json_var_num(target_index, id))
754
+ _ => None
755
+ }
756
+ }
757
+ "operator_add" =>
758
+ match
759
+ (
760
+ compile_number_expr_from_input(
761
+ target,
762
+ target_index,
763
+ block,
764
+ "NUM1",
765
+ depth + 1,
766
+ allow_variable,
767
+ ),
768
+ compile_number_expr_from_input(
769
+ target,
770
+ target_index,
771
+ block,
772
+ "NUM2",
773
+ depth + 1,
774
+ allow_variable,
775
+ ),
776
+ ) {
777
+ (Some(left), Some(right)) => Some(expr_json_bin_num("add", left, right))
778
+ _ => None
779
+ }
780
+ "operator_subtract" =>
781
+ match
782
+ (
783
+ compile_number_expr_from_input(
784
+ target,
785
+ target_index,
786
+ block,
787
+ "NUM1",
788
+ depth + 1,
789
+ allow_variable,
790
+ ),
791
+ compile_number_expr_from_input(
792
+ target,
793
+ target_index,
794
+ block,
795
+ "NUM2",
796
+ depth + 1,
797
+ allow_variable,
798
+ ),
799
+ ) {
800
+ (Some(left), Some(right)) => Some(expr_json_bin_num("sub", left, right))
801
+ _ => None
802
+ }
803
+ "operator_multiply" =>
804
+ match
805
+ (
806
+ compile_number_expr_from_input(
807
+ target,
808
+ target_index,
809
+ block,
810
+ "NUM1",
811
+ depth + 1,
812
+ allow_variable,
813
+ ),
814
+ compile_number_expr_from_input(
815
+ target,
816
+ target_index,
817
+ block,
818
+ "NUM2",
819
+ depth + 1,
820
+ allow_variable,
821
+ ),
822
+ ) {
823
+ (Some(left), Some(right)) => Some(expr_json_bin_num("mul", left, right))
824
+ _ => None
825
+ }
826
+ "operator_divide" =>
827
+ match
828
+ (
829
+ compile_number_expr_from_input(
830
+ target,
831
+ target_index,
832
+ block,
833
+ "NUM1",
834
+ depth + 1,
835
+ allow_variable,
836
+ ),
837
+ compile_number_expr_from_input(
838
+ target,
839
+ target_index,
840
+ block,
841
+ "NUM2",
842
+ depth + 1,
843
+ allow_variable,
844
+ ),
845
+ ) {
846
+ (Some(left), Some(right)) => Some(expr_json_bin_num("div", left, right))
847
+ _ => None
848
+ }
849
+ "operator_mod" =>
850
+ match
851
+ (
852
+ compile_number_expr_from_input(
853
+ target,
854
+ target_index,
855
+ block,
856
+ "NUM1",
857
+ depth + 1,
858
+ allow_variable,
859
+ ),
860
+ compile_number_expr_from_input(
861
+ target,
862
+ target_index,
863
+ block,
864
+ "NUM2",
865
+ depth + 1,
866
+ allow_variable,
867
+ ),
868
+ ) {
869
+ (Some(left), Some(right)) => Some(expr_json_bin_num("mod", left, right))
870
+ _ => None
871
+ }
872
+ "operator_round" =>
873
+ match
874
+ compile_number_expr_from_input(
875
+ target,
876
+ target_index,
877
+ block,
878
+ "NUM",
879
+ depth + 1,
880
+ allow_variable,
881
+ ) {
882
+ Some(value) => Some(expr_json_unary_num("round", value))
883
+ None => None
884
+ }
885
+ "operator_mathop" =>
886
+ match
887
+ (
888
+ field_value(block, "OPERATOR"),
889
+ compile_number_expr_from_input(
890
+ target,
891
+ target_index,
892
+ block,
893
+ "NUM",
894
+ depth + 1,
895
+ allow_variable,
896
+ ),
897
+ ) {
898
+ (Some((raw_op, _)), Some(value)) =>
899
+ match raw_op.trim().to_lower() {
900
+ "abs" => Some(expr_json_unary_num("abs", value))
901
+ "floor" => Some(expr_json_unary_num("floor", value))
902
+ "ceiling" => Some(expr_json_unary_num("ceil", value))
903
+ "sqrt" => Some(expr_json_unary_num("sqrt", value))
904
+ "round" => Some(expr_json_unary_num("round", value))
905
+ _ => None
906
+ }
907
+ _ => None
908
+ }
909
+ _ => None
910
+ }
911
+ }
912
+
913
+ ///|
914
+ fn compile_number_expr_from_input(
915
+ target : TargetState,
916
+ target_index : Int,
917
+ block : ScratchBlock,
918
+ input_name : String,
919
+ depth : Int,
920
+ allow_variable : Bool,
921
+ ) -> Json? {
922
+ if depth > 64 {
923
+ return None
924
+ }
925
+ match resolve_reporter_block_id(block, input_name) {
926
+ Some(block_id) =>
927
+ compile_number_expr_from_block(
928
+ target,
929
+ target_index,
930
+ block_id,
931
+ depth + 1,
932
+ allow_variable,
933
+ )
934
+ None =>
935
+ match block.inputs.get(input_name) {
936
+ Some(raw_input) =>
937
+ match input_payload(raw_input) {
938
+ Some(payload) =>
939
+ compile_number_expr_from_payload(
940
+ target_index, payload, allow_variable,
941
+ )
942
+ None => Some(expr_json_num(0.0))
943
+ }
944
+ None => Some(expr_json_num(0.0))
945
+ }
946
+ }
947
+ }
948
+
949
+ ///|
950
+ fn compile_bool_expr_from_block(
951
+ target : TargetState,
952
+ target_index : Int,
953
+ block_id : String,
954
+ depth : Int,
955
+ variable_numeric_by_id : Map[String, Bool],
956
+ ) -> Json? {
957
+ if depth > 64 {
958
+ return None
959
+ }
960
+ let block = match target.blocks.get(block_id) {
961
+ Some(value) => value
962
+ None => return None
963
+ }
964
+ match block.opcode {
965
+ "operator_lt" =>
966
+ match
967
+ (
968
+ compile_number_expr_from_input(
969
+ target,
970
+ target_index,
971
+ block,
972
+ "OPERAND1",
973
+ depth + 1,
974
+ true,
975
+ ),
976
+ compile_number_expr_from_input(
977
+ target,
978
+ target_index,
979
+ block,
980
+ "OPERAND2",
981
+ depth + 1,
982
+ true,
983
+ ),
984
+ ) {
985
+ (Some(left), Some(right)) =>
986
+ if number_expr_uses_only_numeric_variables(
987
+ left, variable_numeric_by_id, 0,
988
+ ) &&
989
+ number_expr_uses_only_numeric_variables(
990
+ right, variable_numeric_by_id, 0,
991
+ ) {
992
+ Some(expr_json_cmp_num("lt", left, right))
993
+ } else {
994
+ None
995
+ }
996
+ _ => None
997
+ }
998
+ "operator_gt" =>
999
+ match
1000
+ (
1001
+ compile_number_expr_from_input(
1002
+ target,
1003
+ target_index,
1004
+ block,
1005
+ "OPERAND1",
1006
+ depth + 1,
1007
+ true,
1008
+ ),
1009
+ compile_number_expr_from_input(
1010
+ target,
1011
+ target_index,
1012
+ block,
1013
+ "OPERAND2",
1014
+ depth + 1,
1015
+ true,
1016
+ ),
1017
+ ) {
1018
+ (Some(left), Some(right)) =>
1019
+ if number_expr_uses_only_numeric_variables(
1020
+ left, variable_numeric_by_id, 0,
1021
+ ) &&
1022
+ number_expr_uses_only_numeric_variables(
1023
+ right, variable_numeric_by_id, 0,
1024
+ ) {
1025
+ Some(expr_json_cmp_num("gt", left, right))
1026
+ } else {
1027
+ None
1028
+ }
1029
+ _ => None
1030
+ }
1031
+ "operator_equals" =>
1032
+ match
1033
+ (
1034
+ compile_number_expr_from_input(
1035
+ target,
1036
+ target_index,
1037
+ block,
1038
+ "OPERAND1",
1039
+ depth + 1,
1040
+ true,
1041
+ ),
1042
+ compile_number_expr_from_input(
1043
+ target,
1044
+ target_index,
1045
+ block,
1046
+ "OPERAND2",
1047
+ depth + 1,
1048
+ true,
1049
+ ),
1050
+ ) {
1051
+ (Some(left), Some(right)) =>
1052
+ if number_expr_uses_only_numeric_variables(
1053
+ left, variable_numeric_by_id, 0,
1054
+ ) &&
1055
+ number_expr_uses_only_numeric_variables(
1056
+ right, variable_numeric_by_id, 0,
1057
+ ) {
1058
+ Some(expr_json_cmp_num("eq", left, right))
1059
+ } else {
1060
+ None
1061
+ }
1062
+ _ => None
1063
+ }
1064
+ "operator_and" =>
1065
+ match
1066
+ (
1067
+ compile_bool_expr_from_input(
1068
+ target,
1069
+ target_index,
1070
+ block,
1071
+ "OPERAND1",
1072
+ depth + 1,
1073
+ variable_numeric_by_id,
1074
+ ),
1075
+ compile_bool_expr_from_input(
1076
+ target,
1077
+ target_index,
1078
+ block,
1079
+ "OPERAND2",
1080
+ depth + 1,
1081
+ variable_numeric_by_id,
1082
+ ),
1083
+ ) {
1084
+ (Some(left), Some(right)) =>
1085
+ Some(expr_json_bin_bool("and", left, right))
1086
+ _ => None
1087
+ }
1088
+ "operator_or" =>
1089
+ match
1090
+ (
1091
+ compile_bool_expr_from_input(
1092
+ target,
1093
+ target_index,
1094
+ block,
1095
+ "OPERAND1",
1096
+ depth + 1,
1097
+ variable_numeric_by_id,
1098
+ ),
1099
+ compile_bool_expr_from_input(
1100
+ target,
1101
+ target_index,
1102
+ block,
1103
+ "OPERAND2",
1104
+ depth + 1,
1105
+ variable_numeric_by_id,
1106
+ ),
1107
+ ) {
1108
+ (Some(left), Some(right)) => Some(expr_json_bin_bool("or", left, right))
1109
+ _ => None
1110
+ }
1111
+ "operator_not" =>
1112
+ match
1113
+ compile_bool_expr_from_input(
1114
+ target,
1115
+ target_index,
1116
+ block,
1117
+ "OPERAND",
1118
+ depth + 1,
1119
+ variable_numeric_by_id,
1120
+ ) {
1121
+ Some(value) => Some(expr_json_not(value))
1122
+ None => None
1123
+ }
1124
+ _ =>
1125
+ match
1126
+ compile_number_expr_from_block(
1127
+ target,
1128
+ target_index,
1129
+ block_id,
1130
+ depth + 1,
1131
+ true,
1132
+ ) {
1133
+ Some(num_expr) => Some(expr_json_bool_from_number(num_expr))
1134
+ None => None
1135
+ }
1136
+ }
1137
+ }
1138
+
1139
+ ///|
1140
+ fn compile_bool_expr_from_input(
1141
+ target : TargetState,
1142
+ target_index : Int,
1143
+ block : ScratchBlock,
1144
+ input_name : String,
1145
+ depth : Int,
1146
+ variable_numeric_by_id : Map[String, Bool],
1147
+ ) -> Json? {
1148
+ if depth > 64 {
1149
+ return None
1150
+ }
1151
+ match resolve_reporter_block_id(block, input_name) {
1152
+ Some(block_id) =>
1153
+ compile_bool_expr_from_block(
1154
+ target,
1155
+ target_index,
1156
+ block_id,
1157
+ depth + 1,
1158
+ variable_numeric_by_id,
1159
+ )
1160
+ None =>
1161
+ match block.inputs.get(input_name) {
1162
+ Some(raw_input) =>
1163
+ match input_payload(raw_input) {
1164
+ Some(payload) =>
1165
+ compile_bool_expr_from_payload(target_index, payload)
1166
+ None => Some(expr_json_bool(false))
1167
+ }
1168
+ None => Some(expr_json_bool(false))
1169
+ }
1170
+ }
1171
+ }
1172
+
1173
+ ///|
1174
+ fn compile_sequence_from_pc(
1175
+ target : TargetState,
1176
+ target_index : Int,
1177
+ start_pc : Int?,
1178
+ depth : Int,
1179
+ variable_numeric_by_id : Map[String, Bool],
1180
+ ) -> Array[Json]? {
1181
+ if depth > 96 {
1182
+ return None
1183
+ }
1184
+ let out = []
1185
+ let mut cursor = start_pc
1186
+ let mut steps = 0
1187
+ while true {
1188
+ match cursor {
1189
+ Some(pc) => {
1190
+ if pc < 0 || pc >= target.blocks_by_pc.length() {
1191
+ return None
1192
+ }
1193
+ if steps > target.blocks_by_pc.length() * 4 {
1194
+ return None
1195
+ }
1196
+ steps += 1
1197
+ let block = target.blocks_by_pc[pc]
1198
+ let fast_meta = target.block_fast_meta_by_pc[pc]
1199
+ let host_fallback_stmt = opcode_fallback_stmt(
1200
+ block.opcode,
1201
+ target_index,
1202
+ pc,
1203
+ )
1204
+ let host_tail_stmt = AotCompileStmtResult::EmitAndStop(
1205
+ command_json_host_tail(target_index, pc),
1206
+ )
1207
+ let stmt = match block.opcode {
1208
+ "event_whenflagclicked" => AotCompileStmtResult::Noop
1209
+ "data_setvariableto" =>
1210
+ match variable_id_from_field(block) {
1211
+ Some(variable_id) =>
1212
+ match literal_value_from_input(block, "VALUE") {
1213
+ Some(value) =>
1214
+ match json_finite_number_or_none(value) {
1215
+ Some(number_value) =>
1216
+ AotCompileStmtResult::Emit(
1217
+ command_json_set_var_num_expr(
1218
+ target_index,
1219
+ variable_id,
1220
+ expr_json_num(number_value),
1221
+ ),
1222
+ )
1223
+ None =>
1224
+ AotCompileStmtResult::Emit(
1225
+ command_json_set_var_json_const(
1226
+ target_index, variable_id, value,
1227
+ ),
1228
+ )
1229
+ }
1230
+ None =>
1231
+ match
1232
+ compile_number_expr_from_input(
1233
+ target,
1234
+ target_index,
1235
+ block,
1236
+ "VALUE",
1237
+ depth + 1,
1238
+ true,
1239
+ ) {
1240
+ Some(expr) =>
1241
+ AotCompileStmtResult::Emit(
1242
+ command_json_set_var_num_expr(
1243
+ target_index, variable_id, expr,
1244
+ ),
1245
+ )
1246
+ None => host_fallback_stmt
1247
+ }
1248
+ }
1249
+ None => host_fallback_stmt
1250
+ }
1251
+ "data_changevariableby" =>
1252
+ match variable_id_from_field(block) {
1253
+ Some(variable_id) =>
1254
+ match
1255
+ compile_number_expr_from_input(
1256
+ target,
1257
+ target_index,
1258
+ block,
1259
+ "VALUE",
1260
+ depth + 1,
1261
+ true,
1262
+ ) {
1263
+ Some(expr) =>
1264
+ AotCompileStmtResult::Emit(
1265
+ command_json_change_var_num_expr(
1266
+ target_index, variable_id, expr,
1267
+ ),
1268
+ )
1269
+ None => host_fallback_stmt
1270
+ }
1271
+ None => host_fallback_stmt
1272
+ }
1273
+ "motion_movesteps" =>
1274
+ match
1275
+ compile_number_expr_from_input(
1276
+ target,
1277
+ target_index,
1278
+ block,
1279
+ "STEPS",
1280
+ depth + 1,
1281
+ true,
1282
+ ) {
1283
+ Some(steps_expr) =>
1284
+ AotCompileStmtResult::Emit(
1285
+ command_json_draw_opcode(
1286
+ target_index,
1287
+ "motion_movesteps",
1288
+ steps_expr,
1289
+ expr_json_num(0.0),
1290
+ 0,
1291
+ ),
1292
+ )
1293
+ None => host_fallback_stmt
1294
+ }
1295
+ "motion_turnright" =>
1296
+ match
1297
+ compile_number_expr_from_input(
1298
+ target,
1299
+ target_index,
1300
+ block,
1301
+ "DEGREES",
1302
+ depth + 1,
1303
+ true,
1304
+ ) {
1305
+ Some(degrees_expr) =>
1306
+ AotCompileStmtResult::Emit(
1307
+ command_json_draw_opcode(
1308
+ target_index,
1309
+ "motion_turnright",
1310
+ degrees_expr,
1311
+ expr_json_num(0.0),
1312
+ 0,
1313
+ ),
1314
+ )
1315
+ None => host_fallback_stmt
1316
+ }
1317
+ "motion_turnleft" =>
1318
+ match
1319
+ compile_number_expr_from_input(
1320
+ target,
1321
+ target_index,
1322
+ block,
1323
+ "DEGREES",
1324
+ depth + 1,
1325
+ true,
1326
+ ) {
1327
+ Some(degrees_expr) =>
1328
+ AotCompileStmtResult::Emit(
1329
+ command_json_draw_opcode(
1330
+ target_index,
1331
+ "motion_turnleft",
1332
+ degrees_expr,
1333
+ expr_json_num(0.0),
1334
+ 0,
1335
+ ),
1336
+ )
1337
+ None => host_fallback_stmt
1338
+ }
1339
+ "motion_pointindirection" =>
1340
+ match
1341
+ compile_number_expr_from_input(
1342
+ target,
1343
+ target_index,
1344
+ block,
1345
+ "DIRECTION",
1346
+ depth + 1,
1347
+ true,
1348
+ ) {
1349
+ Some(direction_expr) =>
1350
+ AotCompileStmtResult::Emit(
1351
+ command_json_draw_opcode(
1352
+ target_index,
1353
+ "motion_pointindirection",
1354
+ direction_expr,
1355
+ expr_json_num(0.0),
1356
+ 0,
1357
+ ),
1358
+ )
1359
+ None => host_fallback_stmt
1360
+ }
1361
+ "motion_changexby" =>
1362
+ match
1363
+ compile_number_expr_from_input(
1364
+ target,
1365
+ target_index,
1366
+ block,
1367
+ "DX",
1368
+ depth + 1,
1369
+ true,
1370
+ ) {
1371
+ Some(dx_expr) =>
1372
+ AotCompileStmtResult::Emit(
1373
+ command_json_draw_opcode(
1374
+ target_index,
1375
+ "motion_changexby",
1376
+ dx_expr,
1377
+ expr_json_num(0.0),
1378
+ 0,
1379
+ ),
1380
+ )
1381
+ None => host_fallback_stmt
1382
+ }
1383
+ "motion_changeyby" =>
1384
+ match
1385
+ compile_number_expr_from_input(
1386
+ target,
1387
+ target_index,
1388
+ block,
1389
+ "DY",
1390
+ depth + 1,
1391
+ true,
1392
+ ) {
1393
+ Some(dy_expr) =>
1394
+ AotCompileStmtResult::Emit(
1395
+ command_json_draw_opcode(
1396
+ target_index,
1397
+ "motion_changeyby",
1398
+ dy_expr,
1399
+ expr_json_num(0.0),
1400
+ 0,
1401
+ ),
1402
+ )
1403
+ None => host_fallback_stmt
1404
+ }
1405
+ "motion_setx" =>
1406
+ match
1407
+ compile_number_expr_from_input(
1408
+ target,
1409
+ target_index,
1410
+ block,
1411
+ "X",
1412
+ depth + 1,
1413
+ true,
1414
+ ) {
1415
+ Some(x_expr) =>
1416
+ AotCompileStmtResult::Emit(
1417
+ command_json_draw_opcode(
1418
+ target_index,
1419
+ "motion_setx",
1420
+ x_expr,
1421
+ expr_json_num(0.0),
1422
+ 0,
1423
+ ),
1424
+ )
1425
+ None => host_fallback_stmt
1426
+ }
1427
+ "motion_sety" =>
1428
+ match
1429
+ compile_number_expr_from_input(
1430
+ target,
1431
+ target_index,
1432
+ block,
1433
+ "Y",
1434
+ depth + 1,
1435
+ true,
1436
+ ) {
1437
+ Some(y_expr) =>
1438
+ AotCompileStmtResult::Emit(
1439
+ command_json_draw_opcode(
1440
+ target_index,
1441
+ "motion_sety",
1442
+ y_expr,
1443
+ expr_json_num(0.0),
1444
+ 0,
1445
+ ),
1446
+ )
1447
+ None => host_fallback_stmt
1448
+ }
1449
+ "motion_gotoxy" =>
1450
+ match
1451
+ (
1452
+ compile_number_expr_from_input(
1453
+ target,
1454
+ target_index,
1455
+ block,
1456
+ "X",
1457
+ depth + 1,
1458
+ true,
1459
+ ),
1460
+ compile_number_expr_from_input(
1461
+ target,
1462
+ target_index,
1463
+ block,
1464
+ "Y",
1465
+ depth + 1,
1466
+ true,
1467
+ ),
1468
+ ) {
1469
+ (Some(x_expr), Some(y_expr)) =>
1470
+ AotCompileStmtResult::Emit(
1471
+ command_json_draw_opcode(
1472
+ target_index, "motion_gotoxy", x_expr, y_expr, 0,
1473
+ ),
1474
+ )
1475
+ _ => host_fallback_stmt
1476
+ }
1477
+ "motion_ifonedgebounce" =>
1478
+ AotCompileStmtResult::Emit(
1479
+ command_json_draw_opcode(
1480
+ target_index,
1481
+ "motion_ifonedgebounce",
1482
+ expr_json_num(0.0),
1483
+ expr_json_num(0.0),
1484
+ 0,
1485
+ ),
1486
+ )
1487
+ "looks_show" =>
1488
+ AotCompileStmtResult::Emit(
1489
+ command_json_draw_opcode(
1490
+ target_index,
1491
+ "looks_show",
1492
+ expr_json_num(0.0),
1493
+ expr_json_num(0.0),
1494
+ 0,
1495
+ ),
1496
+ )
1497
+ "looks_hide" =>
1498
+ AotCompileStmtResult::Emit(
1499
+ command_json_draw_opcode(
1500
+ target_index,
1501
+ "looks_hide",
1502
+ expr_json_num(0.0),
1503
+ expr_json_num(0.0),
1504
+ 0,
1505
+ ),
1506
+ )
1507
+ "looks_hideallsprites" =>
1508
+ AotCompileStmtResult::Emit(
1509
+ command_json_draw_opcode(
1510
+ target_index,
1511
+ "looks_hideallsprites",
1512
+ expr_json_num(0.0),
1513
+ expr_json_num(0.0),
1514
+ 0,
1515
+ ),
1516
+ )
1517
+ "looks_nextcostume" =>
1518
+ AotCompileStmtResult::Emit(
1519
+ command_json_draw_opcode(
1520
+ target_index,
1521
+ "looks_nextcostume",
1522
+ expr_json_num(0.0),
1523
+ expr_json_num(0.0),
1524
+ 0,
1525
+ ),
1526
+ )
1527
+ "looks_changesizeby" =>
1528
+ match
1529
+ compile_number_expr_from_input(
1530
+ target,
1531
+ target_index,
1532
+ block,
1533
+ "CHANGE",
1534
+ depth + 1,
1535
+ true,
1536
+ ) {
1537
+ Some(delta_expr) =>
1538
+ AotCompileStmtResult::Emit(
1539
+ command_json_draw_opcode(
1540
+ target_index,
1541
+ "looks_changesizeby",
1542
+ delta_expr,
1543
+ expr_json_num(0.0),
1544
+ 0,
1545
+ ),
1546
+ )
1547
+ None => host_fallback_stmt
1548
+ }
1549
+ "looks_setsizeto" =>
1550
+ match
1551
+ compile_number_expr_from_input(
1552
+ target,
1553
+ target_index,
1554
+ block,
1555
+ "SIZE",
1556
+ depth + 1,
1557
+ true,
1558
+ ) {
1559
+ Some(size_expr) =>
1560
+ AotCompileStmtResult::Emit(
1561
+ command_json_draw_opcode(
1562
+ target_index,
1563
+ "looks_setsizeto",
1564
+ size_expr,
1565
+ expr_json_num(0.0),
1566
+ 0,
1567
+ ),
1568
+ )
1569
+ None => host_fallback_stmt
1570
+ }
1571
+ "looks_changeeffectby" =>
1572
+ match
1573
+ (
1574
+ literal_string_from_input_or_field(block, "EFFECT", "EFFECT"),
1575
+ compile_number_expr_from_input(
1576
+ target,
1577
+ target_index,
1578
+ block,
1579
+ "CHANGE",
1580
+ depth + 1,
1581
+ true,
1582
+ ),
1583
+ ) {
1584
+ (Some(effect_name), Some(amount_expr)) =>
1585
+ match normalize_looks_effect_name_for_draw(effect_name) {
1586
+ Some(effect_id) =>
1587
+ AotCompileStmtResult::Emit(
1588
+ command_json_draw_opcode(
1589
+ target_index,
1590
+ "looks_changeeffectby",
1591
+ amount_expr,
1592
+ expr_json_num(0.0),
1593
+ effect_id,
1594
+ ),
1595
+ )
1596
+ None => host_fallback_stmt
1597
+ }
1598
+ _ => host_fallback_stmt
1599
+ }
1600
+ "looks_seteffectto" =>
1601
+ match
1602
+ (
1603
+ literal_string_from_input_or_field(block, "EFFECT", "EFFECT"),
1604
+ compile_number_expr_from_input(
1605
+ target,
1606
+ target_index,
1607
+ block,
1608
+ "VALUE",
1609
+ depth + 1,
1610
+ true,
1611
+ ),
1612
+ ) {
1613
+ (Some(effect_name), Some(amount_expr)) =>
1614
+ match normalize_looks_effect_name_for_draw(effect_name) {
1615
+ Some(effect_id) =>
1616
+ AotCompileStmtResult::Emit(
1617
+ command_json_draw_opcode(
1618
+ target_index,
1619
+ "looks_seteffectto",
1620
+ amount_expr,
1621
+ expr_json_num(0.0),
1622
+ effect_id,
1623
+ ),
1624
+ )
1625
+ None => host_fallback_stmt
1626
+ }
1627
+ _ => host_fallback_stmt
1628
+ }
1629
+ "looks_cleargraphiceffects" =>
1630
+ AotCompileStmtResult::Emit(
1631
+ command_json_draw_opcode(
1632
+ target_index,
1633
+ "looks_cleargraphiceffects",
1634
+ expr_json_num(0.0),
1635
+ expr_json_num(0.0),
1636
+ 0,
1637
+ ),
1638
+ )
1639
+ "pen_clear" =>
1640
+ AotCompileStmtResult::Emit(
1641
+ command_json_draw_opcode(
1642
+ target_index,
1643
+ "pen_clear",
1644
+ expr_json_num(0.0),
1645
+ expr_json_num(0.0),
1646
+ 0,
1647
+ ),
1648
+ )
1649
+ "pen_stamp" =>
1650
+ AotCompileStmtResult::Emit(
1651
+ command_json_draw_opcode(
1652
+ target_index,
1653
+ "pen_stamp",
1654
+ expr_json_num(0.0),
1655
+ expr_json_num(0.0),
1656
+ 0,
1657
+ ),
1658
+ )
1659
+ "pen_penDown" =>
1660
+ AotCompileStmtResult::Emit(
1661
+ command_json_draw_opcode(
1662
+ target_index,
1663
+ "pen_penDown",
1664
+ expr_json_num(0.0),
1665
+ expr_json_num(0.0),
1666
+ 0,
1667
+ ),
1668
+ )
1669
+ "pen_penUp" =>
1670
+ AotCompileStmtResult::Emit(
1671
+ command_json_draw_opcode(
1672
+ target_index,
1673
+ "pen_penUp",
1674
+ expr_json_num(0.0),
1675
+ expr_json_num(0.0),
1676
+ 0,
1677
+ ),
1678
+ )
1679
+ "pen_changePenColorParamBy" =>
1680
+ match
1681
+ (
1682
+ literal_string_from_input_or_field(
1683
+ block, "COLOR_PARAM", "COLOR_PARAM",
1684
+ ),
1685
+ compile_number_expr_from_input(
1686
+ target,
1687
+ target_index,
1688
+ block,
1689
+ "VALUE",
1690
+ depth + 1,
1691
+ true,
1692
+ ),
1693
+ ) {
1694
+ (Some(param_name), Some(value_expr)) =>
1695
+ match normalize_pen_color_param_for_draw(param_name) {
1696
+ Some(param_id) =>
1697
+ AotCompileStmtResult::Emit(
1698
+ command_json_draw_opcode(
1699
+ target_index,
1700
+ "pen_changePenColorParamBy",
1701
+ value_expr,
1702
+ expr_json_num(0.0),
1703
+ param_id,
1704
+ ),
1705
+ )
1706
+ None => host_fallback_stmt
1707
+ }
1708
+ _ => host_fallback_stmt
1709
+ }
1710
+ "pen_setPenColorParamTo" =>
1711
+ match
1712
+ (
1713
+ literal_string_from_input_or_field(
1714
+ block, "COLOR_PARAM", "COLOR_PARAM",
1715
+ ),
1716
+ compile_number_expr_from_input(
1717
+ target,
1718
+ target_index,
1719
+ block,
1720
+ "VALUE",
1721
+ depth + 1,
1722
+ true,
1723
+ ),
1724
+ ) {
1725
+ (Some(param_name), Some(value_expr)) =>
1726
+ match normalize_pen_color_param_for_draw(param_name) {
1727
+ Some(param_id) =>
1728
+ AotCompileStmtResult::Emit(
1729
+ command_json_draw_opcode(
1730
+ target_index,
1731
+ "pen_setPenColorParamTo",
1732
+ value_expr,
1733
+ expr_json_num(0.0),
1734
+ param_id,
1735
+ ),
1736
+ )
1737
+ None => host_fallback_stmt
1738
+ }
1739
+ _ => host_fallback_stmt
1740
+ }
1741
+ "pen_changePenSizeBy" =>
1742
+ match
1743
+ compile_number_expr_from_input(
1744
+ target,
1745
+ target_index,
1746
+ block,
1747
+ "SIZE",
1748
+ depth + 1,
1749
+ true,
1750
+ ) {
1751
+ Some(delta_expr) =>
1752
+ AotCompileStmtResult::Emit(
1753
+ command_json_draw_opcode(
1754
+ target_index,
1755
+ "pen_changePenSizeBy",
1756
+ delta_expr,
1757
+ expr_json_num(0.0),
1758
+ 0,
1759
+ ),
1760
+ )
1761
+ None => host_fallback_stmt
1762
+ }
1763
+ "pen_setPenSizeTo" =>
1764
+ match
1765
+ compile_number_expr_from_input(
1766
+ target,
1767
+ target_index,
1768
+ block,
1769
+ "SIZE",
1770
+ depth + 1,
1771
+ true,
1772
+ ) {
1773
+ Some(size_expr) =>
1774
+ AotCompileStmtResult::Emit(
1775
+ command_json_draw_opcode(
1776
+ target_index,
1777
+ "pen_setPenSizeTo",
1778
+ size_expr,
1779
+ expr_json_num(0.0),
1780
+ 0,
1781
+ ),
1782
+ )
1783
+ None => host_fallback_stmt
1784
+ }
1785
+ "pen_setPenHueToNumber" =>
1786
+ match
1787
+ compile_number_expr_from_input(
1788
+ target,
1789
+ target_index,
1790
+ block,
1791
+ "HUE",
1792
+ depth + 1,
1793
+ true,
1794
+ ) {
1795
+ Some(hue_expr) =>
1796
+ AotCompileStmtResult::Emit(
1797
+ command_json_draw_opcode(
1798
+ target_index,
1799
+ "pen_setPenHueToNumber",
1800
+ hue_expr,
1801
+ expr_json_num(0.0),
1802
+ 0,
1803
+ ),
1804
+ )
1805
+ None => host_fallback_stmt
1806
+ }
1807
+ "pen_changePenHueBy" =>
1808
+ match
1809
+ compile_number_expr_from_input(
1810
+ target,
1811
+ target_index,
1812
+ block,
1813
+ "HUE",
1814
+ depth + 1,
1815
+ true,
1816
+ ) {
1817
+ Some(hue_expr) =>
1818
+ AotCompileStmtResult::Emit(
1819
+ command_json_draw_opcode(
1820
+ target_index,
1821
+ "pen_changePenHueBy",
1822
+ hue_expr,
1823
+ expr_json_num(0.0),
1824
+ 0,
1825
+ ),
1826
+ )
1827
+ None => host_fallback_stmt
1828
+ }
1829
+ "pen_setPenShadeToNumber" =>
1830
+ match
1831
+ compile_number_expr_from_input(
1832
+ target,
1833
+ target_index,
1834
+ block,
1835
+ "SHADE",
1836
+ depth + 1,
1837
+ true,
1838
+ ) {
1839
+ Some(shade_expr) =>
1840
+ AotCompileStmtResult::Emit(
1841
+ command_json_draw_opcode(
1842
+ target_index,
1843
+ "pen_setPenShadeToNumber",
1844
+ shade_expr,
1845
+ expr_json_num(0.0),
1846
+ 0,
1847
+ ),
1848
+ )
1849
+ None => host_fallback_stmt
1850
+ }
1851
+ "pen_changePenShadeBy" =>
1852
+ match
1853
+ compile_number_expr_from_input(
1854
+ target,
1855
+ target_index,
1856
+ block,
1857
+ "SHADE",
1858
+ depth + 1,
1859
+ true,
1860
+ ) {
1861
+ Some(shade_expr) =>
1862
+ AotCompileStmtResult::Emit(
1863
+ command_json_draw_opcode(
1864
+ target_index,
1865
+ "pen_changePenShadeBy",
1866
+ shade_expr,
1867
+ expr_json_num(0.0),
1868
+ 0,
1869
+ ),
1870
+ )
1871
+ None => host_fallback_stmt
1872
+ }
1873
+ "control_if" =>
1874
+ match
1875
+ (
1876
+ compile_bool_expr_from_input(
1877
+ target,
1878
+ target_index,
1879
+ block,
1880
+ "CONDITION",
1881
+ depth + 1,
1882
+ variable_numeric_by_id,
1883
+ ),
1884
+ compile_sequence_from_pc(
1885
+ target,
1886
+ target_index,
1887
+ fast_meta.substack_pc,
1888
+ depth + 1,
1889
+ variable_numeric_by_id,
1890
+ ),
1891
+ ) {
1892
+ (Some(cond), Some(body)) =>
1893
+ if command_list_contains_host_tail(body) {
1894
+ host_tail_stmt
1895
+ } else {
1896
+ AotCompileStmtResult::Emit(command_json_if(cond, body))
1897
+ }
1898
+ _ => host_fallback_stmt
1899
+ }
1900
+ "control_if_else" =>
1901
+ match
1902
+ (
1903
+ compile_bool_expr_from_input(
1904
+ target,
1905
+ target_index,
1906
+ block,
1907
+ "CONDITION",
1908
+ depth + 1,
1909
+ variable_numeric_by_id,
1910
+ ),
1911
+ compile_sequence_from_pc(
1912
+ target,
1913
+ target_index,
1914
+ fast_meta.substack_pc,
1915
+ depth + 1,
1916
+ variable_numeric_by_id,
1917
+ ),
1918
+ compile_sequence_from_pc(
1919
+ target,
1920
+ target_index,
1921
+ fast_meta.substack2_pc,
1922
+ depth + 1,
1923
+ variable_numeric_by_id,
1924
+ ),
1925
+ ) {
1926
+ (Some(cond), Some(then_body), Some(else_body)) =>
1927
+ if command_list_contains_host_tail(then_body) ||
1928
+ command_list_contains_host_tail(else_body) {
1929
+ host_tail_stmt
1930
+ } else {
1931
+ AotCompileStmtResult::Emit(
1932
+ command_json_if_else(cond, then_body, else_body),
1933
+ )
1934
+ }
1935
+ _ => host_fallback_stmt
1936
+ }
1937
+ "control_repeat" =>
1938
+ match
1939
+ (
1940
+ compile_number_expr_from_input(
1941
+ target,
1942
+ target_index,
1943
+ block,
1944
+ "TIMES",
1945
+ depth + 1,
1946
+ true,
1947
+ ),
1948
+ compile_sequence_from_pc(
1949
+ target,
1950
+ target_index,
1951
+ fast_meta.substack_pc,
1952
+ depth + 1,
1953
+ variable_numeric_by_id,
1954
+ ),
1955
+ ) {
1956
+ (Some(times), Some(body)) =>
1957
+ if command_list_contains_host_tail(body) {
1958
+ host_tail_stmt
1959
+ } else {
1960
+ AotCompileStmtResult::Emit(command_json_repeat(times, body))
1961
+ }
1962
+ _ => host_fallback_stmt
1963
+ }
1964
+ "control_repeat_until" =>
1965
+ match
1966
+ (
1967
+ compile_bool_expr_from_input(
1968
+ target,
1969
+ target_index,
1970
+ block,
1971
+ "CONDITION",
1972
+ depth + 1,
1973
+ variable_numeric_by_id,
1974
+ ),
1975
+ compile_sequence_from_pc(
1976
+ target,
1977
+ target_index,
1978
+ fast_meta.substack_pc,
1979
+ depth + 1,
1980
+ variable_numeric_by_id,
1981
+ ),
1982
+ ) {
1983
+ (Some(cond), Some(body)) =>
1984
+ if command_list_contains_host_tail(body) {
1985
+ host_tail_stmt
1986
+ } else {
1987
+ AotCompileStmtResult::Emit(
1988
+ command_json_repeat_until(cond, body),
1989
+ )
1990
+ }
1991
+ _ => host_fallback_stmt
1992
+ }
1993
+ "control_while" =>
1994
+ match
1995
+ (
1996
+ compile_bool_expr_from_input(
1997
+ target,
1998
+ target_index,
1999
+ block,
2000
+ "CONDITION",
2001
+ depth + 1,
2002
+ variable_numeric_by_id,
2003
+ ),
2004
+ compile_sequence_from_pc(
2005
+ target,
2006
+ target_index,
2007
+ fast_meta.substack_pc,
2008
+ depth + 1,
2009
+ variable_numeric_by_id,
2010
+ ),
2011
+ ) {
2012
+ (Some(cond), Some(body)) =>
2013
+ if command_list_contains_host_tail(body) {
2014
+ host_tail_stmt
2015
+ } else {
2016
+ AotCompileStmtResult::Emit(command_json_while(cond, body))
2017
+ }
2018
+ _ => host_fallback_stmt
2019
+ }
2020
+ _ => host_fallback_stmt
2021
+ }
2022
+ match stmt {
2023
+ AotCompileStmtResult::Noop => ()
2024
+ AotCompileStmtResult::Emit(command) => out.push(command)
2025
+ AotCompileStmtResult::EmitAndStop(command) => {
2026
+ out.push(command)
2027
+ return Some(out)
2028
+ }
2029
+ }
2030
+ cursor = fast_meta.next_pc
2031
+ }
2032
+ None => break
2033
+ }
2034
+ }
2035
+ Some(out)
2036
+ }
2037
+
2038
+ ///|
2039
+ fn append_compiled_green_flag_commands(
2040
+ target : TargetState,
2041
+ target_index : Int,
2042
+ variable_numeric_by_id : Map[String, Bool],
2043
+ out : Array[Json],
2044
+ ) -> Bool {
2045
+ for start_id in target.green_flag_starts {
2046
+ let start_pc = match target.block_pc_by_id.get(start_id) {
2047
+ Some(pc) => Some(pc)
2048
+ None => return false
2049
+ }
2050
+ let commands = match
2051
+ compile_sequence_from_pc(
2052
+ target, target_index, start_pc, 0, variable_numeric_by_id,
2053
+ ) {
2054
+ Some(value) => value
2055
+ None => return false
2056
+ }
2057
+ for command in commands {
2058
+ out.push(command)
2059
+ }
2060
+ }
2061
+ true
2062
+ }
2063
+
2064
+ ///|
2065
+ fn compile_aot_full_green_flag_starts_json(
2066
+ targets : Array[TargetState],
2067
+ ) -> (Array[Json], Bool) {
2068
+ let starts = []
2069
+ let mut has_any_start = false
2070
+ for target_index, target in targets {
2071
+ let pcs = []
2072
+ for start_id in target.green_flag_starts {
2073
+ match target.block_pc_by_id.get(start_id) {
2074
+ Some(pc) => pcs.push(pc)
2075
+ None => ()
2076
+ }
2077
+ }
2078
+ if !pcs.is_empty() {
2079
+ starts.push(command_json_full_green_flag_start_entry(target_index, pcs))
2080
+ has_any_start = true
2081
+ }
2082
+ }
2083
+ (starts, has_any_start)
2084
+ }
2085
+
2086
+ ///|
2087
+ fn compile_aot_commands_json(targets : Array[TargetState]) -> String? {
2088
+ let commands = []
2089
+ let variable_numeric_by_id = build_aot_variable_numeric_map(targets)
2090
+ for target_index, target in targets {
2091
+ if !target_is_aot_eligible(target) {
2092
+ commands.clear()
2093
+ break
2094
+ }
2095
+ if !append_compiled_green_flag_commands(
2096
+ target, target_index, variable_numeric_by_id, commands,
2097
+ ) {
2098
+ commands.clear()
2099
+ break
2100
+ }
2101
+ }
2102
+ let opcode_catalog = []
2103
+ for target_index, target in targets {
2104
+ for pc, block in target.blocks_by_pc {
2105
+ opcode_catalog.push(
2106
+ command_json_opcode_catalog(
2107
+ target_index,
2108
+ pc,
2109
+ block,
2110
+ target.block_fast_meta_by_pc[pc],
2111
+ ),
2112
+ )
2113
+ }
2114
+ }
2115
+ let (full_green_flag_starts, has_any_start) = compile_aot_full_green_flag_starts_json(
2116
+ targets,
2117
+ )
2118
+ let exec_mode = if !commands.is_empty() {
2119
+ "linear"
2120
+ } else if has_any_start {
2121
+ "full"
2122
+ } else {
2123
+ "linear"
2124
+ }
2125
+ Some(
2126
+ json_object({
2127
+ "exec": json_array(commands),
2128
+ "exec_mode": json_string(exec_mode),
2129
+ "full_green_flag_starts": json_array(full_green_flag_starts),
2130
+ "catalog": json_array(opcode_catalog),
2131
+ }).stringify(),
2132
+ )
2133
+ }
2134
+
2135
+ ///|
2136
+ fn program_wat_from_payload(
2137
+ project_base64 : String,
2138
+ assets_base64 : String,
2139
+ commands_base64 : String,
2140
+ ) -> String {
2141
+ let project_len = project_base64.length()
2142
+ let assets_offset = project_len
2143
+ let assets_len = assets_base64.length()
2144
+ let commands_offset = assets_offset + assets_len
2145
+ let commands_len = commands_base64.length()
2146
+ let memory_pages = page_count_for_bytes(
2147
+ project_len + assets_len + commands_len,
2148
+ )
2149
+ (
2150
+ $|;; moonscratch_program_v1
2151
+ $|;; abi_version=\{PROGRAM_ABI_VERSION}
2152
+ $|;; project_base64=\{project_base64}
2153
+ $|;; assets_base64=\{assets_base64}
2154
+ $|;; commands_base64=\{commands_base64}
2155
+ $|(module
2156
+ $| (memory (export "memory") \{memory_pages})
2157
+ $| (data (i32.const 0) "\{project_base64}")
2158
+ $| (data (i32.const \{assets_offset}) "\{assets_base64}")
2159
+ $| (data (i32.const \{commands_offset}) "\{commands_base64}")
2160
+ $| (func (export "ms_abi_version") (result i32)
2161
+ $| i32.const \{PROGRAM_ABI_VERSION}
2162
+ $| )
2163
+ $| (func (export "ms_project_ptr") (result i32)
2164
+ $| i32.const 0
2165
+ $| )
2166
+ $| (func (export "ms_project_len") (result i32)
2167
+ $| i32.const \{project_len}
2168
+ $| )
2169
+ $| (func (export "ms_assets_ptr") (result i32)
2170
+ $| i32.const \{assets_offset}
2171
+ $| )
2172
+ $| (func (export "ms_assets_len") (result i32)
2173
+ $| i32.const \{assets_len}
2174
+ $| )
2175
+ $| (func (export "ms_commands_ptr") (result i32)
2176
+ $| i32.const \{commands_offset}
2177
+ $| )
2178
+ $| (func (export "ms_commands_len") (result i32)
2179
+ $| i32.const \{commands_len}
2180
+ $| )
2181
+ $|)
2182
+ $|
2183
+ )
2184
+ }
2185
+
2186
+ ///|
2187
+ pub fn vm_abi_version() -> Int {
2188
+ PROGRAM_ABI_VERSION
2189
+ }
2190
+
2191
+ ///|
2192
+ pub fn vm_compile_project_to_wat(
2193
+ project_json : String,
2194
+ assets_json? : String,
2195
+ ) -> String raise VmError {
2196
+ let bundle = parse_bundle(project_json, assets_json)
2197
+ let (targets, _) = parse_project_targets(bundle.project_json, bundle.assets)
2198
+ let project_base64 = base64_utf8(bundle.project_json)
2199
+ let assets_raw = json_object(bundle.assets).stringify()
2200
+ let assets_base64 = base64_utf8(assets_raw)
2201
+ let commands_base64 = match compile_aot_commands_json(targets) {
2202
+ Some(commands_json) => base64_utf8(commands_json)
2203
+ None => ""
2204
+ }
2205
+ program_wat_from_payload(project_base64, assets_base64, commands_base64)
2206
+ }