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.
- package/.agents/skills/moonbit-agent-guide/LICENSE +202 -0
- package/.agents/skills/moonbit-agent-guide/SKILL.mbt.md +1126 -0
- package/.agents/skills/moonbit-agent-guide/SKILL.md +1126 -0
- package/.agents/skills/moonbit-agent-guide/ide.md +116 -0
- package/.agents/skills/moonbit-agent-guide/references/advanced-moonbit-build.md +106 -0
- package/.agents/skills/moonbit-agent-guide/references/moonbit-language-fundamentals.mbt.md +422 -0
- package/.agents/skills/moonbit-agent-guide/references/moonbit-language-fundamentals.md +422 -0
- package/.agents/skills/moonbit-practice/SKILL.md +258 -0
- package/.agents/skills/moonbit-practice/assets/ci.yaml +25 -0
- package/.agents/skills/moonbit-practice/reference/agents.md +1469 -0
- package/.agents/skills/moonbit-practice/reference/configuration.md +228 -0
- package/.agents/skills/moonbit-practice/reference/ffi.md +229 -0
- package/.agents/skills/moonbit-practice/reference/ide.md +189 -0
- package/.agents/skills/moonbit-practice/reference/performance.md +217 -0
- package/.agents/skills/moonbit-practice/reference/refactor.md +154 -0
- package/.agents/skills/moonbit-practice/reference/stdlib.md +351 -0
- package/.agents/skills/moonbit-practice/reference/testing.md +228 -0
- package/.agents/skills/moonbit-refactoring/LICENSE +21 -0
- package/.agents/skills/moonbit-refactoring/SKILL.md +323 -0
- package/.githooks/README.md +23 -0
- package/.githooks/pre-commit +3 -0
- package/.github/workflows/copilot-setup-steps.yml +40 -0
- package/.turbo/turbo-typecheck.log +2 -0
- package/AGENTS.md +91 -0
- package/LICENSE +21 -0
- package/PLAN.md +64 -0
- package/README.mbt.md +77 -0
- package/README.md +84 -0
- package/TODO.md +120 -0
- package/a.png +0 -0
- package/benchmarks/calc.bench.ts +144 -0
- package/benchmarks/draw.bench.ts +215 -0
- package/benchmarks/load.bench.ts +28 -0
- package/benchmarks/render.bench.ts +53 -0
- package/benchmarks/run.bench.ts +8 -0
- package/benchmarks/types.d.ts +15 -0
- package/docs/scratch-vm-specs/eventloop.md +103 -0
- package/docs/scratch-vm-specs/moonscratch-time-separation.md +50 -0
- package/index.html +91 -0
- package/js/AGENTS.md +5 -0
- package/js/a.ts +52 -0
- package/js/assets/AGENTS.md +5 -0
- package/js/assets/base64.test.ts +14 -0
- package/js/assets/base64.ts +21 -0
- package/js/assets/build-asset.test.ts +26 -0
- package/js/assets/build-asset.ts +28 -0
- package/js/assets/create.test.ts +142 -0
- package/js/assets/create.ts +122 -0
- package/js/assets/index.test.ts +15 -0
- package/js/assets/index.ts +2 -0
- package/js/assets/types.ts +26 -0
- package/js/assets/validation.test.ts +34 -0
- package/js/assets/validation.ts +25 -0
- package/js/assets.test.ts +14 -0
- package/js/assets.ts +1 -0
- package/js/index.test.ts +26 -0
- package/js/index.ts +3 -0
- package/js/render/index.test.ts +65 -0
- package/js/render/index.ts +13 -0
- package/js/render/sharp.ts +87 -0
- package/js/render/svg.ts +68 -0
- package/js/render/types.ts +35 -0
- package/js/render/utils.ts +108 -0
- package/js/render/webgl.ts +274 -0
- package/js/sharp-optional.d.ts +16 -0
- package/js/test/helpers.ts +116 -0
- package/js/test/hikkaku-sample.test.ts +37 -0
- package/js/test/rubik-components.input-motion.test.ts +60 -0
- package/js/test/rubik-components.lists.test.ts +49 -0
- package/js/test/rubik-components.operators.test.ts +104 -0
- package/js/test/rubik-components.pen.test.ts +112 -0
- package/js/test/rubik-components.procedures-loops.test.ts +72 -0
- package/js/test/rubik-components.variables-branches.test.ts +57 -0
- package/js/test/rubik-components.visibility-entry.test.ts +31 -0
- package/js/test/test-projects.ts +598 -0
- package/js/test/variable.ts +200 -0
- package/js/test/warp.test.ts +59 -0
- package/js/vm/AGENTS.md +6 -0
- package/js/vm/README.md +183 -0
- package/js/vm/bindings.test.ts +13 -0
- package/js/vm/bindings.ts +5 -0
- package/js/vm/compare-operators.test.ts +145 -0
- package/js/vm/constants.test.ts +11 -0
- package/js/vm/constants.ts +4 -0
- package/js/vm/effect-guards.test.ts +68 -0
- package/js/vm/effect-guards.ts +44 -0
- package/js/vm/factory.test.ts +486 -0
- package/js/vm/factory.ts +615 -0
- package/js/vm/headless-vm.test.ts +131 -0
- package/js/vm/headless-vm.ts +342 -0
- package/js/vm/index.test.ts +28 -0
- package/js/vm/index.ts +5 -0
- package/js/vm/internal-types.ts +32 -0
- package/js/vm/json.test.ts +40 -0
- package/js/vm/json.ts +273 -0
- package/js/vm/normalize.test.ts +48 -0
- package/js/vm/normalize.ts +65 -0
- package/js/vm/options.test.ts +30 -0
- package/js/vm/options.ts +55 -0
- package/js/vm/pen-transparency.test.ts +115 -0
- package/js/vm/program-wasm.ts +322 -0
- package/js/vm/scheduler-render.test.ts +401 -0
- package/js/vm/scratch-assets.test.ts +136 -0
- package/js/vm/scratch-assets.ts +202 -0
- package/js/vm/types.ts +358 -0
- package/js/vm/value-guards.test.ts +25 -0
- package/js/vm/value-guards.ts +18 -0
- package/moon.mod.json +10 -0
- package/package.json +33 -0
- package/scripts/preinstall.ts +4 -0
- package/src/AGENTS.md +6 -0
- package/src/api.mbt +161 -0
- package/src/api_aot_commands.mbt +184 -0
- package/src/api_effects_json.mbt +72 -0
- package/src/api_options.mbt +60 -0
- package/src/api_program_wasm.mbt +1647 -0
- package/src/api_program_wat.mbt +2206 -0
- package/src/api_snapshot_json.mbt +44 -0
- package/src/cmd/AGENTS.md +5 -0
- package/src/cmd/main/AGENTS.md +5 -0
- package/src/cmd/main/main.mbt +29 -0
- package/src/cmd/main/moon.pkg +7 -0
- package/src/cmd/main/pkg.generated.mbti +13 -0
- package/src/json_helpers.mbt +176 -0
- package/src/moon.pkg +65 -0
- package/src/moonscratch.mbt +3 -0
- package/src/moonscratch_wbtest.mbt +40 -0
- package/src/parser_sb3.mbt +890 -0
- package/src/pkg.generated.mbti +479 -0
- package/src/runtime_eval.mbt +2844 -0
- package/src/runtime_exec.mbt +3850 -0
- package/src/runtime_render.mbt +2550 -0
- package/src/runtime_state.mbt +870 -0
- package/src/test/AGENTS.md +3 -0
- package/src/test/projects/AGENTS.md +6 -0
- package/src/test/projects/moon.pkg +4 -0
- package/src/test/projects/moonscratch_compat_test.mbt +642 -0
- package/src/test/projects/moonscratch_core_test.mbt +1332 -0
- package/src/test/projects/moonscratch_runtime_test.mbt +1087 -0
- package/src/test/projects/pkg.generated.mbti +13 -0
- package/src/test/projects/test_support.mbt +35 -0
- package/src/types_effects.mbt +20 -0
- package/src/types_error.mbt +4 -0
- package/src/types_options.mbt +31 -0
- package/src/types_runtime_structs.mbt +254 -0
- package/src/types_vm.mbt +109 -0
- package/tsconfig.json +29 -0
- package/viewer/index.ts +399 -0
- package/viewer/vite.d.ts +1 -0
- package/viewer/worker.ts +161 -0
- package/vite.config.ts +11 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
## Code Navigation with `moon ide`
|
|
2
|
+
|
|
3
|
+
**ALWAYS use `moon ide` for code navigation in MoonBit projects instead of manual file searching, grep, or semantic search.**
|
|
4
|
+
|
|
5
|
+
This tool provides two essential commands for precise code exploration:
|
|
6
|
+
|
|
7
|
+
### Core Commands
|
|
8
|
+
|
|
9
|
+
- `moon ide goto-definition` - Find where a symbol is defined
|
|
10
|
+
- `moon ide find-references` - Find all usages of a symbol
|
|
11
|
+
|
|
12
|
+
### Query System
|
|
13
|
+
|
|
14
|
+
Symbol lookup uses a two-part query system for precise results:
|
|
15
|
+
|
|
16
|
+
#### 1. Symbol Name Query (`-query`)
|
|
17
|
+
|
|
18
|
+
Fuzzy search for symbol names with package filtering support:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Find any symbol named 'symbol'
|
|
22
|
+
moon ide goto-definition -query 'symbol'
|
|
23
|
+
|
|
24
|
+
# Find methods of a specific type
|
|
25
|
+
moon ide goto-definition -query 'Type::method'
|
|
26
|
+
|
|
27
|
+
# Find trait method implementations
|
|
28
|
+
moon ide goto-definition -query 'Trait for Type with method'
|
|
29
|
+
|
|
30
|
+
# Find symbol in specific package using @pkg prefix
|
|
31
|
+
moon ide goto-definition -query '@moonbitlang/x encode'
|
|
32
|
+
|
|
33
|
+
# Find symbol in multiple packages (searches in pkg1 OR pkg2)
|
|
34
|
+
moon ide goto-definition -query '@username/mymodule/pkg1 @username/mymodule/pkg2 helper'
|
|
35
|
+
|
|
36
|
+
# Find symbol in nested package
|
|
37
|
+
moon ide goto-definition -query '@username/mymodule/mypkg helper'
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Supported symbols**: functions, constants, let bindings, types, structs, enums, traits
|
|
41
|
+
|
|
42
|
+
**Package filtering**: Prefix your query with `@package_name` to scope the search. Multiple `@pkg` prefixes create an OR condition.
|
|
43
|
+
|
|
44
|
+
#### 2. Tag-based Filtering (`-tags`)
|
|
45
|
+
|
|
46
|
+
Pre-filter symbols by characteristics before name matching:
|
|
47
|
+
|
|
48
|
+
**Visibility tags**:
|
|
49
|
+
|
|
50
|
+
- `pub` - Public symbols
|
|
51
|
+
- `pub all` - Public structs with all public fields
|
|
52
|
+
- `pub open` - Public traits with all methods public
|
|
53
|
+
- `priv` - Private symbols
|
|
54
|
+
|
|
55
|
+
**Symbol type tags**:
|
|
56
|
+
|
|
57
|
+
- `type` - Type definitions (struct, enum, typealias, abstract)
|
|
58
|
+
- `error` - Error type definitions
|
|
59
|
+
- `enum` - Enum definitions and variants
|
|
60
|
+
- `struct` - Struct definitions
|
|
61
|
+
- `alias` - Type/function/trait aliases
|
|
62
|
+
- `let` - Top-level let bindings
|
|
63
|
+
- `const` - Constant definitions
|
|
64
|
+
- `fn` - Function definitions
|
|
65
|
+
- `trait` - Trait definitions
|
|
66
|
+
- `impl` - Trait implementations
|
|
67
|
+
- `test` - Named test functions
|
|
68
|
+
|
|
69
|
+
**Combine tags with logical operators**:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Public functions only
|
|
73
|
+
moon ide goto-definition -tags 'pub fn' -query 'my_func'
|
|
74
|
+
|
|
75
|
+
# Functions or constants
|
|
76
|
+
moon ide goto-definition -tags 'fn | const' -query 'helper'
|
|
77
|
+
|
|
78
|
+
# Public functions or constants
|
|
79
|
+
moon ide goto-definition -tags 'pub (fn | const)' -query 'api'
|
|
80
|
+
|
|
81
|
+
# Public types or traits
|
|
82
|
+
moon ide goto-definition -tags 'pub (type | trait)' -query 'MyType'
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Practical Examples
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# Find public function definition
|
|
89
|
+
moon ide goto-definition -tags 'pub fn' -query 'maximum'
|
|
90
|
+
|
|
91
|
+
# Find all references to a struct
|
|
92
|
+
moon ide find-references -tags 'struct' -query 'Rectangle'
|
|
93
|
+
|
|
94
|
+
# Find trait implementations
|
|
95
|
+
moon ide goto-definition -tags 'impl' -query 'Show for MyType'
|
|
96
|
+
|
|
97
|
+
# Find errors in specific package
|
|
98
|
+
moon ide goto-definition -tags 'error' -query '@mymodule/parser ParseError'
|
|
99
|
+
|
|
100
|
+
# Find symbol across multiple packages
|
|
101
|
+
moon ide goto-definition -query '@moonbitlang/x @moonbitlang/core encode'
|
|
102
|
+
|
|
103
|
+
# Combine package filtering with tags
|
|
104
|
+
moon ide goto-definition -tags 'pub fn' -query '@username/myapp helper'
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Query Processing
|
|
108
|
+
|
|
109
|
+
The tool processes queries in this order:
|
|
110
|
+
|
|
111
|
+
1. Filter symbols by `-tags` conditions
|
|
112
|
+
2. Extract package scope from `@pkg` prefixes in `-query`
|
|
113
|
+
3. Fuzzy match remaining symbols by name
|
|
114
|
+
4. Return top 3 best matches with location information
|
|
115
|
+
|
|
116
|
+
**Best Practice**: Start with `-tags` to reduce noise, then use `@pkg` prefixes in `-query` to scope by package for precise navigation.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
## Conditional Compilation
|
|
2
|
+
|
|
3
|
+
Target specific backends/modes in `moon.pkg.json`:
|
|
4
|
+
|
|
5
|
+
```json
|
|
6
|
+
{
|
|
7
|
+
"targets": {
|
|
8
|
+
"wasm_only.mbt": ["wasm"],
|
|
9
|
+
"js_only.mbt": ["js"],
|
|
10
|
+
"debug_only.mbt": ["debug"],
|
|
11
|
+
"wasm_or_js.mbt": ["wasm", "js"], // for wasm or js backend
|
|
12
|
+
"not_js.mbt": ["not", "js"], // for nonjs backend
|
|
13
|
+
"complex.mbt": ["or", ["and", "wasm", "release"], ["and", "js", "debug"]] // more complex conditions
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Available conditions:**
|
|
19
|
+
|
|
20
|
+
- **Backends**: `"wasm"`, `"wasm-gc"`, `"js"`, `"native"`
|
|
21
|
+
- **Build modes**: `"debug"`, `"release"`
|
|
22
|
+
- **Logical operators**: `"and"`, `"or"`, `"not"`
|
|
23
|
+
|
|
24
|
+
## Link Configuration
|
|
25
|
+
|
|
26
|
+
### Basic Linking
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"link": true, // Enable linking for this package
|
|
31
|
+
// OR for advanced cases:
|
|
32
|
+
"link": {
|
|
33
|
+
"wasm": {
|
|
34
|
+
"exports": ["hello", "foo:bar"], // Export functions
|
|
35
|
+
"heap-start-address": 1024, // Memory layout
|
|
36
|
+
"import-memory": {
|
|
37
|
+
// Import external memory
|
|
38
|
+
"module": "env",
|
|
39
|
+
"name": "memory"
|
|
40
|
+
},
|
|
41
|
+
"export-memory-name": "memory" // Export memory with name
|
|
42
|
+
},
|
|
43
|
+
"wasm-gc": {
|
|
44
|
+
"exports": ["hello"],
|
|
45
|
+
"use-js-builtin-string": true, // JS String Builtin support
|
|
46
|
+
"imported-string-constants": "_" // String namespace
|
|
47
|
+
},
|
|
48
|
+
"js": {
|
|
49
|
+
"exports": ["hello"],
|
|
50
|
+
"format": "esm" // "esm", "cjs", or "iife"
|
|
51
|
+
},
|
|
52
|
+
"native": {
|
|
53
|
+
"cc": "gcc", // C compiler
|
|
54
|
+
"cc-flags": "-O2 -DMOONBIT", // Compile flags
|
|
55
|
+
"cc-link-flags": "-s" // Link flags
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Warning Control
|
|
62
|
+
|
|
63
|
+
Disable specific warnings in `moon.mod.json` or `moon.pkg.json`:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"warn-list": "-2-29" // Disable unused variable (2) & unused package (29)
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Common warning numbers:**
|
|
72
|
+
|
|
73
|
+
- `1` - Unused function
|
|
74
|
+
- `2` - Unused variable
|
|
75
|
+
- `11` - Partial pattern matching
|
|
76
|
+
- `12` - Unreachable code
|
|
77
|
+
- `29` - Unused package
|
|
78
|
+
|
|
79
|
+
Use `moonc build-package -warn-help` to see all available warnings.
|
|
80
|
+
|
|
81
|
+
## Pre-build Commands
|
|
82
|
+
|
|
83
|
+
Embed external files as MoonBit code:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"pre-build": [
|
|
88
|
+
{
|
|
89
|
+
"input": "data.txt",
|
|
90
|
+
"output": "embedded.mbt",
|
|
91
|
+
"command": ":embed -i $input -o $output --name data --text"
|
|
92
|
+
},
|
|
93
|
+
... // more embed commands
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Generated code example:
|
|
99
|
+
|
|
100
|
+
```mbt check
|
|
101
|
+
///|
|
|
102
|
+
let data : String =
|
|
103
|
+
#|hello,
|
|
104
|
+
#|world
|
|
105
|
+
#|
|
|
106
|
+
```
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
# MoonBit Language Fundamentals
|
|
2
|
+
|
|
3
|
+
## Quick reference:
|
|
4
|
+
|
|
5
|
+
```mbt check
|
|
6
|
+
///|
|
|
7
|
+
/// comments doc string
|
|
8
|
+
pub fn sum(x : Int, y : Int) -> Int {
|
|
9
|
+
x + y
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
///|
|
|
13
|
+
struct Rect {
|
|
14
|
+
width : Int
|
|
15
|
+
height : Int
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
///|
|
|
19
|
+
fn Rect::area(self : Rect) -> Int {
|
|
20
|
+
self.width * self.height
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
///|
|
|
24
|
+
pub impl Show for Rect with output(_self, logger) {
|
|
25
|
+
logger.write_string("Rect")
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
///|
|
|
29
|
+
enum MyOption {
|
|
30
|
+
MyNone
|
|
31
|
+
MySome(Int)
|
|
32
|
+
} derive(Show, ToJson, Eq, Compare)
|
|
33
|
+
|
|
34
|
+
///|
|
|
35
|
+
/// match + loops are expressions
|
|
36
|
+
test "everything is expression in MoonBit" {
|
|
37
|
+
// tuple
|
|
38
|
+
let (n, opt) = (1, MySome(2))
|
|
39
|
+
// if expressions return values
|
|
40
|
+
let msg : String = if n > 0 { "pos" } else { "non-pos" }
|
|
41
|
+
let res = match opt {
|
|
42
|
+
MySome(x) => {
|
|
43
|
+
inspect(x, content="2")
|
|
44
|
+
1
|
|
45
|
+
}
|
|
46
|
+
MyNone => 0
|
|
47
|
+
}
|
|
48
|
+
let status : Result[Int, String] = Ok(10)
|
|
49
|
+
// match expressions return values
|
|
50
|
+
let description = match status {
|
|
51
|
+
Ok(value) => "Success: \{value}"
|
|
52
|
+
Err(error) => "Error: \{error}"
|
|
53
|
+
}
|
|
54
|
+
let array = [1, 2, 3, 4, 5]
|
|
55
|
+
let mut i = 0 // mutable bindings (local only, globals are immutable)
|
|
56
|
+
let target = 3
|
|
57
|
+
// loops return values with 'break'
|
|
58
|
+
let found : Int? = while i < array.length() {
|
|
59
|
+
if array[i] == target {
|
|
60
|
+
break Some(i) // Exit with value
|
|
61
|
+
}
|
|
62
|
+
i = i + 1
|
|
63
|
+
} else { // Value when loop completes normally
|
|
64
|
+
None
|
|
65
|
+
}
|
|
66
|
+
assert_eq(found, Some(2)) // Found at index 2
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
///|
|
|
70
|
+
/// global bindings
|
|
71
|
+
pub let my_name : String = "MoonBit"
|
|
72
|
+
|
|
73
|
+
///|
|
|
74
|
+
pub const PI : Double = 3.14159 // constants use UPPER_SNAKE or PascalCase
|
|
75
|
+
|
|
76
|
+
///|
|
|
77
|
+
pub fn maximum(xs : Array[Int]) -> Int raise {
|
|
78
|
+
// Toplevel functions are *mutually recursive* by default
|
|
79
|
+
// `raise` annotation means the function would raise any Error
|
|
80
|
+
// Only add `raise XXError` when you do need track the specific error type
|
|
81
|
+
match xs {
|
|
82
|
+
[] => fail("Empty array") // fail() is built-in for generic errors
|
|
83
|
+
[x] => x
|
|
84
|
+
// pattern match over array, the `.. rest` is a rest pattern
|
|
85
|
+
// it is of type `ArrayView[Int]` which is a slice
|
|
86
|
+
[x, .. rest] => {
|
|
87
|
+
let mut max_val = x // `mut` only allowed in local bindings
|
|
88
|
+
for y in rest {
|
|
89
|
+
if y > max_val {
|
|
90
|
+
max_val = y
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
max_val // return can be omitted if the last expression is the return value
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
///|
|
|
99
|
+
/// pub(all) means it can be both read and created outside the package
|
|
100
|
+
pub(all) struct Point {
|
|
101
|
+
x : Int
|
|
102
|
+
mut y : Int
|
|
103
|
+
} derive(Show, ToJson)
|
|
104
|
+
|
|
105
|
+
///|
|
|
106
|
+
pub enum MyResult[T, E] {
|
|
107
|
+
MyOk(T) // semicolon `;` is optional when we have a newline
|
|
108
|
+
MyErr(E) // Enum variants must start uppercase
|
|
109
|
+
} derive(Show, Eq, ToJson)
|
|
110
|
+
// pub means it can only be pattern matched outside the package
|
|
111
|
+
// but it can not be created outside the package, use `pub(all)` otherwise
|
|
112
|
+
|
|
113
|
+
///|
|
|
114
|
+
/// pub (open) means the trait can be implemented for outside packages
|
|
115
|
+
pub(open) trait Comparable {
|
|
116
|
+
compare(Self, Self) -> Int // `Self` refers to the implementing type
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
///|
|
|
120
|
+
test "inspect test" {
|
|
121
|
+
let result = sum(1, 2)
|
|
122
|
+
inspect(result, content="3")
|
|
123
|
+
// The `content` can be auto-corrected by running `moon test --update`
|
|
124
|
+
let point = Point::{ x: 10, y: 20 }
|
|
125
|
+
// For complex structures, use @json.inspect for better readability:
|
|
126
|
+
@json.inspect(point, content={ "x": 10, "y": 20 })
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Complex Types
|
|
131
|
+
|
|
132
|
+
```mbt check
|
|
133
|
+
///|
|
|
134
|
+
pub type UserId = Int // Int is aliased to UserId - like symlink
|
|
135
|
+
|
|
136
|
+
///|
|
|
137
|
+
/// Tuple-struct for callback
|
|
138
|
+
pub struct Handler((String) -> Unit) // A newtype wrapper
|
|
139
|
+
|
|
140
|
+
///|
|
|
141
|
+
/// Tuple-struct syntax for single-field newtypes
|
|
142
|
+
struct Meters(Int) // Tuple-struct syntax
|
|
143
|
+
|
|
144
|
+
///|
|
|
145
|
+
let distance : Meters = Meters(100)
|
|
146
|
+
|
|
147
|
+
///|
|
|
148
|
+
let raw : Int = distance.0 // Access first field with .0
|
|
149
|
+
|
|
150
|
+
///|
|
|
151
|
+
struct Addr {
|
|
152
|
+
host : String
|
|
153
|
+
port : Int
|
|
154
|
+
} derive(Show, Eq, ToJson, FromJson)
|
|
155
|
+
|
|
156
|
+
///|
|
|
157
|
+
/// Structural types with literal syntax
|
|
158
|
+
let config : Addr = Addr::{
|
|
159
|
+
// `Type::` can be omitted since the type is already known
|
|
160
|
+
host: "localhost",
|
|
161
|
+
port: 8080,
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Common Derivable Traits
|
|
168
|
+
|
|
169
|
+
Most types can automatically derive standard traits using the `derive(...)` syntax:
|
|
170
|
+
|
|
171
|
+
- **`Show`** - Enables `to_string()` and string interpolation with `\{value}`
|
|
172
|
+
- **`Eq`** - Enables `==` and `!=` equality operators
|
|
173
|
+
- **`Compare`** - Enables `<`, `>`, `<=`, `>=` comparison operators
|
|
174
|
+
- **`ToJson`** - Enables `@json.inspect()` for readable test output
|
|
175
|
+
- **`Hash`** - Enables use as Map keys
|
|
176
|
+
|
|
177
|
+
```mbt check
|
|
178
|
+
///|
|
|
179
|
+
struct Coordinate {
|
|
180
|
+
x : Int
|
|
181
|
+
y : Int
|
|
182
|
+
} derive(Show, Eq, ToJson)
|
|
183
|
+
|
|
184
|
+
///|
|
|
185
|
+
enum Status {
|
|
186
|
+
Active
|
|
187
|
+
Inactive
|
|
188
|
+
} derive(Show, Eq, Compare)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Best practice**: Always derive `Show` and `Eq` for data types. Add `ToJson` if you plan to test them with `@json.inspect()`.
|
|
192
|
+
|
|
193
|
+
## Reference Semantics by Default
|
|
194
|
+
|
|
195
|
+
MoonBit passes most types by reference semantically (the optimizer may copy
|
|
196
|
+
immutables):
|
|
197
|
+
|
|
198
|
+
```mbt check
|
|
199
|
+
///|
|
|
200
|
+
/// Structs with 'mut' fields are always passed by reference
|
|
201
|
+
struct Counter {
|
|
202
|
+
mut value : Int
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
///|
|
|
206
|
+
fn increment(c : Counter) -> Unit {
|
|
207
|
+
c.value += 1 // Modifies the original
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
///|
|
|
211
|
+
/// Arrays and Maps are mutable references
|
|
212
|
+
fn modify_array(arr : Array[Int]) -> Unit {
|
|
213
|
+
arr[0] = 999 // Modifies original array
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
///|
|
|
217
|
+
test "reference semantics" {
|
|
218
|
+
let counter : Ref[Int] = Ref::{ val: 0 }
|
|
219
|
+
counter.val += 1
|
|
220
|
+
assert_true(counter.val is 1)
|
|
221
|
+
let arr : Array[Int] = [1, 2, 3] // unlike Rust, no `mut` keyword needed
|
|
222
|
+
modify_array(arr)
|
|
223
|
+
assert_true(arr[0] is 999)
|
|
224
|
+
let mut x = 3 // `mut` neeed for re-assignment to the bindings
|
|
225
|
+
x += 2
|
|
226
|
+
assert_true(x is 5)
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Pattern Matching
|
|
231
|
+
|
|
232
|
+
```mbt check
|
|
233
|
+
///|
|
|
234
|
+
#warnings("-unused_value")
|
|
235
|
+
test "pattern match over Array, struct and StringView" {
|
|
236
|
+
let arr : Array[Int] = [10, 20, 25, 30]
|
|
237
|
+
match arr {
|
|
238
|
+
[] => ... // empty array
|
|
239
|
+
[single] => ... // single element
|
|
240
|
+
[first, .. middle, rest] => {
|
|
241
|
+
let _ : ArrayView[Int] = middle // middle is ArrayView[Int]
|
|
242
|
+
assert_true(first is 10 && middle is [20, 25] && rest is 30)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
fn process_point(point : Point) -> Unit {
|
|
246
|
+
match point {
|
|
247
|
+
{ x: 0, y: 0 } => ...
|
|
248
|
+
{ x, y } if x == y => ...
|
|
249
|
+
{ x, .. } if x < 0 => ...
|
|
250
|
+
...
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/// StringView pattern matching for parsing
|
|
254
|
+
fn is_palindrome(s : StringView) -> Bool {
|
|
255
|
+
loop s {
|
|
256
|
+
[] | [_] => true
|
|
257
|
+
[a, .. rest, b] if a == b => continue rest
|
|
258
|
+
// a is of type Char, rest is of type StringView
|
|
259
|
+
_ => false
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Functional `loop` control flow
|
|
268
|
+
|
|
269
|
+
The `loop` construct is unique to MoonBit:
|
|
270
|
+
|
|
271
|
+
```mbt check
|
|
272
|
+
///|
|
|
273
|
+
/// Functional loop with pattern matching on loop variables
|
|
274
|
+
/// @list.List is from the standard library
|
|
275
|
+
fn sum_list(list : @list.List[Int]) -> Int {
|
|
276
|
+
loop (list, 0) {
|
|
277
|
+
(Empty, acc) => acc // Base case returns accumulator
|
|
278
|
+
(More(x, tail=rest), acc) => continue (rest, x + acc) // Recurse with new values
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
///|
|
|
283
|
+
/// Multiple loop variables with complex control flow
|
|
284
|
+
fn find_pair(arr : Array[Int], target : Int) -> (Int, Int)? {
|
|
285
|
+
loop (0, arr.length() - 1) {
|
|
286
|
+
(i, j) if i >= j => None
|
|
287
|
+
(i, j) => {
|
|
288
|
+
let sum = arr[i] + arr[j]
|
|
289
|
+
if sum == target {
|
|
290
|
+
Some((i, j)) // Found pair
|
|
291
|
+
} else if sum < target {
|
|
292
|
+
continue (i + 1, j) // Move left pointer
|
|
293
|
+
} else {
|
|
294
|
+
continue (i, j - 1) // Move right pointer
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**Note**: You must provide a payload to `loop`. If you want an infinite loop, use `while true { ... }` instead. The syntax `loop { ... }` without arguments is invalid.
|
|
302
|
+
|
|
303
|
+
## Methods and Traits
|
|
304
|
+
|
|
305
|
+
Methods use `Type::method_name` syntax, traits require explicit implementation:
|
|
306
|
+
|
|
307
|
+
```mbt check
|
|
308
|
+
///|
|
|
309
|
+
struct Rectangle {
|
|
310
|
+
width : Double
|
|
311
|
+
height : Double
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
///|
|
|
315
|
+
// Methods are prefixed with Type::
|
|
316
|
+
fn Rectangle::area(self : Rectangle) -> Double {
|
|
317
|
+
self.width * self.height
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
///|
|
|
321
|
+
/// Static methods don't need self
|
|
322
|
+
fn Rectangle::new(w : Double, h : Double) -> Rectangle {
|
|
323
|
+
{ width: w, height: h }
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
///|
|
|
327
|
+
/// Show trait now uses output(self, logger) for custom formatting
|
|
328
|
+
/// to_string() is automatically derived from this
|
|
329
|
+
pub impl Show for Rectangle with output(self, logger) {
|
|
330
|
+
logger.write_string("Rectangle(\{self.width}x\{self.height})")
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
///|
|
|
334
|
+
/// Traits can have non-object-safe methods
|
|
335
|
+
trait Named {
|
|
336
|
+
name() -> String // No 'self' parameter - not object-safe
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
///|
|
|
340
|
+
/// Trait bounds in generics
|
|
341
|
+
fn[T : Show + Named] describe(value : T) -> String {
|
|
342
|
+
"\{T::name()}: \{value.to_string()}"
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
///|
|
|
346
|
+
/// Trait implementation
|
|
347
|
+
impl Hash for Rectangle with hash_combine(self, hasher) {
|
|
348
|
+
hasher..combine(self.width)..combine(self.height)
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## Operator Overloading
|
|
353
|
+
|
|
354
|
+
MoonBit supports operator overloading through traits:
|
|
355
|
+
|
|
356
|
+
```mbt check
|
|
357
|
+
///|
|
|
358
|
+
struct Vector(Int, Int)
|
|
359
|
+
|
|
360
|
+
///|
|
|
361
|
+
/// Implement arithmetic operators
|
|
362
|
+
pub impl Add for Vector with add(self, other) {
|
|
363
|
+
Vector(self.0 + other.0, self.1 + other.1)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
///|
|
|
367
|
+
struct Person {
|
|
368
|
+
age : Int
|
|
369
|
+
} derive(Eq)
|
|
370
|
+
|
|
371
|
+
///|
|
|
372
|
+
/// Comparison operators
|
|
373
|
+
pub impl Compare for Person with compare(self, other) {
|
|
374
|
+
self.age.compare(other.age)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
///|
|
|
378
|
+
test "overloading" {
|
|
379
|
+
let v1 : Vector = Vector(1, 2)
|
|
380
|
+
let v2 : Vector = Vector(3, 4)
|
|
381
|
+
let _v3 : Vector = v1 + v2
|
|
382
|
+
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
## Access Control Modifiers
|
|
387
|
+
|
|
388
|
+
MoonBit has fine-grained visibility control:
|
|
389
|
+
|
|
390
|
+
```mbt check
|
|
391
|
+
///|
|
|
392
|
+
/// `fn` defaults to Private - only visible in current package
|
|
393
|
+
fn internal_helper() -> Unit {
|
|
394
|
+
...
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
///|
|
|
398
|
+
pub fn get_value() -> Int {
|
|
399
|
+
...
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
///|
|
|
403
|
+
// Struct (default) - type visible, implementation hidden
|
|
404
|
+
struct DataStructure {}
|
|
405
|
+
|
|
406
|
+
///|
|
|
407
|
+
/// `pub struct` defaults to readonly - can read, pattern match, but not create
|
|
408
|
+
pub struct Config {}
|
|
409
|
+
|
|
410
|
+
///|
|
|
411
|
+
/// Public all - full access
|
|
412
|
+
pub(all) struct Config2 {}
|
|
413
|
+
|
|
414
|
+
///|
|
|
415
|
+
/// Abstract trait (default) - cannot be implemented by
|
|
416
|
+
/// types outside this package
|
|
417
|
+
pub trait MyTrait {}
|
|
418
|
+
|
|
419
|
+
///|
|
|
420
|
+
/// Open for extension
|
|
421
|
+
pub(open) trait Extendable {}
|
|
422
|
+
```
|