plum

#treesitter#compiler#wasm

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

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


fbfbd7bPeter John 2026-08-09T18:47:56+05:30
feat(plum-core): parse enum discriminant-value declarations
plum-checker/src/monomorphize.rs CHANGED
@@ -168,11 +168,13 @@ pub fn specialize_enum(e: &ast::Enum, subst: &Substitution, mangled_name: &str)
168
168
  let type_args: Vec<PlumType> = params.iter().filter_map(|p| subst.get(p).cloned()).collect();
169
169
  ast::Enum {
170
170
  name: mangled_name.to_string(),
171
+ params: e.params.clone(),
171
172
  variants: e.variants.iter().map(|v| ast::EnumVariant {
172
173
  name: mangle(&v.name, &type_args),
173
174
  fields: v.fields.iter().map(|f| {
174
175
  subst.get(f).map(|t| t.to_string()).unwrap_or_else(|| f.clone())
175
176
  }).collect(),
177
+ values: v.values.clone(),
176
178
  }).collect(),
177
179
  }
178
180
  }
plum-core/src/ast.rs CHANGED
@@ -63,13 +63,21 @@ pub struct TraitMethod {
63
63
  #[derive(Debug, Clone, PartialEq)]
64
64
  pub struct Enum {
65
65
  pub name: String,
66
+ pub params: Vec<EnumParam>,
66
67
  pub variants: Vec<EnumVariant>,
67
68
  }
68
69
 
70
+ #[derive(Debug, Clone, PartialEq)]
71
+ pub struct EnumParam {
72
+ pub name: String,
73
+ pub ty: Type,
74
+ }
75
+
69
76
  #[derive(Debug, Clone, PartialEq)]
70
77
  pub struct EnumVariant {
71
78
  pub name: String,
72
79
  pub fields: Vec<String>,
80
+ pub values: Vec<Expr>,
73
81
  }
74
82
 
75
83
  // ---------- Functions ----------
plum-core/src/parser.rs CHANGED
@@ -198,24 +198,46 @@ impl<'a> AstParser<'a> {
198
198
 
199
199
  fn parse_enum(&self, node: Node) -> Enum {
200
200
  let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
201
+ let params = self.children_of_kind(node, "enum_param")
202
+ .into_iter()
203
+ .map(|n| self.parse_enum_param(n))
204
+ .collect();
201
205
  let variants = self.children_of_kind(node, "field")
202
206
  .into_iter()
203
207
  .map(|f| self.parse_enum_variant(f))
204
208
  .collect();
205
- Enum { name, variants }
209
+ Enum { name, params, variants }
210
+ }
211
+
212
+ fn parse_enum_param(&self, node: Node) -> EnumParam {
213
+ // enum_param: var_identifier ":" type
214
+ let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
215
+ let ty = node
216
+ .named_child(1)
217
+ .map(|n| self.parse_type(n))
218
+ .unwrap_or(Type { name: String::new(), generics: vec![] });
219
+ EnumParam { name, ty }
206
220
  }
207
221
 
208
222
  fn parse_enum_variant(&self, node: Node) -> EnumVariant {
209
- // enum_field (aliased to field): "|" type_identifier ("[" (type_identifier | generic),* "]")?
223
+ // enum_field (aliased to field): "|" type_identifier
224
+ // ("[" (type_identifier | generic),* "]")? -- existing: generic type payload
225
+ // | ("(" expression,* ")")? -- new: discriminant value literals
210
- // named children: type_identifier (name), then each field type inside "[]" — a
226
+ // named children after the name: either type_identifier/generic (fields) or
211
- // `type_identifier` (concrete, e.g. `Int`) or a `generic` node (single uppercase letter).
227
+ // expression (values) the two are disjoint child-kind sets, never mixed.
212
228
  let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
213
- let fields: Vec<String> = (1..node.named_child_count())
229
+ let rest: Vec<Node> = (1..node.named_child_count())
214
230
  .filter_map(|i| node.named_child(i as u32))
231
+ .collect();
232
+ let fields: Vec<String> = rest.iter()
215
233
  .filter(|n| matches!(n.kind(), "type_identifier" | "generic"))
216
- .map(|n| self.text(n))
234
+ .map(|n| self.text(*n))
235
+ .collect();
236
+ let values: Vec<Expr> = rest.iter()
237
+ .filter(|n| n.kind() == "expression")
238
+ .map(|n| { let u = self.unwrap_expr_node(*n); self.parse_expression(u) })
217
239
  .collect();
218
- EnumVariant { name, fields }
240
+ EnumVariant { name, fields, values }
219
241
  }
220
242
 
221
243
  // ---- functions --------------------------------------------------------
plum-core/tests/parser_test.rs CHANGED
@@ -18,6 +18,10 @@ fn only_trait(source: &Source) -> &Trait {
18
18
  source.items.iter().find_map(|i| match i { Item::Trait(t) => Some(t), _ => None }).expect("expected a Trait item")
19
19
  }
20
20
 
21
+ fn only_enum(source: &Source) -> &Enum {
22
+ source.items.iter().find_map(|i| match i { Item::Enum(e) => Some(e), _ => None }).expect("expected an Enum item")
23
+ }
24
+
21
25
  #[test]
22
26
  fn nested_class_methods_become_top_level_fn_items_with_type_param_set() {
23
27
  let src = "\
@@ -63,6 +67,22 @@ enum Step =
63
67
  assert_eq!(to_number.type_param, Some("Step".to_string()));
64
68
  }
65
69
 
70
+ #[test]
71
+ fn enum_discriminant_values_parse_into_params_and_variant_values() {
72
+ let src = "\
73
+ enum Step(n: Int) =
74
+ | ReadMin(0)
75
+ | ReadMax(1)
76
+ ";
77
+ let source = parse(src);
78
+ let e = only_enum(&source);
79
+ assert_eq!(e.params, vec![EnumParam { name: "n".to_string(), ty: Type { name: "Int".to_string(), generics: vec![] } }]);
80
+ assert_eq!(e.variants[0].fields, Vec::<String>::new());
81
+ assert_eq!(e.variants[0].values, vec![Expr::Int(0)]);
82
+ assert_eq!(e.variants[1].fields, Vec::<String>::new());
83
+ assert_eq!(e.variants[1].values, vec![Expr::Int(1)]);
84
+ }
85
+
66
86
  #[test]
67
87
  fn class_and_enum_with_no_nested_methods_produce_no_extra_fn_items() {
68
88
  let src = "\