plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
fbfbd7b
— Peter John
2026-08-09T18:47:56+05:30
feat(plum-core): parse enum discriminant-value declarations
- plum-checker/src/monomorphize.rs +2 -0
- plum-core/src/ast.rs +8 -0
- plum-core/src/parser.rs +29 -7
- plum-core/tests/parser_test.rs +20 -0
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
|
|
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 (
|
|
226
|
+
// named children after the name: either type_identifier/generic (fields) or
|
|
211
|
-
//
|
|
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
|
|
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 = "\
|