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,44 @@
1
+ ///|
2
+ fn target_snapshot_json(target : TargetState) -> Json {
3
+ let lists_json = {}
4
+ target.lists.each((list_id, items) => lists_json[list_id] = json_array(items))
5
+
6
+ json_object({
7
+ "id": json_string(target.id),
8
+ "name": json_string(target.name),
9
+ "isStage": json_bool(target.is_stage),
10
+ "x": json_number(target.x),
11
+ "y": json_number(target.y),
12
+ "direction": json_number(target.direction),
13
+ "size": json_number(target.size),
14
+ "volume": json_number(target.volume),
15
+ "musicInstrument": json_number(
16
+ Double::from_int(target.music_instrument + 1),
17
+ ),
18
+ "textToSpeechVoice": json_string(target.tts_voice),
19
+ "visible": json_bool(target.visible),
20
+ "currentCostume": json_number(Double::from_int(target.current_costume)),
21
+ "variables": json_object(target.variables),
22
+ "lists": json_object(lists_json),
23
+ })
24
+ }
25
+
26
+ ///|
27
+ fn snapshot_to_json(vm : Vm) -> Json {
28
+ let targets = []
29
+ for target in vm.targets {
30
+ if !target.deleted {
31
+ targets.push(target_snapshot_json(target))
32
+ }
33
+ }
34
+ json_object({
35
+ "runId": json_number(Double::from_int(vm.run_id)),
36
+ "nowMs": json_number(Double::from_int(vm.now_ms)),
37
+ "running": json_bool(vm.running),
38
+ "answer": json_string(vm.answer),
39
+ "musicTempo": json_number(vm.music_tempo),
40
+ "textToSpeechLanguage": json_string(vm.tts_language),
41
+ "activeThreads": json_number(Double::from_int(vm.threads.length())),
42
+ "targets": json_array(targets),
43
+ })
44
+ }
@@ -0,0 +1,5 @@
1
+ # src/cmd
2
+
3
+ - Directory for CLI-related code.
4
+ - Add implementations minimally via `src/cmd/main/`.
5
+ - If shared logic is needed, move it to `src/` for reuse.
@@ -0,0 +1,5 @@
1
+ # src/cmd/main
2
+
3
+ - Manages the CLI entrypoint.
4
+ - Handle argument parsing and I/O here, keeping domain logic in `src/`.
5
+ - After changes, confirm no regression in existing CLI behavior with `moon test`.
@@ -0,0 +1,29 @@
1
+ ///|
2
+ fn main {
3
+ let minimal_project =
4
+ #|{
5
+ #| "targets": [
6
+ #| {
7
+ #| "isStage": true,
8
+ #| "name": "Stage",
9
+ #| "variables": {},
10
+ #| "lists": {},
11
+ #| "blocks": {}
12
+ #| }
13
+ #| ]
14
+ #|}
15
+
16
+ let vm_result = try? ({
17
+ let precompiled = @moonscratch.vm_compile_from_json(minimal_project)
18
+ @moonscratch.vm_new_from_compiled(precompiled)
19
+ })
20
+ match vm_result {
21
+ Ok(vm) => {
22
+ @moonscratch.vm_start(vm)
23
+ @moonscratch.vm_set_time(vm, 16)
24
+ let report = @moonscratch.vm_step_frame(vm)
25
+ println("step report: \{report}")
26
+ }
27
+ Err(_) => println("failed to initialize vm")
28
+ }
29
+ }
@@ -0,0 +1,7 @@
1
+ import {
2
+ "nakasyou/moonscratch" @moonscratch,
3
+ }
4
+
5
+ options(
6
+ "is-main": true,
7
+ )
@@ -0,0 +1,13 @@
1
+ // Generated using `moon info`, DON'T EDIT IT
2
+ package "nakasyou/moonscratch/cmd/main"
3
+
4
+ // Values
5
+
6
+ // Errors
7
+
8
+ // Types and methods
9
+
10
+ // Type aliases
11
+
12
+ // Traits
13
+
@@ -0,0 +1,176 @@
1
+ ///|
2
+ fn[T] invalid_project(message : String) -> T raise VmError {
3
+ raise VmError::InvalidProject(message)
4
+ }
5
+
6
+ ///|
7
+ fn parse_json_or_fail(raw : String) -> Json raise VmError {
8
+ let parsed = try? @json.parse(raw)
9
+ match parsed {
10
+ Ok(json) => json
11
+ Err(err) => invalid_project("failed to parse json: \{err}")
12
+ }
13
+ }
14
+
15
+ ///|
16
+ fn expect_object(
17
+ json : Json,
18
+ context : String,
19
+ ) -> Map[String, Json] raise VmError {
20
+ match json {
21
+ Object(map) => map
22
+ _ => invalid_project("\{context} must be a JSON object")
23
+ }
24
+ }
25
+
26
+ ///|
27
+ fn expect_array(json : Json, context : String) -> Array[Json] raise VmError {
28
+ match json {
29
+ Array(values) => values
30
+ _ => invalid_project("\{context} must be a JSON array")
31
+ }
32
+ }
33
+
34
+ ///|
35
+ fn object_get(obj : Map[String, Json], key : String) -> Json? {
36
+ obj.get(key)
37
+ }
38
+
39
+ ///|
40
+ fn object_get_or(
41
+ obj : Map[String, Json],
42
+ key : String,
43
+ fallback : Json,
44
+ ) -> Json {
45
+ obj.get_or_default(key, fallback)
46
+ }
47
+
48
+ ///|
49
+ fn object_get_string_or(
50
+ obj : Map[String, Json],
51
+ key : String,
52
+ fallback : String,
53
+ ) -> String {
54
+ match obj.get(key) {
55
+ Some(String(s)) => s
56
+ _ => fallback
57
+ }
58
+ }
59
+
60
+ ///|
61
+ fn object_get_bool_or(
62
+ obj : Map[String, Json],
63
+ key : String,
64
+ fallback : Bool,
65
+ ) -> Bool {
66
+ match obj.get(key) {
67
+ Some(True) => true
68
+ Some(False) => false
69
+ _ => fallback
70
+ }
71
+ }
72
+
73
+ ///|
74
+ fn object_get_number_or(
75
+ obj : Map[String, Json],
76
+ key : String,
77
+ fallback : Double,
78
+ ) -> Double {
79
+ match obj.get(key) {
80
+ Some(Number(n, ..)) => n
81
+ _ => fallback
82
+ }
83
+ }
84
+
85
+ ///|
86
+ fn object_get_object_or_empty(
87
+ obj : Map[String, Json],
88
+ key : String,
89
+ ) -> Map[String, Json] {
90
+ match obj.get(key) {
91
+ Some(Object(map)) => map
92
+ _ => {}
93
+ }
94
+ }
95
+
96
+ ///|
97
+ fn json_to_string_value(json : Json) -> String {
98
+ match json {
99
+ String(s) => s
100
+ Number(n, ..) => n.to_string()
101
+ True => "true"
102
+ False => "false"
103
+ Null => ""
104
+ _ => json.stringify()
105
+ }
106
+ }
107
+
108
+ ///|
109
+ fn parse_double_or_none(s : String) -> Double? {
110
+ let trimmed = s.trim().to_string()
111
+ if trimmed.is_empty() {
112
+ None
113
+ } else {
114
+ let parsed = try? @strconv.parse_double(trimmed)
115
+ match parsed {
116
+ Ok(value) => Some(value)
117
+ Err(_) => None
118
+ }
119
+ }
120
+ }
121
+
122
+ ///|
123
+ fn json_to_number_value(json : Json) -> Double {
124
+ match json {
125
+ Number(n, ..) => n
126
+ String(s) =>
127
+ match parse_double_or_none(s) {
128
+ Some(value) => value
129
+ None => 0.0
130
+ }
131
+ True => 1.0
132
+ False => 0.0
133
+ Null => 0.0
134
+ _ => 0.0
135
+ }
136
+ }
137
+
138
+ ///|
139
+ fn json_to_bool_value(json : Json) -> Bool {
140
+ match json {
141
+ True => true
142
+ False => false
143
+ Number(n, ..) => n != 0.0
144
+ String(s) => {
145
+ let lowered = s.trim().to_lower()
146
+ lowered != "" && lowered != "0" && lowered != "false"
147
+ }
148
+ Null => false
149
+ _ => true
150
+ }
151
+ }
152
+
153
+ ///|
154
+ fn json_number(value : Double) -> Json {
155
+ Json::number(value)
156
+ }
157
+
158
+ ///|
159
+ fn json_string(value : String) -> Json {
160
+ Json::string(value)
161
+ }
162
+
163
+ ///|
164
+ fn json_bool(value : Bool) -> Json {
165
+ Json::boolean(value)
166
+ }
167
+
168
+ ///|
169
+ fn json_array(values : Array[Json]) -> Json {
170
+ Json::array(values)
171
+ }
172
+
173
+ ///|
174
+ fn json_object(values : Map[String, Json]) -> Json {
175
+ Json::object(values)
176
+ }
package/src/moon.pkg ADDED
@@ -0,0 +1,65 @@
1
+ import {
2
+ "moonbitlang/core/encoding/base64" @base64,
3
+ "moonbitlang/core/encoding/utf8" @utf8,
4
+ "moonbitlang/core/json" @json,
5
+ "moonbitlang/core/math" @math,
6
+ "moonbitlang/core/strconv" @strconv,
7
+ }
8
+
9
+ options(
10
+ link: {
11
+ "js": {
12
+ "exports": [
13
+ "vm_compile_from_json",
14
+ "vm_compile_project_to_wat",
15
+ "vm_compile_project_to_wasm",
16
+ "vm_abi_version",
17
+ "vm_new_from_compiled",
18
+ "vm_start",
19
+ "vm_green_flag",
20
+ "vm_step_frame",
21
+ "vm_set_time",
22
+ "vm_set_aot_commands_json",
23
+ "vm_get_variable_number_by_id",
24
+ "vm_set_variable_number_by_id",
25
+ "vm_set_variable_json_by_id",
26
+ "vm_exec_script_tail_by_pc",
27
+ "vm_exec_opcode_once_by_pc",
28
+ "vm_exec_draw_opcode",
29
+ "vm_post_io_json",
30
+ "vm_broadcast",
31
+ "vm_stop_all",
32
+ "vm_take_effects_json",
33
+ "vm_snapshot_json",
34
+ "vm_render_frame",
35
+ ],
36
+ "format": "esm",
37
+ },
38
+ "wasm-gc": {
39
+ "exports": [
40
+ "vm_compile_from_json",
41
+ "vm_compile_project_to_wat",
42
+ "vm_compile_project_to_wasm",
43
+ "vm_abi_version",
44
+ "vm_new_from_compiled",
45
+ "vm_start",
46
+ "vm_green_flag",
47
+ "vm_step_frame",
48
+ "vm_set_time",
49
+ "vm_set_aot_commands_json",
50
+ "vm_get_variable_number_by_id",
51
+ "vm_set_variable_number_by_id",
52
+ "vm_set_variable_json_by_id",
53
+ "vm_exec_script_tail_by_pc",
54
+ "vm_exec_opcode_once_by_pc",
55
+ "vm_exec_draw_opcode",
56
+ "vm_post_io_json",
57
+ "vm_broadcast",
58
+ "vm_stop_all",
59
+ "vm_take_effects_json",
60
+ "vm_snapshot_json",
61
+ "vm_render_frame",
62
+ ],
63
+ },
64
+ },
65
+ )
@@ -0,0 +1,3 @@
1
+ // Main package file for your library code.
2
+ // Put public APIs and shared helpers here.
3
+ // Keep blocks small and focused for easy refactors.
@@ -0,0 +1,40 @@
1
+ ///|
2
+ test "default options" {
3
+ let options = default_vm_options()
4
+ inspect(options.max_clones, content="300")
5
+ inspect(options.turbo, content="false")
6
+ }
7
+
8
+ ///|
9
+ test "compile project to generated wat metadata" {
10
+ let minimal_project = "{\"targets\":[{\"isStage\":true,\"name\":\"Stage\",\"variables\":{},\"lists\":{},\"blocks\":{},\"costumes\":[]}]}"
11
+ let wat_result = try? vm_compile_project_to_wat(minimal_project)
12
+ let wat = match wat_result {
13
+ Ok(value) => value
14
+ Err(_) => fail("vm_compile_project_to_wat failed")
15
+ }
16
+ inspect(wat.contains(";; moonscratch_program_v1"), content="true")
17
+ inspect(wat.contains("(export \"ms_abi_version\")"), content="true")
18
+ inspect(wat.contains("(export \"ms_commands_len\")"), content="true")
19
+ }
20
+
21
+ ///|
22
+ test "compile project to generated wasm bytes" {
23
+ let minimal_project = "{\"targets\":[{\"isStage\":true,\"name\":\"Stage\",\"variables\":{},\"lists\":{},\"blocks\":{},\"costumes\":[]}]}"
24
+ let wasm_result = try? vm_compile_project_to_wasm(minimal_project)
25
+ let wasm_base64 = match wasm_result {
26
+ Ok(value) => value
27
+ Err(_) => fail("vm_compile_project_to_wasm failed")
28
+ }
29
+ let decoded = @base64.decode(wasm_base64.view()) catch {
30
+ _ => fail("vm_compile_project_to_wasm returned invalid base64")
31
+ }
32
+ let bytes = decoded.to_array()
33
+ if bytes.length() < 4 {
34
+ fail("wasm bytes are too short")
35
+ }
36
+ inspect(bytes[0] == (0x00).to_byte(), content="true")
37
+ inspect(bytes[1] == (0x61).to_byte(), content="true")
38
+ inspect(bytes[2] == (0x73).to_byte(), content="true")
39
+ inspect(bytes[3] == (0x6d).to_byte(), content="true")
40
+ }