plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


8f91d27Peter John 2026-07-24T11:03:43+05:30
feat(grammar): migrate generics to bracket syntax with uppercase letters
tooling/tree-sitter-plum/grammar.js CHANGED
@@ -56,7 +56,7 @@ module.exports = grammar({
56
56
  // distinguish fn_argument_list from class_argument_list until it sees content or ')'.
57
57
  // Declaring the conflict at the argument-list level (not the call level) resolves this.
58
58
  conflicts: ($) => [[$.fn_argument_list, $.class_argument_list]],
59
- inline: ($) => [$.generic_type, $.generic],
59
+ inline: ($) => [$.generic_type],
60
60
  rules: {
61
61
  source: ($) =>
62
62
  seq(
@@ -69,7 +69,7 @@ module.exports = grammar({
69
69
  import: ($) => seq("import", $.url),
70
70
  url: () => sep1(/[a-zA-Z_][a-zA-Z_0-9]*/, "/"),
71
71
 
72
- generics: ($) => seq("(", commaSep1($.generic_type), ")"),
72
+ generics: ($) => seq("[", commaSep1($.generic_type), "]"),
73
73
  generic_type: ($) =>
74
74
  seq($.generic, optional(seq(":", sep1($.type_identifier, "+")))),
75
75
  type: ($) =>
@@ -78,12 +78,7 @@ module.exports = grammar({
78
78
  $.type_identifier,
79
79
  field(
80
80
  "generics",
81
- optional(
82
- choice(
83
- seq("[", commaSep1($.type), "]"),
81
+ optional(seq("[", commaSep1($.type), "]")),
84
- seq("(", commaSep1($.type), ")"),
85
- ),
86
- ),
87
82
  ),
88
83
  ),
89
84
  $.generic,
@@ -94,8 +89,8 @@ module.exports = grammar({
94
89
  seq(
95
90
  "type",
96
91
  field("name", $.type_identifier),
97
- field("implements", optional(seq("(", commaSep1($.type_identifier), ")"))),
98
92
  field("generics", optional($.generics)),
93
+ field("implements", optional(seq("(", commaSep1($.type_identifier), ")"))),
99
94
  "=",
100
95
  $._indent,
101
96
  field("fields", optional(repeat(alias($.class_field, $.field)))),
@@ -118,7 +113,7 @@ module.exports = grammar({
118
113
  seq(
119
114
  field("name", $.fn_identifier),
120
115
  field("params", seq("(", optional(commaSep1(choice($.self, $.param))), ")")),
121
- field("returns", optional(seq("->", $.return_type))),
116
+ field("returns", optional(seq("->", $.type))),
122
117
  ),
123
118
 
124
119
  param: ($) =>
@@ -138,9 +133,6 @@ module.exports = grammar({
138
133
  optional(seq("->", field("returns", $.type))),
139
134
  ),
140
135
 
141
- return_type: ($) =>
142
- seq($.type_identifier, field("generics", optional($.generics))),
143
-
144
136
  enum: ($) =>
145
137
  seq(
146
138
  "enum",
@@ -155,7 +147,7 @@ module.exports = grammar({
155
147
  seq(
156
148
  "|",
157
149
  field("name", $.type_identifier),
158
- field("parameters", optional(seq("(", commaSep1(choice($.type_identifier, $.generic)), ")"))),
150
+ field("parameters", optional(seq("[", commaSep1(choice($.type_identifier, $.generic)), "]"))),
159
151
  ),
160
152
 
161
153
  fn: ($) =>
@@ -169,7 +161,7 @@ module.exports = grammar({
169
161
  // own (the receiver type already came from `<Receiver>`), so the parser
170
162
  // discards it rather than emitting a `param` — see `parse_fn`.
171
163
  field("params", seq("(", optional(commaSep1(choice($.self, $.param))), ")")),
172
- field("returns", optional(seq("->", $.return_type))),
164
+ field("returns", optional(seq("->", $.type))),
173
165
  field("body", seq("=", choice($.expression, $.body))),
174
166
  )
175
167
  ),
@@ -526,18 +518,14 @@ module.exports = grammar({
526
518
  self: (_) => /self/,
527
519
  comment: _ => token(seq('#', /.*/)),
528
520
  identifier: (_) => /[_a-z][_a-zA-Z0-9]*/,
529
- generic: ($) => choice($.a, $.b, $.c, $.d), // single letter
521
+ generic: (_) => /[A-Z]/, // any single uppercase letter — reserved, illegal as a type_identifier
530
- a: (_) => token("a"),
531
- b: (_) => token("b"),
532
- c: (_) => token("c"),
533
- d: (_) => token("d"),
534
522
  mod_identifier: () => /[a-z][a-z0-9]*(_[a-z0-9]+)*/, // lower snake case
535
523
  const_identifier: (_) => /[A-Z][A-Z0-9]*(_[A-Z0-9]+)*/, // upper snake case
536
524
  // Superset of fn_identifier's charset (adds "_") so fn_call's callee can share
537
525
  // this single token instead of forcing the lexer to pick between two rules.
538
526
  var_identifier: (_) => /[a-z][a-zA-Z0-9]*(_[a-zA-Z0-9]+)*/, // lower snake case (or camelCase, when used as a callee)
539
527
  fn_identifier: (_) => /[a-z][a-zA-Z0-9]*/, // camel case
540
- type_identifier: (_) => /[A-Z][a-zA-Z0-9]*/, // capital case
528
+ type_identifier: (_) => /[A-Z][a-zA-Z0-9]+/, // capital case, 2+ chars (single uppercase letters are reserved for `generic`)
541
529
  },
542
530
  });
543
531
 
tooling/tree-sitter-plum/src/grammar.json CHANGED
Binary file
tooling/tree-sitter-plum/src/node-types.json CHANGED
Binary file
tooling/tree-sitter-plum/src/parser.c CHANGED
Binary file
tooling/tree-sitter-plum/test/corpus/enum.txt CHANGED
@@ -22,7 +22,7 @@ toStr<Bool>() -> Str =
22
22
  (fn_identifier)
23
23
  (type
24
24
  (type_identifier))
25
- (return_type
25
+ (type
26
26
  (type_identifier))
27
27
  (body
28
28
  (expression
@@ -37,7 +37,7 @@ enum - generic variant fields
37
37
  ================================================================================
38
38
 
39
39
  enum Option =
40
- | Some(a)
40
+ | Some[T]
41
41
  | None
42
42
 
43
43
  --------------------------------------------------------------------------------
@@ -47,6 +47,6 @@ enum Option =
47
47
  (type_identifier)
48
48
  (field
49
49
  (type_identifier)
50
- (a))
50
+ (generic))
51
51
  (field
52
52
  (type_identifier))))
tooling/tree-sitter-plum/test/corpus/function.txt CHANGED
@@ -41,7 +41,7 @@ sum(a: Int, b: Int) -> Int =
41
41
  (var_identifier)
42
42
  (type
43
43
  (type_identifier)))
44
- (return_type
44
+ (type
45
45
  (type_identifier))
46
46
  (body
47
47
  (todo))))
@@ -72,7 +72,7 @@ random(a: Int = 10) =
72
72
  function - generics
73
73
  ================================================================================
74
74
 
75
- add(param: a, param2: List(b)) -> List(b) =
75
+ add(param: T, param2: List[U]) -> List[U] =
76
76
  todo
77
77
 
78
78
  --------------------------------------------------------------------------------
@@ -83,17 +83,17 @@ add(param: a, param2: List(b)) -> List(b) =
83
83
  (param
84
84
  (var_identifier)
85
85
  (type
86
- (a)))
86
+ (generic)))
87
87
  (param
88
88
  (var_identifier)
89
89
  (type
90
90
  (type_identifier)
91
91
  (type
92
- (b))))
93
- (return_type
92
+ (generic))))
93
+ (type
94
94
  (type_identifier)
95
+ (type
95
- (generics
96
+ (generic)))
96
- (b)))
97
97
  (body
98
98
  (todo))))
99
99
 
@@ -111,7 +111,7 @@ fullname<User>() -> String =
111
111
  (fn_identifier)
112
112
  (type
113
113
  (type_identifier))
114
- (return_type
114
+ (type
115
115
  (type_identifier))
116
116
  (body
117
117
  (todo))))
@@ -120,7 +120,7 @@ fullname<User>() -> String =
120
120
  function - method with explicit self param
121
121
  ================================================================================
122
122
 
123
- remove<List>(self, v: a) =
123
+ remove<List>(self, v: T) =
124
124
  todo
125
125
 
126
126
  --------------------------------------------------------------------------------
@@ -134,7 +134,7 @@ remove<List>(self, v: a) =
134
134
  (param
135
135
  (var_identifier)
136
136
  (type
137
- (a)))
137
+ (generic)))
138
138
  (body
139
139
  (todo))))
140
140
 
@@ -154,7 +154,7 @@ makeSome(v: Int) -> Option =
154
154
  (var_identifier)
155
155
  (type
156
156
  (type_identifier)))
157
- (return_type
157
+ (type
158
158
  (type_identifier))
159
159
  (body
160
160
  (expression
@@ -182,7 +182,7 @@ isNone(o: Option) -> Bool =
182
182
  (var_identifier)
183
183
  (type
184
184
  (type_identifier)))
185
- (return_type
185
+ (type
186
186
  (type_identifier))
187
187
  (body
188
188
  (expression
@@ -212,7 +212,7 @@ bothTrue(a: Bool, b: Bool) -> Bool =
212
212
  (var_identifier)
213
213
  (type
214
214
  (type_identifier)))
215
- (return_type
215
+ (type
216
216
  (type_identifier))
217
217
  (body
218
218
  (expression
@@ -248,7 +248,7 @@ pick(cond: Bool, a: Int, b: Int) -> Int =
248
248
  (var_identifier)
249
249
  (type
250
250
  (type_identifier)))
251
- (return_type
251
+ (type
252
252
  (type_identifier))
253
253
  (body
254
254
  (expression
@@ -277,7 +277,7 @@ useClosure() -> Bool =
277
277
  (source
278
278
  (fn
279
279
  (fn_identifier)
280
- (return_type
280
+ (type
281
281
  (type_identifier))
282
282
  (body
283
283
  (assign
@@ -312,7 +312,7 @@ useClosure() -> Bool =
312
312
  (source
313
313
  (fn
314
314
  (fn_identifier)
315
- (return_type
315
+ (type
316
316
  (type_identifier))
317
317
  (body
318
318
  (assign
@@ -346,7 +346,7 @@ each(cb: fn(Int)) -> Bool =
346
346
  (fn_value_type
347
347
  (type
348
348
  (type_identifier))))
349
- (return_type
349
+ (type
350
350
  (type_identifier))
351
351
  (body
352
352
  (expression
@@ -357,7 +357,7 @@ each(cb: fn(Int)) -> Bool =
357
357
  function - function-value type param annotation with generic types and return
358
358
  ================================================================================
359
359
 
360
- each(cb: fn(a) -> b) -> Bool =
360
+ each(cb: fn(T) -> U) -> Bool =
361
361
  True
362
362
 
363
363
  --------------------------------------------------------------------------------
@@ -369,10 +369,10 @@ each(cb: fn(a) -> b) -> Bool =
369
369
  (var_identifier)
370
370
  (fn_value_type
371
371
  (type
372
- (a))
372
+ (generic))
373
373
  (type
374
- (b))))
375
- (return_type
374
+ (generic))))
375
+ (type
376
376
  (type_identifier))
377
377
  (body
378
378
  (expression
@@ -391,7 +391,7 @@ main() -> Int =
391
391
  (source
392
392
  (fn
393
393
  name: (fn_identifier)
394
- returns: (return_type
394
+ returns: (type
395
395
  (type_identifier))
396
396
  body: (body
397
397
  (expression
@@ -419,7 +419,7 @@ main() -> Int =
419
419
  (source
420
420
  (fn
421
421
  name: (fn_identifier)
422
- returns: (return_type
422
+ returns: (type
423
423
  (type_identifier))
424
424
  body: (body
425
425
  (expression
tooling/tree-sitter-plum/test/corpus/trait.txt CHANGED
@@ -18,17 +18,17 @@ trait Animal =
18
18
  (type_identifier)
19
19
  (field
20
20
  (fn_identifier)
21
- (return_type
21
+ (type
22
22
  (type_identifier))))
23
23
  (trait
24
24
  (type_identifier)
25
25
  (field
26
26
  (fn_identifier)
27
- (return_type
27
+ (type
28
28
  (type_identifier)))
29
29
  (field
30
30
  (fn_identifier)
31
- (return_type
31
+ (type
32
32
  (type_identifier)))
33
33
  (field
34
34
  (fn_identifier))
@@ -38,5 +38,5 @@ trait Animal =
38
38
  (var_identifier)
39
39
  (type
40
40
  (type_identifier)))
41
- (return_type
41
+ (type
42
42
  (type_identifier)))))
tooling/tree-sitter-plum/test/corpus/type.txt CHANGED
@@ -4,7 +4,7 @@ type
4
4
 
5
5
  type Dog =
6
6
  name: Str
7
- age: b
7
+ age: B
8
8
 
9
9
  type Cat(Stringable) =
10
10
  name: Str
@@ -34,7 +34,7 @@ toStr<Cat>() -> Str =
34
34
  (field
35
35
  (var_identifier)
36
36
  (type
37
- (b))))
37
+ (generic))))
38
38
  (class
39
39
  (type_identifier)
40
40
  (type_identifier)
@@ -54,7 +54,7 @@ toStr<Cat>() -> Str =
54
54
  (var_identifier)
55
55
  (type
56
56
  (type_identifier)))
57
- (return_type
57
+ (type
58
58
  (type_identifier))
59
59
  (body
60
60
  (expression
@@ -78,7 +78,7 @@ toStr<Cat>() -> Str =
78
78
  (var_identifier)
79
79
  (type
80
80
  (type_identifier)))
81
- (return_type
81
+ (type
82
82
  (type_identifier))
83
83
  (body
84
84
  (expression
@@ -102,7 +102,7 @@ toStr<Cat>() -> Str =
102
102
  (var_identifier)
103
103
  (type
104
104
  (type_identifier)))
105
- (return_type
105
+ (type
106
106
  (type_identifier))
107
107
  (body
108
108
  (expression
@@ -124,7 +124,7 @@ toStr<Cat>() -> Str =
124
124
  (fn_identifier)
125
125
  (type
126
126
  (type_identifier))
127
- (return_type
127
+ (type
128
128
  (type_identifier))
129
129
  (body
130
130
  (expression