plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
141b67a
— Peter John
2026-11-01T17:36:38+05:30
T3: record migration
- plum-checker/tests/error_union_tests.rs +4 -8
- plum-cli/tests/assert_once_test.rs +1 -2
- plum-core/tests/loader_test.rs +2 -4
- plum-examples/chains.plum +1 -2
- plum-examples/closures.plum +1 -2
- plum-examples/forms.plum +2 -4
- plum-examples/functions.plum +3 -6
- plum-examples/generics.plum +3 -6
- plum-examples/match.plum +1 -2
- plum-examples/methods.plum +2 -4
- plum-examples/oop_visitor.plum +4 -8
- plum-examples/operators.plum +2 -4
- plum-examples/optionals.plum +2 -4
- plum-examples/safe_navigation.plum +3 -6
- plum-examples/types.plum +10 -20
- plum-std/Buffer.plum +1 -2
- plum-std/Deque.plum +1 -2
- plum-std/Grid.plum +1 -2
- plum-std/Heap.plum +1 -2
- plum-std/Http.plum +1 -2
- plum-std/Json.plum +1 -2
- plum-std/List.plum +1 -2
- plum-std/Map.plum +2 -4
- plum-std/Random.plum +1 -2
- plum-std/Set.plum +1 -2
- plum-std/Str.plum +1 -2
- plum-std/Time.plum +3 -6
- plum-std/Uuid.plum +1 -2
- plum-tooling/tree-sitter-plum/test/aa.plum +1 -2
- plum-tooling/tree-sitter-plum/test/corpus/const.txt +10 -13
- plum-tooling/tree-sitter-plum/test/corpus/type.txt +26 -35
- plum-tooling/tree-sitter-plum/test/highlight/sample.plum +1 -2
plum-checker/tests/error_union_tests.rs
CHANGED
|
@@ -341,8 +341,7 @@ fun d(n: Int) -> !Str =
|
|
|
341
341
|
#[test]
|
|
342
342
|
fn safe_nav_on_error_union() {
|
|
343
343
|
let src = "\
|
|
344
|
-
enum User =
|
|
345
|
-
|
|
344
|
+
record User(name: Str)
|
|
346
345
|
|
|
347
346
|
fun load(n: Int) -> !User =
|
|
348
347
|
return error.NoUser
|
|
@@ -353,8 +352,7 @@ fun name(n: Int) -> !Str =
|
|
|
353
352
|
// Same set; `?.` flattens.
|
|
354
353
|
assert!(check(src).is_ok(), "{:?}", check(src));
|
|
355
354
|
let src2 = "\
|
|
356
|
-
enum User =
|
|
357
|
-
|
|
355
|
+
record User(name: Str)
|
|
358
356
|
|
|
359
357
|
fun load(n: Int) -> !User =
|
|
360
358
|
return error.NoUser
|
|
@@ -369,8 +367,7 @@ fun name(n: Int) -> Str =
|
|
|
369
367
|
fn pinned_set_vs_generic_unambiguous() {
|
|
370
368
|
// Generics next to a pinned set: the set comes first, in front of the `!`.
|
|
371
369
|
let src = "\
|
|
372
|
-
enum Box[T] =
|
|
373
|
-
|
|
370
|
+
record Box[T](item: T)
|
|
374
371
|
|
|
375
372
|
fun unbox(b: Box[Int]) -> error{Empty}!Box[Int] =
|
|
376
373
|
return error.Empty
|
|
@@ -412,8 +409,7 @@ fn null_is_absent_anywhere() {
|
|
|
412
409
|
// Constructor argument of type `Int?`: not an early return, and legal in a
|
|
413
410
|
// fn that returns a plain value.
|
|
414
411
|
let src = "\
|
|
415
|
-
enum Cfg =
|
|
416
|
-
|
|
412
|
+
record Cfg(port: Int?)
|
|
417
413
|
|
|
418
414
|
fun mk() -> Cfg =
|
|
419
415
|
Cfg(port: null)
|
plum-cli/tests/assert_once_test.rs
CHANGED
|
@@ -24,8 +24,7 @@ fn assert_operands_evaluated_once() {
|
|
|
24
24
|
import std/Int
|
|
25
25
|
import std/Float
|
|
26
26
|
|
|
27
|
-
enum Counter =
|
|
28
|
-
|
|
27
|
+
record Counter(n: Int) =
|
|
29
28
|
|
|
30
29
|
fun bump(self) -> Int =
|
|
31
30
|
self.n = self.n + 1
|
plum-core/tests/loader_test.rs
CHANGED
|
@@ -213,8 +213,7 @@ fn sameMethodNameOnDifferentReceiversAcrossFilesIsNotACollision() {
|
|
|
213
213
|
"\
|
|
214
214
|
module fixtures
|
|
215
215
|
|
|
216
|
-
enum Cat =
|
|
217
|
-
|
|
216
|
+
record Cat(age: Unit) =
|
|
218
217
|
|
|
219
218
|
fun length(self) -> Unit =
|
|
220
219
|
self.age
|
|
@@ -228,8 +227,7 @@ module fixtures
|
|
|
228
227
|
|
|
229
228
|
import cat
|
|
230
229
|
|
|
231
|
-
enum Box =
|
|
232
|
-
|
|
230
|
+
record Box(items: Unit) =
|
|
233
231
|
|
|
234
232
|
fun length(self) -> Unit =
|
|
235
233
|
self.items
|
plum-examples/chains.plum
CHANGED
|
@@ -24,8 +24,7 @@ import std/Seq
|
|
|
24
24
|
# No closure is allocated and there is no call_indirect: the wasm is the loop you
|
|
25
25
|
# would have written by hand. See plum-std/Seq.plum.
|
|
26
26
|
|
|
27
|
-
enum Soldier =
|
|
28
|
-
|
|
27
|
+
record Soldier(name: Str, team: Int, hp: Float, x: Float, y: Float)
|
|
29
28
|
|
|
30
29
|
fun dist2(ax: Float, ay: Float, bx: Float, by: Float) -> Float =
|
|
31
30
|
dx := ax - bx
|
plum-examples/closures.plum
CHANGED
|
@@ -32,8 +32,7 @@ fun useFloatClosure() -> Float =
|
|
|
32
32
|
offset + v
|
|
33
33
|
eachF(floatCb)
|
|
34
34
|
|
|
35
|
-
enum ClosureCat =
|
|
36
|
-
|
|
35
|
+
record ClosureCat(age: Int)
|
|
37
36
|
|
|
38
37
|
fun eachCat(cb: fn(ClosureCat) -> Int) -> Int =
|
|
39
38
|
cb(ClosureCat(age: 7))
|
plum-examples/forms.plum
CHANGED
|
@@ -12,8 +12,7 @@ import std/Seq
|
|
|
12
12
|
# thing is expanded at the call site, so there are no closures. See
|
|
13
13
|
# plum-std/Seq.plum and plum-core/src/forms.rs.
|
|
14
14
|
|
|
15
|
-
enum Fighter =
|
|
16
|
-
|
|
15
|
+
record Fighter(name: Str, hp: Int, dead: Bool)
|
|
17
16
|
|
|
18
17
|
fun sampleFighters() -> List[Fighter] =
|
|
19
18
|
List(Fighter(name: "a", hp: 10, dead: false), Fighter(name: "b", hp: 0, dead: true), Fighter(name: "c", hp: 7, dead: false))
|
|
@@ -57,8 +56,7 @@ fun tailForm(xs: List[Int]) -> Int =
|
|
|
57
56
|
|
|
58
57
|
|
|
59
58
|
# A type with a fun* method: `self` is bound to the receiver at the use site.
|
|
60
|
-
enum Countdown =
|
|
61
|
-
|
|
59
|
+
record Countdown(from: Int) =
|
|
62
60
|
|
|
63
61
|
fun* ticks(self) *Int =
|
|
64
62
|
i := self.from
|
plum-examples/functions.plum
CHANGED
|
@@ -45,8 +45,7 @@ fun factorial(x: Int) -> Int =
|
|
|
45
45
|
fun double(n: Int) -> Int =
|
|
46
46
|
n * 2
|
|
47
47
|
|
|
48
|
-
enum GenericBox[T] =
|
|
49
|
-
|
|
48
|
+
record GenericBox[T](value: T) =
|
|
50
49
|
|
|
51
50
|
fun getIntValue() -> T =
|
|
52
51
|
self.value
|
|
@@ -58,8 +57,7 @@ fun useGenericBox() -> Int =
|
|
|
58
57
|
fun identity(value: T) -> T =
|
|
59
58
|
value
|
|
60
59
|
|
|
61
|
-
enum GenericBox2[T] =
|
|
62
|
-
|
|
60
|
+
record GenericBox2[T](value: T) =
|
|
63
61
|
|
|
64
62
|
fun getValue() -> T =
|
|
65
63
|
self.value
|
|
@@ -89,8 +87,7 @@ fun strContentsOr(p: Parcel[Str], default: Int) -> Int =
|
|
|
89
87
|
Delivered(v) => 4
|
|
90
88
|
Lost => default
|
|
91
89
|
|
|
92
|
-
enum GenericBox3[T] =
|
|
93
|
-
|
|
90
|
+
record GenericBox3[T](value: T) =
|
|
94
91
|
|
|
95
92
|
fun getBoxValue() -> T =
|
|
96
93
|
self.value
|
plum-examples/generics.plum
CHANGED
|
@@ -45,8 +45,7 @@ fun readDeep(outer: List[List[Map[Str, Int]]], r: Int, c: Int, key: Str) -> erro
|
|
|
45
45
|
v := try m.get(key)
|
|
46
46
|
return v
|
|
47
47
|
|
|
48
|
-
enum Circle =
|
|
49
|
-
|
|
48
|
+
record Circle(r: Float) =
|
|
50
49
|
|
|
51
50
|
fun area(self) -> Float =
|
|
52
51
|
self.r * self.r * 3.0
|
|
@@ -54,8 +53,7 @@ enum Circle =
|
|
|
54
53
|
fun name(self) -> Str =
|
|
55
54
|
"circle"
|
|
56
55
|
|
|
57
|
-
enum Square =
|
|
58
|
-
|
|
56
|
+
record Square(s: Float) =
|
|
59
57
|
|
|
60
58
|
fun area(self) -> Float =
|
|
61
59
|
self.s * self.s
|
|
@@ -72,8 +70,7 @@ fun nameOf(s: T) -> Str =
|
|
|
72
70
|
# "Trait values": there are no traits, so the behavior a generic needs is passed
|
|
73
71
|
# explicitly as a record of functions (a "Num dictionary"). `num.add(a, b)` calls
|
|
74
72
|
# the closure stored in the `add` field (a method named `add` would win).
|
|
75
|
-
enum Num[T] =
|
|
76
|
-
|
|
73
|
+
record Num[T](add: fn(T, T) -> T, zero: T)
|
|
77
74
|
|
|
78
75
|
fun sumWith(xs: List[T], num: Num[T]) -> T =
|
|
79
76
|
acc := num.zero
|
plum-examples/match.plum
CHANGED
|
@@ -131,8 +131,7 @@ fun bothOptions(a: Reading[Int], b: Reading[Int]) -> Int =
|
|
|
131
131
|
Sample(x), Sample(y) => x + y
|
|
132
132
|
_, _ => 0
|
|
133
133
|
|
|
134
|
-
enum Point =
|
|
135
|
-
|
|
134
|
+
record Point(x: Int, y: Int) =
|
|
136
135
|
|
|
137
136
|
fun sum(self) -> Int =
|
|
138
137
|
match self
|
plum-examples/methods.plum
CHANGED
|
@@ -4,8 +4,7 @@ import std/Str
|
|
|
4
4
|
import std/Int
|
|
5
5
|
import std/Float
|
|
6
6
|
|
|
7
|
-
enum Cat =
|
|
8
|
-
|
|
7
|
+
record Cat(name: Str, age: Int) =
|
|
9
8
|
|
|
10
9
|
fun getAge(self) -> Int =
|
|
11
10
|
self.age
|
|
@@ -13,8 +12,7 @@ enum Cat =
|
|
|
13
12
|
fun birthday(self) -> Int =
|
|
14
13
|
self.age + 1
|
|
15
14
|
|
|
16
|
-
enum Wrapper =
|
|
17
|
-
|
|
15
|
+
record Wrapper(inner: Cat, tag: Int) =
|
|
18
16
|
|
|
19
17
|
fun innerAge(self) -> Int =
|
|
20
18
|
self.inner.age
|
plum-examples/oop_visitor.plum
CHANGED
|
@@ -37,8 +37,7 @@ enum Rating =
|
|
|
37
37
|
| Good(name: Str)
|
|
38
38
|
| Bad(name: Str)
|
|
39
39
|
|
|
40
|
-
enum FantasyBook =
|
|
41
|
-
|
|
40
|
+
record FantasyBook(title: Str, pages: Int, hasMythicalCreatures: Bool) =
|
|
42
41
|
|
|
43
42
|
fun collectInterestingInfo(self) -> Str =
|
|
44
43
|
if self.hasMythicalCreatures
|
|
@@ -46,8 +45,7 @@ enum FantasyBook =
|
|
|
46
45
|
else
|
|
47
46
|
"Fantasy book \"{self.title}\" has no mythical creatures"
|
|
48
47
|
|
|
49
|
-
enum ScifiBook =
|
|
50
|
-
|
|
48
|
+
record ScifiBook(title: Str, pages: Int, theme: Str) =
|
|
51
49
|
|
|
52
50
|
fun collectInterestingInfo(self) -> Str =
|
|
53
51
|
if self.theme == "space exploration"
|
|
@@ -55,8 +53,7 @@ enum ScifiBook =
|
|
|
55
53
|
else
|
|
56
54
|
"Scifi book \"{self.title}\" has theme {self.theme}"
|
|
57
55
|
|
|
58
|
-
enum ChildrensTaleBook =
|
|
59
|
-
|
|
56
|
+
record ChildrensTaleBook(title: Str, pages: Int, moralLesson: Str) =
|
|
60
57
|
|
|
61
58
|
fun collectInterestingInfo(self) -> Str =
|
|
62
59
|
if self.pages == 0
|
|
@@ -64,8 +61,7 @@ enum ChildrensTaleBook =
|
|
|
64
61
|
else
|
|
65
62
|
"Childrens book \"{self.title}\" teaches: {self.moralLesson}"
|
|
66
63
|
|
|
67
|
-
enum NonFictionBook =
|
|
68
|
-
|
|
64
|
+
record NonFictionBook(title: Str, pages: Int, rating1: Rating, rating2: Rating) =
|
|
69
65
|
|
|
70
66
|
fun collectInterestingInfo(self) -> Str =
|
|
71
67
|
match self.rating1, self.rating2
|
plum-examples/operators.plum
CHANGED
|
@@ -5,8 +5,7 @@ import std/Float
|
|
|
5
5
|
|
|
6
6
|
# Operators on non-primitive operands call methods: `a + b` is `a.add(b)`,
|
|
7
7
|
# `-a` is `a.neg()`, `a == b` is `a.eq(b)`, `a < b` is `a.lt(b)`.
|
|
8
|
-
enum Vec2 =
|
|
9
|
-
|
|
8
|
+
record Vec2(x: Float, y: Float) =
|
|
10
9
|
|
|
11
10
|
fun toStr(self) -> Str =
|
|
12
11
|
"Vec2({self.x}, {self.y})"
|
|
@@ -29,8 +28,7 @@ enum Vec2 =
|
|
|
29
28
|
fun le(self, o: Vec2) -> Bool =
|
|
30
29
|
self.x <= o.x
|
|
31
30
|
|
|
32
|
-
enum Plain =
|
|
33
|
-
|
|
31
|
+
record Plain(v: Int)
|
|
34
32
|
|
|
35
33
|
fun double(a: T) -> T =
|
|
36
34
|
a + a
|
plum-examples/optionals.plum
CHANGED
|
@@ -6,11 +6,9 @@ import std/List
|
|
|
6
6
|
# `while x := opt`, `opt!` (crashes on null) and `opt == null`; `catch` and `try`
|
|
7
7
|
# are for errors only. `!T?` is an error union whose success value is an optional.
|
|
8
8
|
|
|
9
|
-
enum Box =
|
|
10
|
-
|
|
9
|
+
record Box(label: Str)
|
|
11
10
|
|
|
12
|
-
enum Holder =
|
|
13
|
-
|
|
11
|
+
record Holder(item: Box?, count: Int?)
|
|
14
12
|
|
|
15
13
|
fun maybeBox(c: Bool) -> Box? =
|
|
16
14
|
if c
|
plum-examples/safe_navigation.plum
CHANGED
|
@@ -18,17 +18,14 @@ import std/List
|
|
|
18
18
|
# Pure sugar for `obj.andThen(|v| v.field)`, desugared entirely in the parser
|
|
19
19
|
# (see `plum-core`'s `parseSafeAttribute`).
|
|
20
20
|
|
|
21
|
-
enum Person =
|
|
22
|
-
|
|
21
|
+
record Person(name: Str, age: Int) =
|
|
23
22
|
|
|
24
23
|
fun birthday(self) -> Int =
|
|
25
24
|
self.age + 1
|
|
26
25
|
|
|
27
|
-
enum Target =
|
|
28
|
-
|
|
26
|
+
record Target(name: Str)
|
|
29
27
|
|
|
30
|
-
enum User =
|
|
31
|
-
|
|
28
|
+
record User(name: Str, target: Target?)
|
|
32
29
|
|
|
33
30
|
fun personName(p: Person?) -> Str? =
|
|
34
31
|
p?.name
|
plum-examples/types.plum
CHANGED
|
@@ -5,17 +5,14 @@ import std/Int
|
|
|
5
5
|
import std/Float
|
|
6
6
|
import std/Seq
|
|
7
7
|
|
|
8
|
-
enum Point =
|
|
9
|
-
|
|
8
|
+
record Point(x: Int, y: Int)
|
|
10
9
|
|
|
11
|
-
enum Named =
|
|
12
|
-
|
|
10
|
+
record Named(name: Str) =
|
|
13
11
|
|
|
14
12
|
fun toStr(self) -> Str =
|
|
15
13
|
self.name
|
|
16
14
|
|
|
17
|
-
enum Box[T] =
|
|
18
|
-
|
|
15
|
+
record Box[T](value: T)
|
|
19
16
|
|
|
20
17
|
enum Color =
|
|
21
18
|
| Red
|
|
@@ -35,26 +32,21 @@ fun makeStrBox() -> Box[Str] =
|
|
|
35
32
|
|
|
36
33
|
# ---- record-shaped and sum-type enum regression tests ----
|
|
37
34
|
|
|
38
|
-
enum Cat =
|
|
39
|
-
|
|
35
|
+
record Cat(name: Str, age: Int) =
|
|
40
36
|
|
|
41
37
|
fun getAge() -> Int =
|
|
42
38
|
self.age
|
|
43
39
|
|
|
44
|
-
enum Dog =
|
|
45
|
-
|
|
40
|
+
record Dog(name: Str, age: Int) =
|
|
46
41
|
|
|
47
42
|
fun getAge(self) -> Int =
|
|
48
43
|
self.age
|
|
49
44
|
|
|
50
|
-
enum IntPair =
|
|
51
|
-
|
|
45
|
+
record IntPair(a: Int, b: Int)
|
|
52
46
|
|
|
53
|
-
enum Wrapper =
|
|
54
|
-
|
|
47
|
+
record Wrapper(inner: IntPair, tag: Int)
|
|
55
48
|
|
|
56
|
-
enum LoopBox =
|
|
57
|
-
|
|
49
|
+
record LoopBox(v: Int)
|
|
58
50
|
|
|
59
51
|
fun sumLoopBoxes() -> Int =
|
|
60
52
|
total := 0
|
|
@@ -89,15 +81,13 @@ fun area(s: ShapeKind) -> Int =
|
|
|
89
81
|
Circle(r) =>
|
|
90
82
|
return r * r
|
|
91
83
|
|
|
92
|
-
enum Vec2 =
|
|
93
|
-
|
|
84
|
+
record Vec2(x: Int, y: Int)
|
|
94
85
|
|
|
95
86
|
enum ShapeWithFields =
|
|
96
87
|
| CircleField(radius: Int)
|
|
97
88
|
| SquareField(side: Int)
|
|
98
89
|
|
|
99
|
-
enum MaybeBox =
|
|
100
|
-
|
|
90
|
+
record MaybeBox(value: Int?) =
|
|
101
91
|
|
|
102
92
|
fun unwrap(default: Int) -> Int =
|
|
103
93
|
self.value ?: default
|
plum-std/Buffer.plum
CHANGED
|
@@ -14,8 +14,7 @@ import std/Float
|
|
|
14
14
|
# `bytes.Buffer`'s asymptotic behavior — unlike the previous `Str`-backed
|
|
15
15
|
# version of this type, which recopied everything written so far on every
|
|
16
16
|
# write.
|
|
17
|
-
enum Buffer =
|
|
18
|
-
|
|
17
|
+
record Buffer(data: []Byte, len: Int) =
|
|
19
18
|
|
|
20
19
|
# A no-param `init` nested in the type is `Buffer()`'s constructor — a
|
|
21
20
|
# zero-arg `Buffer()` call desugars to `Buffer.init()` (see `plum-checker`'s
|
plum-std/Deque.plum
CHANGED
|
@@ -9,8 +9,7 @@ import std/Float
|
|
|
9
9
|
# element `i` (0 = front) lives at `(head + i) % capacity`. Pushes at either end
|
|
10
10
|
# are O(1) amortized — when full, capacity doubles and the elements are copied
|
|
11
11
|
# out in front-to-back order (so the ring unwraps).
|
|
12
|
-
enum Deque[T] =
|
|
13
|
-
|
|
12
|
+
record Deque[T](buf: Array[T], head: Int, size: Int) =
|
|
14
13
|
|
|
15
14
|
fun init() -> Deque[T] =
|
|
16
15
|
b := Array[T]().resize(8)
|
plum-std/Grid.plum
CHANGED
|
@@ -9,8 +9,7 @@ import std/Float
|
|
|
9
9
|
# `y` the row in `[0, h)`. `get`/`set` do no bounds check of their own beyond
|
|
10
10
|
# the underlying array's trap — use `inBounds` first when coordinates may stray
|
|
11
11
|
# off the map (neighbour scans, pathfinding).
|
|
12
|
-
enum Grid[T] =
|
|
13
|
-
|
|
12
|
+
record Grid[T](w: Int, h: Int, data: Array[T]) =
|
|
14
13
|
|
|
15
14
|
# `Grid.filled(w, h, v)` — a `w` x `h` grid with every cell set to `v`
|
|
16
15
|
# (`T` is inferred from `v`).
|
plum-std/Heap.plum
CHANGED
|
@@ -11,8 +11,7 @@ import std/Float
|
|
|
11
11
|
# comparison, so keep it cheap and don't mutate an element's key while it is in
|
|
12
12
|
# the heap. `push`/`pop` are O(log n); `peek`/`length` are O(1). Equal keys pop in
|
|
13
13
|
# no guaranteed order.
|
|
14
|
-
enum Heap[T] =
|
|
15
|
-
|
|
14
|
+
record Heap[T](items: List[T], key: fn(T) -> Float) =
|
|
16
15
|
|
|
17
16
|
fun length(self) -> Int =
|
|
18
17
|
return self.items.length()
|
plum-std/Http.plum
CHANGED
|
@@ -13,8 +13,7 @@ error HttpError = HttpFailed | BadResponse
|
|
|
13
13
|
|
|
14
14
|
# An HTTP response: status code, response headers, and the raw response
|
|
15
15
|
# body as a `Str` (a byte array, so binary bodies round-trip intact).
|
|
16
|
-
enum Response =
|
|
17
|
-
|
|
16
|
+
record Response(status: Int, headers: Map[Str, Str], body: Str) =
|
|
18
17
|
|
|
19
18
|
# 2xx status codes are success; everything else (redirects, 4xx, 5xx) is
|
|
20
19
|
# surfaced here rather than as an error from `request` — a non-2xx
|
plum-std/Json.plum
CHANGED
|
@@ -85,8 +85,7 @@ enum JsonParse =
|
|
|
85
85
|
# A recursive-descent parser over a `Str` source, tracking its read position
|
|
86
86
|
# in `pos`. It reads directly from a `Str`, the same way every other stdlib
|
|
87
87
|
# parser (e.g. `Int.parse`) does.
|
|
88
|
-
enum JsonParser =
|
|
89
|
-
|
|
88
|
+
record JsonParser(src: Str, pos: Int, text: Str) =
|
|
90
89
|
|
|
91
90
|
# Records why parsing is about to fail at the current position; the caller
|
|
92
91
|
# then fails with `InvalidJson`.
|
plum-std/List.plum
CHANGED
|
@@ -12,8 +12,7 @@ import std/Str
|
|
|
12
12
|
# `get`/`set` are O(1). The core operations (`init`, `length`, `get`, `set`,
|
|
13
13
|
# `push`, `pop`, `clear`) sit directly on the `Array` intrinsics; everything
|
|
14
14
|
# else (`map`, `retain`, `reduce`, `chunk`, ...) is ordinary Plum on top of them.
|
|
15
|
-
enum List[T] =
|
|
16
|
-
|
|
15
|
+
record List[T](data: Array[T], size: Int) =
|
|
17
16
|
|
|
18
17
|
# `List[T](1, 2, 3)` (or, for an empty list, `List[T]()` — explicit
|
|
19
18
|
# generics required either way, since nothing else in a call this shape
|
plum-std/Map.plum
CHANGED
|
@@ -9,8 +9,7 @@ import std/Str
|
|
|
9
9
|
#
|
|
10
10
|
# `toStr` exists because `List[T]` calls `toStr` on its elements (duck-typed)
|
|
11
11
|
# and `Map` stores `List[Pair[K, V]]` buckets.
|
|
12
|
-
enum Pair[K, V] =
|
|
13
|
-
|
|
12
|
+
record Pair[K, V](key: K, val: V) =
|
|
14
13
|
|
|
15
14
|
fun toStr(self) -> Str =
|
|
16
15
|
return "{self.key.toStr()}: {self.val.toStr()}"
|
|
@@ -21,8 +20,7 @@ enum Pair[K, V] =
|
|
|
21
20
|
# association list costs), degrading to O(n) only if many keys collide into
|
|
22
21
|
# the same bucket.
|
|
23
22
|
# `K` must have a `hash() -> Int` method (duck-typed, checked per instantiation).
|
|
24
|
-
enum Map[K, V] =
|
|
25
|
-
|
|
23
|
+
record Map[K, V](buckets: Array[List[Pair[K, V]]], size: Int) =
|
|
26
24
|
|
|
27
25
|
# Number of buckets every `Map` allocates at construction — fixed for the
|
|
28
26
|
# map's whole lifetime, no rehashing/resizing as it grows. A real hash table
|
plum-std/Random.plum
CHANGED
|
@@ -16,8 +16,7 @@ import std/Float
|
|
|
16
16
|
# One raw 32-bit half of the host's pseudo-random generator.
|
|
17
17
|
extern fun rawRandomHalf() -> Int
|
|
18
18
|
|
|
19
|
-
enum Random =
|
|
20
|
-
|
|
19
|
+
record Random(seed: Int) =
|
|
21
20
|
|
|
22
21
|
# A raw, host-provided pseudo-random 64-bit Int (xorshift64*, seeded from the
|
|
23
22
|
# wall clock — not cryptographically secure). Every other wall-clock random
|
plum-std/Set.plum
CHANGED
|
@@ -7,8 +7,7 @@ import std/Float
|
|
|
7
7
|
# A set of unique `T`s, a thin layer over `Map[T, Bool]` (so `T` needs the same
|
|
8
8
|
# duck-typed `hash() -> Int` and `==` that `Map`'s keys do). `Set[Int]()`
|
|
9
9
|
# builds an empty one.
|
|
10
|
-
enum Set[T] =
|
|
11
|
-
|
|
10
|
+
record Set[T](map: Map[T, Bool]) =
|
|
12
11
|
|
|
13
12
|
fun init() -> Set[T] =
|
|
14
13
|
return Set(map: Map[T, Bool]())
|
plum-std/Str.plum
CHANGED
|
@@ -22,8 +22,7 @@ import std/Float
|
|
|
22
22
|
# means constructing a brand new `Str`/`Buffer` pair (`Buffer.write`'s own
|
|
23
23
|
# callers are always building up a FRESH buffer before wrapping it, never
|
|
24
24
|
# mutating an already-published `Str`'s backing storage).
|
|
25
|
-
enum Str =
|
|
26
|
-
|
|
25
|
+
record Str(data: Buffer) =
|
|
27
26
|
|
|
28
27
|
# Number of bytes in the string — `self.data` (a `Buffer`) already tracks
|
|
29
28
|
# this, so unlike `byteAt`/`byteToStr` this needs no compiler intrinsic of
|
plum-std/Time.plum
CHANGED
|
@@ -8,8 +8,7 @@ import std/Str
|
|
|
8
8
|
extern fun rawNowMillis() -> Float
|
|
9
9
|
|
|
10
10
|
# A single point in time, stored as milliseconds since the Unix epoch.
|
|
11
|
-
enum Time =
|
|
12
|
-
|
|
11
|
+
record Time(value: Int) =
|
|
13
12
|
|
|
14
13
|
MILLIS_PER_DAY = 86400000
|
|
15
14
|
|
|
@@ -82,8 +81,7 @@ enum Time =
|
|
|
82
81
|
+ "." + self.millisecond().toStr().padStart("0", 3) + "Z"
|
|
83
82
|
|
|
84
83
|
# A Gregorian calendar date, decomposed from a day count by `civilFromDays`.
|
|
85
|
-
enum Civil =
|
|
86
|
-
|
|
84
|
+
record Civil(year: Int, month: Int, day: Int)
|
|
87
85
|
|
|
88
86
|
# Days-since-epoch to proleptic Gregorian year/month/day, via Howard
|
|
89
87
|
# Hinnant's `civil_from_days` (http://howardhinnant.github.io/date_algorithms.html) —
|
|
@@ -116,8 +114,7 @@ fun floorMod(a: Int, b: Int) -> Int =
|
|
|
116
114
|
|
|
117
115
|
# A span of time, stored as milliseconds. Negative values are meaningful
|
|
118
116
|
# (e.g. the result of `Time.since` when `self` is earlier than `other`).
|
|
119
|
-
enum Duration =
|
|
120
|
-
|
|
117
|
+
record Duration(value: Int) =
|
|
121
118
|
|
|
122
119
|
fun millis(n: Int) -> Duration =
|
|
123
120
|
return Duration(value: n)
|
plum-std/Uuid.plum
CHANGED
|
@@ -6,8 +6,7 @@ import std/Random
|
|
|
6
6
|
|
|
7
7
|
# A UUID, stored as its canonical 36-character
|
|
8
8
|
# `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` text form.
|
|
9
|
-
enum Uuid =
|
|
10
|
-
|
|
9
|
+
record Uuid(value: Str) =
|
|
11
10
|
|
|
12
11
|
fun toStr(self) -> Str =
|
|
13
12
|
return self.value
|
plum-tooling/tree-sitter-plum/test/aa.plum
CHANGED
|
@@ -14,8 +14,7 @@ primitive Defaults =
|
|
|
14
14
|
error CatError = Hungry | Asleep
|
|
15
15
|
|
|
16
16
|
# Cat is an animal that meows always
|
|
17
|
-
enum Cat =
|
|
18
|
-
|
|
17
|
+
record Cat(name: Str, age: Int) =
|
|
19
18
|
|
|
20
19
|
fun withName(self, name: Str) -> Cat =
|
|
21
20
|
Cat(name: name, age: self.age)
|
plum-tooling/tree-sitter-plum/test/corpus/const.txt
CHANGED
|
@@ -121,8 +121,7 @@ primitive Consts =
|
|
|
121
121
|
associated consts in an enum body and Type.NAME access
|
|
122
122
|
================================================================================
|
|
123
123
|
|
|
124
|
-
enum Vec2 =
|
|
125
|
-
|
|
124
|
+
record Vec2(x: Float, y: Float) =
|
|
126
125
|
|
|
127
126
|
ZERO = Vec2(x: 0.0, y: 0.0)
|
|
128
127
|
|
|
@@ -134,18 +133,16 @@ fun main() =
|
|
|
134
133
|
--------------------------------------------------------------------------------
|
|
135
134
|
|
|
136
135
|
(source
|
|
137
|
-
(
|
|
136
|
+
(record
|
|
138
137
|
(type_identifier)
|
|
139
|
-
(field
|
|
140
|
-
(type_identifier)
|
|
141
|
-
|
|
138
|
+
(enum_named_field
|
|
142
|
-
|
|
139
|
+
(var_identifier)
|
|
143
|
-
|
|
140
|
+
(type
|
|
144
|
-
|
|
141
|
+
(type_identifier)))
|
|
145
|
-
|
|
142
|
+
(enum_named_field
|
|
146
|
-
|
|
143
|
+
(var_identifier)
|
|
147
|
-
|
|
144
|
+
(type
|
|
148
|
-
|
|
145
|
+
(type_identifier)))
|
|
149
146
|
(const
|
|
150
147
|
(const_identifier)
|
|
151
148
|
(expression
|
plum-tooling/tree-sitter-plum/test/corpus/type.txt
CHANGED
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
type
|
|
3
3
|
================================================================================
|
|
4
4
|
|
|
5
|
-
enum Dog =
|
|
6
|
-
|
|
5
|
+
record Dog(name: Str, age: B)
|
|
7
6
|
|
|
8
|
-
enum Cat =
|
|
9
|
-
|
|
7
|
+
record Cat(name: Str, age: Int) =
|
|
10
8
|
|
|
11
9
|
fun init(name: Str) -> Cat =
|
|
12
10
|
Cat(name: name, age: 0)
|
|
@@ -23,30 +21,26 @@ enum Cat =
|
|
|
23
21
|
--------------------------------------------------------------------------------
|
|
24
22
|
|
|
25
23
|
(source
|
|
26
|
-
(
|
|
24
|
+
(record
|
|
27
25
|
(type_identifier)
|
|
28
|
-
(field
|
|
29
|
-
(type_identifier)
|
|
30
|
-
|
|
26
|
+
(enum_named_field
|
|
31
|
-
|
|
27
|
+
(var_identifier)
|
|
32
|
-
|
|
28
|
+
(type
|
|
33
|
-
|
|
29
|
+
(type_identifier)))
|
|
34
|
-
|
|
30
|
+
(enum_named_field
|
|
35
|
-
|
|
31
|
+
(var_identifier)
|
|
36
|
-
|
|
32
|
+
(type
|
|
37
|
-
|
|
33
|
+
(generic))))
|
|
38
|
-
(
|
|
34
|
+
(record
|
|
39
35
|
(type_identifier)
|
|
40
|
-
(field
|
|
41
|
-
(type_identifier)
|
|
42
|
-
|
|
36
|
+
(enum_named_field
|
|
43
|
-
|
|
37
|
+
(var_identifier)
|
|
44
|
-
|
|
38
|
+
(type
|
|
45
|
-
|
|
39
|
+
(type_identifier)))
|
|
46
|
-
|
|
40
|
+
(enum_named_field
|
|
47
|
-
|
|
41
|
+
(var_identifier)
|
|
48
|
-
|
|
42
|
+
(type
|
|
49
|
-
|
|
43
|
+
(type_identifier)))
|
|
50
44
|
(fn
|
|
51
45
|
(fn_identifier)
|
|
52
46
|
(param
|
|
@@ -153,8 +147,7 @@ enum Cat =
|
|
|
153
147
|
type - nested method declaration
|
|
154
148
|
================================================================================
|
|
155
149
|
|
|
156
|
-
enum Cat =
|
|
157
|
-
|
|
150
|
+
record Cat(name: Str) =
|
|
158
151
|
|
|
159
152
|
fun getName(self) -> Str =
|
|
160
153
|
self.name
|
|
@@ -162,14 +155,12 @@ enum Cat =
|
|
|
162
155
|
--------------------------------------------------------------------------------
|
|
163
156
|
|
|
164
157
|
(source
|
|
165
|
-
(
|
|
158
|
+
(record
|
|
166
159
|
(type_identifier)
|
|
167
|
-
(field
|
|
168
|
-
(type_identifier)
|
|
169
|
-
|
|
160
|
+
(enum_named_field
|
|
170
|
-
|
|
161
|
+
(var_identifier)
|
|
171
|
-
|
|
162
|
+
(type
|
|
172
|
-
|
|
163
|
+
(type_identifier)))
|
|
173
164
|
(fn
|
|
174
165
|
(fn_identifier)
|
|
175
166
|
(self)
|
plum-tooling/tree-sitter-plum/test/highlight/sample.plum
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
module std
|
|
2
2
|
|
|
3
|
-
enum Cat =
|
|
4
|
-
|
|
3
|
+
record Cat(name: Str, age: Int) =
|
|
5
4
|
|
|
6
5
|
fun withName(self, name: Str) -> Cat =
|
|
7
6
|
Cat(name: name, age: self.age)
|