1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
|
// An ordinary line comment.
///|
/// A doc comment. `///|` above is the separator MoonBit's own formatter puts
/// between top-level items — it is a comment too, and coloured as one.
///
/// Everything below compiles. `moon check` reports no warnings and no errors,
/// which is the point: a colouring demo that does not build is a screenshot.
// ── literals ────────────────────────────────────────────────────────────────
///|
fn literals() -> Unit {
// Strings, and the four other quoted forms.
let plain = "an ordinary string"
let escaped = "a quote \" and a backslash \\ and a newline \n"
let unicode = "\u{1F31C} é"
let bytes = b"\xDE\xAD\xBE\xEF"
let ch = 'x'
let escaped_char = '\n'
let byte_char = b'A'
// Interpolation is one string span, brace to brace: the expression inside is
// deliberately not coloured as code.
let name = "MoonBit"
let interpolated = "hello \{name}, and \{1 + 2}"
// The one line in this file the editor colours *wrongly*, kept on purpose.
// A string nested inside an interpolation ends the outer literal as far as
// the scanner is concerned, so `yes` and `no` below come out as identifiers
// rather than as part of the string. The compiler disagrees, and is right;
// finding the real end needs the parser. See reference/languages.md.
let nested = "answer: \{if true { "yes" } else { "no" }}"
// A multi-line string is a run of lines, each complete in itself. #| is
// literal; $| interpolates.
let raw =
#| {"note": "braces and \backslashes are literal here"}
#| second line
let woven =
$| the name again: \{name}
$| and arithmetic: \{6 * 7}
println(plain)
println(escaped)
println(unicode)
println("bytes: \{bytes.length()} of them")
println("chars: \{ch} \{escaped_char.to_int()} \{byte_char.to_int()}")
println(interpolated)
println(nested)
println(raw)
println(woven)
}
// ── numbers ─────────────────────────────────────────────────────────────────
///|
/// Every numeric form the grammar allows, including the suffixes — which are
/// upper case or they are not suffixes at all.
fn numbers() -> Unit {
let decimal = 1_000_000
let hexadecimal = 0xFF_FF
let octal = 0o17
let binary = 0b1010_1010
let double = 1.5
let trailing_point = 1.0
let exponent = 1.5e-3
let hex_float = 0x1.8p3
let unsigned : UInt = 42U
let long : Int64 = 42L
let unsigned_long : UInt64 = 42UL
let big : BigInt = 42N
let single : Float = 1.0F
// An integer ends before `..`, so this is 1, then ..=, then 5 — not the
// double `1.` followed by `.=5`.
let mut sum = 0
for i in 1..<=5 {
sum = sum + i
}
for i in 0..<3 {
sum = sum - i
}
println("\{decimal} \{hexadecimal} \{octal} \{binary}")
println("\{double} \{trailing_point} \{exponent} \{hex_float}")
println("\{unsigned} \{long} \{unsigned_long} \{big} \{single}")
println("range sum: \{sum}")
}
// ── types, traits, and the case rule ────────────────────────────────────────
///|
/// A capitalised name can only be a type, a trait or a constructor — that is a
/// lexical rule in MoonBit, not a convention, so the scanner needs no table of
/// built-in type names.
pub(all) struct Point {
x : Int
y : Int
} derive(Eq, Debug)
///|
pub(all) enum Shape {
Dot
Line(Point, Point)
Poly(Array[Point])
} derive(Debug)
///|
pub(open) trait Describe {
fn describe(Self) -> String
}
///|
pub impl Describe for Point with fn describe(self) {
"Point(\{self.x}, \{self.y})"
}
///|
pub impl Show for Point with fn output(self, logger) {
logger.write_string(self.describe())
}
///|
/// `extend` promotes a trait's method onto the type, so `p.describe()` works
/// as well as `Describe::describe(p)`.
extend Point with Describe::{describe}
///|
/// A labelled argument with a default: the call site may leave it out.
fn walk(path : Array[Point], closed? : Bool = false) -> Int {
let steps = path.length()
if closed {
steps
} else {
steps - 1
}
}
// ── control flow ────────────────────────────────────────────────────────────
///|
/// Raised when a shape has no points to speak of.
pub suberror Empty
///|
fn corners(shape : Shape) -> Int raise Empty {
match shape {
Dot => 1
Line(_, _) => 2
Poly(points) => {
guard points.length() > 0 else { raise Empty }
points.length()
}
}
}
///|
fn control() -> Unit {
let square : Array[Point] = [
{ x: 0, y: 0, },
{ x: 1, y: 0, },
{ x: 1, y: 1, },
{ x: 0, y: 1, },
]
println("open path steps: \{walk(square)}")
println("closed path steps: \{walk(square, closed=true)}")
for shape in [Dot, Line({ x: 0, y: 0, }, { x: 1, y: 1, }), Poly(square)] {
debug(shape)
println(" … has \{corners(shape)} corner(s)") catch {
Empty => println(" … has no corners")
}
}
// A `for` loop carrying accumulators, with `break` to return a value and
// `continue` to go round again. (The older `loop (a, b) { … }` form is
// deprecated; this is what replaced it.)
let counted = for i = 0, acc = 0 {
if i >= 10 {
break acc
} else if i % 2 == 0 {
continue i + 1, acc + i
} else {
continue i + 1, acc
}
}
println("even numbers below ten add to \{counted}")
// while, with a mutable binding.
let mut countdown = 3
while countdown > 0 {
countdown = countdown - 1
}
println("countdown reached \{countdown}")
// Option and Result, and the four constructors the language's readers know.
let found : Int? = Some(7)
let missing : Int? = None
let good : Result[Int, String] = Ok(1)
let bad : Result[Int, String] = Err("nope")
// Option and Result are printed with `debug`: interpolating them would go
// through Show, which the toolchain now steers away from for debugging.
debug(found)
debug(missing)
debug(good)
debug(bad)
println("truth values: \{true} \{false}")
// A tuple, and its accessor.
let pair = (1, "one")
println("pair.0 = \{pair.0}, pair.1 = \{pair.1}")
// The pipe operator.
let piped = [3, 1, 2] |> sorted
println("piped:")
debug(piped)
}
///|
fn sorted(xs : Array[Int]) -> Array[Int] {
let copy = xs.copy()
copy.sort()
copy
}
// ── attributes ──────────────────────────────────────────────────────────────
///|
/// An attribute takes the whole line: after the dotted name, everything up to
/// the newline is its raw payload.
#deprecated("kept only so the colouring has something to show")
pub fn old_name() -> Int {
1
}
///|
/// A user-defined attribute has a namespace and is ignored by the compiler.
#custom.note(kind="demo", enabled=true)
fn annotated() -> Int {
2
}
// ── the entry point ─────────────────────────────────────────────────────────
///|
fn main {
literals()
println("")
numbers()
println("")
control()
println("")
println("annotated() = \{annotated()}")
}
|