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