plum

#treesitter#compiler#wasm

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

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


0a830c8Peter John 2026-07-19T17:22:28+05:30
refactor: restructure as Cargo workspace, add plum-core skeleton
Cargo.toml CHANGED
@@ -1,9 +1,3 @@
1
- [package]
1
+ [workspace]
2
- name = "plum"
3
- version = "0.1.0"
4
- edition = "2021"
5
-
6
- [dependencies]
7
- baz-tree-sitter-traversal = "0.1.4"
2
+ members = ["plum-core", "plum-cli"]
8
- tree-sitter = "0.24.5"
3
+ resolver = "2"
9
- tree-sitter-plum = "0.1.0"
plum-cli/Cargo.toml ADDED
@@ -0,0 +1,7 @@
1
+ [package]
2
+ name = "plum-cli"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+
6
+ [dependencies]
7
+ plum-core = { path = "../plum-core" }
plum-cli/src/lib.rs ADDED
@@ -0,0 +1 @@
1
+ // plum-cli library placeholder
plum-core/Cargo.toml ADDED
@@ -0,0 +1,10 @@
1
+ [package]
2
+ name = "plum-core"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+
6
+ [dependencies]
7
+ tree-sitter = "0.26"
8
+ tree-sitter-plum = { path = "../tooling/tree-sitter-plum" }
9
+ topiary-core = "0.7.3"
10
+ topiary-tree-sitter-facade = "0.7.3"
plum-core/src/ast.rs ADDED
@@ -0,0 +1,354 @@
1
+ #[derive(Debug, Clone, PartialEq)]
2
+ pub struct Source {
3
+ pub module: Option<Module>,
4
+ pub imports: Vec<Import>,
5
+ pub items: Vec<Item>,
6
+ }
7
+
8
+ #[derive(Debug, Clone, PartialEq)]
9
+ pub struct Module {
10
+ pub name: String,
11
+ }
12
+
13
+ #[derive(Debug, Clone, PartialEq)]
14
+ pub struct Import {
15
+ pub path: String,
16
+ }
17
+
18
+ #[derive(Debug, Clone, PartialEq)]
19
+ pub enum Item {
20
+ Class(Class),
21
+ Trait(Trait),
22
+ Enum(Enum),
23
+ Fn(Fn),
24
+ Const(Const),
25
+ }
26
+
27
+ // ---------- Type definitions ----------
28
+
29
+ #[derive(Debug, Clone, PartialEq)]
30
+ pub struct Class {
31
+ pub name: String,
32
+ pub implements: Vec<String>,
33
+ pub generics: Vec<GenericParam>,
34
+ pub fields: Vec<Field>,
35
+ }
36
+
37
+ #[derive(Debug, Clone, PartialEq)]
38
+ pub struct GenericParam {
39
+ pub name: String,
40
+ pub bounds: Vec<String>,
41
+ }
42
+
43
+ #[derive(Debug, Clone, PartialEq)]
44
+ pub struct Field {
45
+ pub name: String,
46
+ pub ty: Type,
47
+ }
48
+
49
+ #[derive(Debug, Clone, PartialEq)]
50
+ pub struct Trait {
51
+ pub name: String,
52
+ pub generics: Vec<GenericParam>,
53
+ pub methods: Vec<TraitMethod>,
54
+ }
55
+
56
+ #[derive(Debug, Clone, PartialEq)]
57
+ pub struct TraitMethod {
58
+ pub name: String,
59
+ pub params: Vec<Param>,
60
+ pub returns: Option<ReturnType>,
61
+ }
62
+
63
+ #[derive(Debug, Clone, PartialEq)]
64
+ pub struct Enum {
65
+ pub name: String,
66
+ pub variants: Vec<EnumVariant>,
67
+ }
68
+
69
+ #[derive(Debug, Clone, PartialEq)]
70
+ pub struct EnumVariant {
71
+ pub name: String,
72
+ pub fields: Vec<String>,
73
+ }
74
+
75
+ // ---------- Functions ----------
76
+
77
+ #[derive(Debug, Clone, PartialEq)]
78
+ pub struct Fn {
79
+ pub name: String,
80
+ /// Type parameter for method dispatch, e.g. `<Cat>` in `toStr<Cat>()`
81
+ pub type_param: Option<String>,
82
+ pub params: Vec<Param>,
83
+ pub returns: Option<ReturnType>,
84
+ pub body: FnBody,
85
+ }
86
+
87
+ #[derive(Debug, Clone, PartialEq)]
88
+ pub struct Const {
89
+ pub name: String,
90
+ pub value: Expr,
91
+ }
92
+
93
+ #[derive(Debug, Clone, PartialEq)]
94
+ pub struct Param {
95
+ pub name: String,
96
+ pub ty: ParamType,
97
+ pub default: Option<Expr>,
98
+ }
99
+
100
+ #[derive(Debug, Clone, PartialEq)]
101
+ pub enum ParamType {
102
+ Type(Type),
103
+ Variadic(Type),
104
+ }
105
+
106
+ #[derive(Debug, Clone, PartialEq)]
107
+ pub struct Type {
108
+ pub name: String,
109
+ pub generics: Vec<Type>,
110
+ }
111
+
112
+ #[derive(Debug, Clone, PartialEq)]
113
+ pub struct ReturnType {
114
+ pub name: String,
115
+ pub generics: Vec<GenericParam>,
116
+ }
117
+
118
+ #[derive(Debug, Clone, PartialEq)]
119
+ pub enum FnBody {
120
+ Expr(Expr),
121
+ Block(Block),
122
+ }
123
+
124
+ // ---------- Statements ----------
125
+
126
+ #[derive(Debug, Clone, PartialEq)]
127
+ pub struct Block {
128
+ pub stmts: Vec<Stmt>,
129
+ }
130
+
131
+ #[derive(Debug, Clone, PartialEq)]
132
+ pub enum Stmt {
133
+ Assign(Assign),
134
+ Break,
135
+ Continue,
136
+ Assert(Expr),
137
+ For(For),
138
+ While(While),
139
+ If(If),
140
+ Match(Match),
141
+ Return(Option<Expr>),
142
+ Todo,
143
+ Expr(Expr),
144
+ }
145
+
146
+ #[derive(Debug, Clone, PartialEq)]
147
+ pub struct Assign {
148
+ pub targets: Vec<String>,
149
+ pub values: Vec<Expr>,
150
+ }
151
+
152
+ #[derive(Debug, Clone, PartialEq)]
153
+ pub struct For {
154
+ pub vars: Vec<String>,
155
+ pub iter: Expr,
156
+ pub body: Block,
157
+ }
158
+
159
+ #[derive(Debug, Clone, PartialEq)]
160
+ pub struct While {
161
+ pub condition: Expr,
162
+ pub body: Block,
163
+ }
164
+
165
+ #[derive(Debug, Clone, PartialEq)]
166
+ pub struct If {
167
+ pub condition: Expr,
168
+ pub body: Block,
169
+ pub else_ifs: Vec<ElseIf>,
170
+ pub else_: Option<Block>,
171
+ }
172
+
173
+ #[derive(Debug, Clone, PartialEq)]
174
+ pub struct ElseIf {
175
+ pub condition: Expr,
176
+ pub body: Block,
177
+ }
178
+
179
+ #[derive(Debug, Clone, PartialEq)]
180
+ pub struct Match {
181
+ pub subjects: Vec<Expr>,
182
+ pub cases: Vec<Case>,
183
+ }
184
+
185
+ #[derive(Debug, Clone, PartialEq)]
186
+ pub struct Case {
187
+ pub patterns: Vec<CasePattern>,
188
+ pub body: Block,
189
+ }
190
+
191
+ #[derive(Debug, Clone, PartialEq)]
192
+ pub enum CasePattern {
193
+ Class { name: String, fields: Vec<CasePattern> },
194
+ String(String),
195
+ Int(i64),
196
+ Float(f64),
197
+ Name(String),
198
+ Wildcard,
199
+ }
200
+
201
+ // ---------- Expressions ----------
202
+
203
+ /// Expressions are the core of the language. Operator precedence is already
204
+ /// resolved by the tree-sitter parser (via the PREC table in grammar.js), so
205
+ /// the tree structure here directly reflects evaluation order.
206
+ #[derive(Debug, Clone, PartialEq)]
207
+ pub enum Expr {
208
+ /// Arithmetic / bitwise / range: `a + b`, `a .. b`, etc.
209
+ Binary(Box<BinaryExpr>),
210
+ /// Prefix `+` or `-`
211
+ Unary(Box<UnaryExpr>),
212
+ /// `&&` / `||`
213
+ Bool(Box<BoolExpr>),
214
+ /// `!expr`
215
+ Not(Box<Expr>),
216
+ /// `<`, `<=`, `==`, `!=`, `>=`, `>`, `<>`
217
+ Compare(Box<CompareExpr>),
218
+ /// `cond ? then : else`
219
+ Ternary(Box<TernaryExpr>),
220
+ /// `fnName(args…)`
221
+ FnCall(FnCall),
222
+ /// `TypeName(field: value, …)`
223
+ ClassCall(ClassCall),
224
+ /// `expr.field` or `expr.method(…)`
225
+ Attribute(Box<AttributeExpr>),
226
+ /// `{expr}` — grouped/parenthesized expression
227
+ Paren(Box<Expr>),
228
+ String(StringExpr),
229
+ Int(i64),
230
+ Float(f64),
231
+ Self_,
232
+ Var(String),
233
+ TypeName(String),
234
+ }
235
+
236
+ #[derive(Debug, Clone, PartialEq)]
237
+ pub struct BinaryExpr {
238
+ pub op: BinOp,
239
+ pub left: Expr,
240
+ pub right: Expr,
241
+ }
242
+
243
+ #[derive(Debug, Clone, PartialEq)]
244
+ pub enum BinOp {
245
+ Add,
246
+ Sub,
247
+ Mul,
248
+ Div,
249
+ Mod,
250
+ BitOr,
251
+ BitAnd,
252
+ Xor,
253
+ Shl,
254
+ Shr,
255
+ Range,
256
+ }
257
+
258
+ #[derive(Debug, Clone, PartialEq)]
259
+ pub struct UnaryExpr {
260
+ pub op: UnOp,
261
+ pub operand: Expr,
262
+ }
263
+
264
+ #[derive(Debug, Clone, PartialEq)]
265
+ pub enum UnOp {
266
+ Pos,
267
+ Neg,
268
+ }
269
+
270
+ #[derive(Debug, Clone, PartialEq)]
271
+ pub struct BoolExpr {
272
+ pub op: BoolOp,
273
+ pub left: Expr,
274
+ pub right: Expr,
275
+ }
276
+
277
+ #[derive(Debug, Clone, PartialEq)]
278
+ pub enum BoolOp {
279
+ And,
280
+ Or,
281
+ }
282
+
283
+ #[derive(Debug, Clone, PartialEq)]
284
+ pub struct CompareExpr {
285
+ pub op: CmpOp,
286
+ pub left: Expr,
287
+ pub right: Expr,
288
+ }
289
+
290
+ #[derive(Debug, Clone, PartialEq)]
291
+ pub enum CmpOp {
292
+ Lt,
293
+ Lte,
294
+ Eq,
295
+ Neq,
296
+ Gte,
297
+ Gt,
298
+ NotEq2,
299
+ }
300
+
301
+ #[derive(Debug, Clone, PartialEq)]
302
+ pub struct TernaryExpr {
303
+ pub condition: Expr,
304
+ pub then: Expr,
305
+ pub else_: Expr,
306
+ }
307
+
308
+ #[derive(Debug, Clone, PartialEq)]
309
+ pub struct FnCall {
310
+ pub name: String,
311
+ pub args: Vec<Arg>,
312
+ }
313
+
314
+ #[derive(Debug, Clone, PartialEq)]
315
+ pub enum Arg {
316
+ Positional(Expr),
317
+ Keyword { name: String, value: Expr },
318
+ Pair { key: String, value: Expr },
319
+ }
320
+
321
+ #[derive(Debug, Clone, PartialEq)]
322
+ pub struct ClassCall {
323
+ pub type_name: String,
324
+ pub fields: Vec<FieldArg>,
325
+ }
326
+
327
+ #[derive(Debug, Clone, PartialEq)]
328
+ pub struct FieldArg {
329
+ pub name: String,
330
+ pub value: Expr,
331
+ }
332
+
333
+ #[derive(Debug, Clone, PartialEq)]
334
+ pub struct AttributeExpr {
335
+ pub object: Expr,
336
+ pub attr: AttrKind,
337
+ }
338
+
339
+ #[derive(Debug, Clone, PartialEq)]
340
+ pub enum AttrKind {
341
+ Field(String),
342
+ Method(FnCall),
343
+ }
344
+
345
+ #[derive(Debug, Clone, PartialEq)]
346
+ pub struct StringExpr {
347
+ pub parts: Vec<StringPart>,
348
+ }
349
+
350
+ #[derive(Debug, Clone, PartialEq)]
351
+ pub enum StringPart {
352
+ Text(String),
353
+ Interp(Expr),
354
+ }
plum-core/src/formatter.rs ADDED
@@ -0,0 +1,19 @@
1
+ // plum-core/src/formatter.rs
2
+ #[derive(Debug)]
3
+ pub enum FormatterError {
4
+ TopiaryCoreError(String),
5
+ }
6
+
7
+ impl std::fmt::Display for FormatterError {
8
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
9
+ match self {
10
+ FormatterError::TopiaryCoreError(msg) => write!(f, "Topiary error: {msg}"),
11
+ }
12
+ }
13
+ }
14
+
15
+ impl std::error::Error for FormatterError {}
16
+
17
+ pub fn format_source(_source: &str) -> Result<String, FormatterError> {
18
+ unimplemented!()
19
+ }
plum-core/src/lib.rs ADDED
@@ -0,0 +1,6 @@
1
+ pub mod ast;
2
+ pub mod parser;
3
+ pub mod formatter;
4
+
5
+ pub use formatter::{format_source, FormatterError};
6
+ pub use parser::AstParser;
plum-core/src/parser.rs ADDED
@@ -0,0 +1,764 @@
1
+ use tree_sitter::Node;
2
+ use crate::ast::*;
3
+
4
+ pub struct AstParser<'a> {
5
+ source: &'a [u8],
6
+ }
7
+
8
+ impl<'a> AstParser<'a> {
9
+ pub fn new(source: &'a str) -> Self {
10
+ AstParser { source: source.as_bytes() }
11
+ }
12
+
13
+ fn text(&self, node: Node) -> String {
14
+ node.utf8_text(self.source).unwrap_or("").to_string()
15
+ }
16
+
17
+ /// Peel transparent `expression` / `primary_expression` wrapper nodes.
18
+ fn unwrap_expr_node<'b>(&self, node: Node<'b>) -> Node<'b> {
19
+ match node.kind() {
20
+ "expression" | "primary_expression" => {
21
+ node.named_child(0).map(|c| self.unwrap_expr_node(c)).unwrap_or(node)
22
+ }
23
+ _ => node,
24
+ }
25
+ }
26
+
27
+ /// Collect named children of `node` that have the given `kind`.
28
+ fn children_of_kind(&self, node: Node<'a>, kind: &str) -> Vec<Node<'a>> {
29
+ let mut cursor = node.walk();
30
+ node.named_children(&mut cursor)
31
+ .filter(|n| n.kind() == kind)
32
+ .collect()
33
+ }
34
+
35
+ // ---- top level --------------------------------------------------------
36
+
37
+ pub fn parse_source(&self, node: Node) -> Source {
38
+ assert_eq!(node.kind(), "source");
39
+ let mut module = None;
40
+ let mut imports = Vec::new();
41
+ let mut items = Vec::new();
42
+ let mut cursor = node.walk();
43
+ for child in node.named_children(&mut cursor) {
44
+ match child.kind() {
45
+ "module" => module = Some(self.parse_module(child)),
46
+ "import" => imports.push(self.parse_import(child)),
47
+ "class" => items.push(Item::Class(self.parse_class(child))),
48
+ "trait" => items.push(Item::Trait(self.parse_trait(child))),
49
+ "enum" => items.push(Item::Enum(self.parse_enum(child))),
50
+ "fn" => items.push(Item::Fn(self.parse_fn(child))),
51
+ "const" => items.push(Item::Const(self.parse_const(child))),
52
+ _ => {}
53
+ }
54
+ }
55
+ Source { module, imports, items }
56
+ }
57
+
58
+ fn parse_module(&self, node: Node) -> Module {
59
+ // module: "module" mod_identifier
60
+ // named_child(0) = mod_identifier
61
+ let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
62
+ Module { name }
63
+ }
64
+
65
+ fn parse_import(&self, node: Node) -> Import {
66
+ // import: "import" url — url is the only named child
67
+ let path = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
68
+ Import { path }
69
+ }
70
+
71
+ // ---- class / trait / enum ---------------------------------------------
72
+
73
+ fn parse_class(&self, node: Node) -> Class {
74
+ // class: "type" type_identifier ("(" type_identifier,* ")")? generics? "=" body
75
+ // Named children in order: type_identifier (name), type_identifier* (implements), field*
76
+ let mut cursor = node.walk();
77
+ let named: Vec<Node> = node.named_children(&mut cursor).collect();
78
+
79
+ let name = named.first().map(|n| self.text(*n)).unwrap_or_default();
80
+
81
+ // implements = type_identifiers that appear before any `field` node
82
+ let implements: Vec<String> = named[1..]
83
+ .iter()
84
+ .take_while(|n| n.kind() == "type_identifier")
85
+ .map(|n| self.text(*n))
86
+ .collect();
87
+
88
+ let generics = self.parse_generics_field(node);
89
+
90
+ let fields: Vec<Field> = named
91
+ .iter()
92
+ .filter(|n| n.kind() == "field")
93
+ .map(|n| self.parse_field(*n))
94
+ .collect();
95
+
96
+ Class { name, implements, generics, fields }
97
+ }
98
+
99
+ fn parse_generics_field(&self, node: Node) -> Vec<GenericParam> {
100
+ // generics: "(" generic_type,* ")"
101
+ // The generics node is not field-named in a straightforward way; look
102
+ // for a child whose kind is "generics".
103
+ let generics_node = self.children_of_kind(node, "generics").into_iter().next();
104
+ generics_node.map(|g| {
105
+ self.children_of_kind(g, "generic_type")
106
+ .into_iter()
107
+ .map(|n| self.parse_generic_type(n))
108
+ .collect()
109
+ }).unwrap_or_default()
110
+ }
111
+
112
+ fn parse_generic_type(&self, node: Node) -> GenericParam {
113
+ // generic_type: generic (":" sep1(type_identifier, "+"))?
114
+ // named_child(0) = generic (single letter)
115
+ // remaining named children = bound type_identifiers
116
+ let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
117
+ let bounds = (1..node.named_child_count())
118
+ .filter_map(|i| node.named_child(i as u32))
119
+ .map(|n| self.text(n))
120
+ .collect();
121
+ GenericParam { name, bounds }
122
+ }
123
+
124
+ fn parse_field(&self, node: Node) -> Field {
125
+ // class_field (aliased to field): var_identifier ":" type
126
+ let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
127
+ let ty = node
128
+ .named_child(1)
129
+ .map(|n| self.parse_type(n))
130
+ .unwrap_or(Type { name: String::new(), generics: vec![] });
131
+ Field { name, ty }
132
+ }
133
+
134
+ fn parse_trait(&self, node: Node) -> Trait {
135
+ // trait: "trait" type_identifier generics? "=" body
136
+ let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
137
+ let generics = self.parse_generics_field(node);
138
+ let methods = self.children_of_kind(node, "field")
139
+ .into_iter()
140
+ .map(|f| self.parse_trait_method(f))
141
+ .collect();
142
+ Trait { name, generics, methods }
143
+ }
144
+
145
+ fn parse_trait_method(&self, node: Node) -> TraitMethod {
146
+ // trait_field (aliased to field): fn_identifier "(" params ")" ("->" return_type)?
147
+ let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
148
+ let params = self.collect_params_from(node);
149
+ let returns = node
150
+ .named_children(&mut node.walk())
151
+ .find(|n| n.kind() == "return_type")
152
+ .map(|n| self.parse_return_type(n));
153
+ TraitMethod { name, params, returns }
154
+ }
155
+
156
+ fn parse_enum(&self, node: Node) -> Enum {
157
+ let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
158
+ let variants = self.children_of_kind(node, "field")
159
+ .into_iter()
160
+ .map(|f| self.parse_enum_variant(f))
161
+ .collect();
162
+ Enum { name, variants }
163
+ }
164
+
165
+ fn parse_enum_variant(&self, node: Node) -> EnumVariant {
166
+ // enum_field (aliased to field): "|" type_identifier ("(" type_identifier,* ")")?
167
+ // named children: type_identifier (name), type_identifier* (fields inside "()")
168
+ let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
169
+ let fields: Vec<String> = (1..node.named_child_count())
170
+ .filter_map(|i| node.named_child(i as u32))
171
+ .filter(|n| n.kind() == "type_identifier")
172
+ .map(|n| self.text(n))
173
+ .collect();
174
+ EnumVariant { name, fields }
175
+ }
176
+
177
+ // ---- functions --------------------------------------------------------
178
+
179
+ fn parse_fn(&self, node: Node) -> Fn {
180
+ // fn: fn_identifier type? "(" param,* ")" ("->" return_type)? "=" body_or_expr
181
+ // Named children: fn_identifier, type?, param*, return_type?, body/expr
182
+ let mut cursor = node.walk();
183
+ let named: Vec<Node> = node.named_children(&mut cursor).collect();
184
+
185
+ let name = named.first().map(|n| self.text(*n)).unwrap_or_default();
186
+
187
+ // type param — the `<Cat>` node has kind "type" and contains a type_identifier
188
+ let type_param = named
189
+ .iter()
190
+ .find(|n| n.kind() == "type")
191
+ .and_then(|t| t.named_child(0))
192
+ .map(|n| self.text(n));
193
+
194
+ let params: Vec<Param> = named
195
+ .iter()
196
+ .filter(|n| n.kind() == "param")
197
+ .map(|n| self.parse_param(*n))
198
+ .collect();
199
+
200
+ let returns = named
201
+ .iter()
202
+ .find(|n| n.kind() == "return_type")
203
+ .map(|n| self.parse_return_type(*n));
204
+
205
+ // body is the last named child — it is either a `body` node (block)
206
+ // or an expression node when the body is a single expression.
207
+ let body = named.last().and_then(|last| {
208
+ match last.kind() {
209
+ // Skip non-body trailing nodes
210
+ "fn_identifier" | "type" | "param" | "return_type" => None,
211
+ "body" => Some(FnBody::Block(self.parse_block(*last))),
212
+ _ => {
213
+ let unwrapped = self.unwrap_expr_node(*last);
214
+ Some(FnBody::Expr(self.parse_expression(unwrapped)))
215
+ }
216
+ }
217
+ }).unwrap_or(FnBody::Block(Block { stmts: vec![] }));
218
+
219
+ Fn { name, type_param, params, returns, body }
220
+ }
221
+
222
+ fn parse_const(&self, node: Node) -> Const {
223
+ // const: const_identifier "=" expression
224
+ let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
225
+ let value = node
226
+ .named_child(1)
227
+ .map(|n| {
228
+ let unwrapped = self.unwrap_expr_node(n);
229
+ self.parse_expression(unwrapped)
230
+ })
231
+ .unwrap_or(Expr::Int(0));
232
+ Const { name, value }
233
+ }
234
+
235
+ // ---- params / return type ---------------------------------------------
236
+
237
+ /// Collect `param` named children from any node that has them.
238
+ fn collect_params_from(&self, node: Node) -> Vec<Param> {
239
+ self.children_of_kind(node, "param")
240
+ .into_iter()
241
+ .map(|n| self.parse_param(n))
242
+ .collect()
243
+ }
244
+
245
+ fn parse_param(&self, node: Node) -> Param {
246
+ // param: var_identifier ":" (type | variadic_type) ("=" expression)?
247
+ let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
248
+ let ty = node.named_child(1).map(|n| {
249
+ if n.kind() == "variadic_type" {
250
+ let inner = n.named_child(0)
251
+ .map(|t| self.parse_type(t))
252
+ .unwrap_or(Type { name: String::new(), generics: vec![] });
253
+ ParamType::Variadic(inner)
254
+ } else {
255
+ ParamType::Type(self.parse_type(n))
256
+ }
257
+ }).unwrap_or(ParamType::Type(Type { name: String::new(), generics: vec![] }));
258
+ let default = node.named_child(2).map(|n| {
259
+ let unwrapped = self.unwrap_expr_node(n);
260
+ self.parse_expression(unwrapped)
261
+ });
262
+ Param { name, ty, default }
263
+ }
264
+
265
+ fn parse_type(&self, node: Node) -> Type {
266
+ // type: type_identifier ("[" type,* "]")?
267
+ // named_child(0) = type_identifier
268
+ let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_else(|| self.text(node));
269
+ let mut cursor = node.walk();
270
+ let generics: Vec<Type> = node
271
+ .named_children(&mut cursor)
272
+ .skip(1)
273
+ .filter(|n| n.kind() == "type")
274
+ .map(|n| self.parse_type(n))
275
+ .collect();
276
+ Type { name, generics }
277
+ }
278
+
279
+ fn parse_return_type(&self, node: Node) -> ReturnType {
280
+ // return_type: type_identifier generics?
281
+ // named_child(0) = type_identifier
282
+ let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
283
+ let generics = self.parse_generics_field(node);
284
+ ReturnType { name, generics }
285
+ }
286
+
287
+ // ---- statements -------------------------------------------------------
288
+
289
+ fn parse_block(&self, node: Node) -> Block {
290
+ let mut cursor = node.walk();
291
+ let stmts = node
292
+ .named_children(&mut cursor)
293
+ .filter_map(|n| self.parse_stmt(n))
294
+ .collect();
295
+ Block { stmts }
296
+ }
297
+
298
+ fn parse_stmt(&self, node: Node) -> Option<Stmt> {
299
+ let node = self.unwrap_expr_node(node);
300
+ Some(match node.kind() {
301
+ "assign" => Stmt::Assign(self.parse_assign(node)),
302
+ "break" => Stmt::Break,
303
+ "continue" => Stmt::Continue,
304
+ "return" => {
305
+ let expr = node.named_child(0).map(|n| {
306
+ let u = self.unwrap_expr_node(n);
307
+ self.parse_expression(u)
308
+ });
309
+ Stmt::Return(expr)
310
+ }
311
+ "todo" => Stmt::Todo,
312
+ "assert" => {
313
+ let expr = node.named_child(0)
314
+ .map(|n| { let u = self.unwrap_expr_node(n); self.parse_expression(u) })
315
+ .unwrap_or(Expr::Int(0));
316
+ Stmt::Assert(expr)
317
+ }
318
+ "for" => Stmt::For(self.parse_for(node)),
319
+ "while" => Stmt::While(self.parse_while(node)),
320
+ "if" => Stmt::If(self.parse_if(node)),
321
+ "match" => Stmt::Match(self.parse_match(node)),
322
+ kind if is_expression_kind(kind) => Stmt::Expr(self.parse_expression(node)),
323
+ _ => return None,
324
+ })
325
+ }
326
+
327
+ fn parse_assign(&self, node: Node) -> Assign {
328
+ // assign: commaSep1(var_identifier) "=" commaSep1(expression)
329
+ // Named children are all var_identifiers then all expressions.
330
+ // We split at the first non-var_identifier.
331
+ let mut cursor = node.walk();
332
+ let named: Vec<Node> = node.named_children(&mut cursor).collect();
333
+ let split = named.iter().position(|n| n.kind() != "var_identifier").unwrap_or(named.len());
334
+ let targets = named[..split].iter().map(|n| self.text(*n)).collect();
335
+ let values = named[split..]
336
+ .iter()
337
+ .map(|n| { let u = self.unwrap_expr_node(*n); self.parse_expression(u) })
338
+ .collect();
339
+ Assign { targets, values }
340
+ }
341
+
342
+ fn parse_for(&self, node: Node) -> For {
343
+ // for: "for" commaSep1(var_identifier) "in" primary_expression body
344
+ // Named children: var_identifier+, primary_expression (iter), body
345
+ let mut cursor = node.walk();
346
+ let named: Vec<Node> = node.named_children(&mut cursor).collect();
347
+
348
+ let split = named.iter().position(|n| n.kind() != "var_identifier").unwrap_or(0);
349
+ let vars = named[..split].iter().map(|n| self.text(*n)).collect();
350
+
351
+ let iter = named.get(split)
352
+ .map(|n| { let u = self.unwrap_expr_node(*n); self.parse_primary_expression(u) })
353
+ .unwrap_or(Expr::Int(0));
354
+
355
+ let body = named.last()
356
+ .filter(|n| n.kind() == "body")
357
+ .map(|n| self.parse_block(*n))
358
+ .unwrap_or(Block { stmts: vec![] });
359
+
360
+ For { vars, iter, body }
361
+ }
362
+
363
+ fn parse_while(&self, node: Node) -> While {
364
+ // while: "while" expression body
365
+ let mut cursor = node.walk();
366
+ let named: Vec<Node> = node.named_children(&mut cursor).collect();
367
+ let condition = named.first()
368
+ .map(|n| { let u = self.unwrap_expr_node(*n); self.parse_expression(u) })
369
+ .unwrap_or(Expr::Int(0));
370
+ let body = named.last()
371
+ .filter(|n| n.kind() == "body")
372
+ .map(|n| self.parse_block(*n))
373
+ .unwrap_or(Block { stmts: vec![] });
374
+ While { condition, body }
375
+ }
376
+
377
+ fn parse_if(&self, node: Node) -> If {
378
+ // if: "if" expression body else_if* else?
379
+ let mut cursor = node.walk();
380
+ let named: Vec<Node> = node.named_children(&mut cursor).collect();
381
+
382
+ let condition = named.first()
383
+ .map(|n| { let u = self.unwrap_expr_node(*n); self.parse_expression(u) })
384
+ .unwrap_or(Expr::Int(0));
385
+ let body = named.get(1)
386
+ .filter(|n| n.kind() == "body")
387
+ .map(|n| self.parse_block(*n))
388
+ .unwrap_or(Block { stmts: vec![] });
389
+ let else_ifs = named.iter()
390
+ .filter(|n| n.kind() == "else_if")
391
+ .map(|n| self.parse_else_if(*n))
392
+ .collect();
393
+ let else_ = named.iter()
394
+ .find(|n| n.kind() == "else")
395
+ .and_then(|n| n.named_child(0))
396
+ .map(|n| self.parse_block(n));
397
+ If { condition, body, else_ifs, else_ }
398
+ }
399
+
400
+ fn parse_else_if(&self, node: Node) -> ElseIf {
401
+ // else_if: "else if" expression body
402
+ let mut cursor = node.walk();
403
+ let named: Vec<Node> = node.named_children(&mut cursor).collect();
404
+ let condition = named.first()
405
+ .map(|n| { let u = self.unwrap_expr_node(*n); self.parse_expression(u) })
406
+ .unwrap_or(Expr::Int(0));
407
+ let body = named.last()
408
+ .filter(|n| n.kind() == "body")
409
+ .map(|n| self.parse_block(*n))
410
+ .unwrap_or(Block { stmts: vec![] });
411
+ ElseIf { condition, body }
412
+ }
413
+
414
+ fn parse_match(&self, node: Node) -> Match {
415
+ // match: "match" commaSep1(expression) "is" case+
416
+ let mut cursor = node.walk();
417
+ let named: Vec<Node> = node.named_children(&mut cursor).collect();
418
+ let split = named.iter().position(|n| n.kind() == "case").unwrap_or(named.len());
419
+ let subjects = named[..split]
420
+ .iter()
421
+ .map(|n| { let u = self.unwrap_expr_node(*n); self.parse_expression(u) })
422
+ .collect();
423
+ let cases = named[split..]
424
+ .iter()
425
+ .filter(|n| n.kind() == "case")
426
+ .map(|n| self.parse_case(*n))
427
+ .collect();
428
+ Match { subjects, cases }
429
+ }
430
+
431
+ fn parse_case(&self, node: Node) -> Case {
432
+ // case: commaSep1(case_pattern) "=>" body
433
+ let mut cursor = node.walk();
434
+ let named: Vec<Node> = node.named_children(&mut cursor).collect();
435
+ let patterns = named.iter()
436
+ .filter(|n| n.kind() == "case_pattern")
437
+ .map(|n| self.parse_case_pattern(*n))
438
+ .collect();
439
+ let body = named.iter()
440
+ .find(|n| n.kind() == "body")
441
+ .map(|n| self.parse_block(*n))
442
+ .unwrap_or(Block { stmts: vec![] });
443
+ Case { patterns, body }
444
+ }
445
+
446
+ fn parse_case_pattern(&self, node: Node) -> CasePattern {
447
+ // case_pattern wraps: class_pattern | string | integer | float | dotted_name | "_"
448
+ let inner = node.named_child(0).unwrap_or(node);
449
+ match inner.kind() {
450
+ "class_pattern" => {
451
+ // class_pattern: dotted_name "(" case_pattern,* ")"
452
+ let name = inner.named_child(0).map(|n| self.text(n)).unwrap_or_default();
453
+ let fields = (1..inner.named_child_count())
454
+ .filter_map(|i| inner.named_child(i as u32))
455
+ .filter(|n| n.kind() == "case_pattern")
456
+ .map(|n| self.parse_case_pattern(n))
457
+ .collect();
458
+ CasePattern::Class { name, fields }
459
+ }
460
+ "string" => CasePattern::String(self.parse_string_raw(inner)),
461
+ "integer" => CasePattern::Int(self.parse_integer(inner)),
462
+ "float" => CasePattern::Float(self.parse_float(inner)),
463
+ "dotted_name" => CasePattern::Name(self.text(inner)),
464
+ _ => {
465
+ let t = self.text(inner);
466
+ if t == "_" { CasePattern::Wildcard } else { CasePattern::Name(t) }
467
+ }
468
+ }
469
+ }
470
+
471
+ // ---- expressions ------------------------------------------------------
472
+
473
+ pub fn parse_expression(&self, node: Node) -> Expr {
474
+ let node = self.unwrap_expr_node(node);
475
+ match node.kind() {
476
+ "comparison_operator" => self.parse_compare(node),
477
+ "not_operator" => {
478
+ let arg = node.named_child(0)
479
+ .map(|n| { let u = self.unwrap_expr_node(n); self.parse_expression(u) })
480
+ .unwrap_or(Expr::Int(0));
481
+ Expr::Not(Box::new(arg))
482
+ }
483
+ "boolean_operator" => self.parse_bool_op(node),
484
+ "ternary_expression" => self.parse_ternary(node),
485
+ _ => self.parse_primary_expression(node),
486
+ }
487
+ }
488
+
489
+ pub fn parse_primary_expression(&self, node: Node) -> Expr {
490
+ let node = self.unwrap_expr_node(node);
491
+ match node.kind() {
492
+ "binary_operator" => self.parse_binary(node),
493
+ "unary_operator" => self.parse_unary(node),
494
+ "attribute" => self.parse_attribute(node),
495
+ "fn_call" => Expr::FnCall(self.parse_fn_call(node)),
496
+ "class_call" => Expr::ClassCall(self.parse_class_call(node)),
497
+ "parenthesized_expression" => {
498
+ // parenthesized_expression: "{" expression "}"
499
+ let inner = node.named_child(0)
500
+ .map(|n| { let u = self.unwrap_expr_node(n); self.parse_expression(u) })
501
+ .unwrap_or(Expr::Int(0));
502
+ Expr::Paren(Box::new(inner))
503
+ }
504
+ "string" => Expr::String(self.parse_string(node)),
505
+ "integer" => Expr::Int(self.parse_integer(node)),
506
+ "float" => Expr::Float(self.parse_float(node)),
507
+ "self" => Expr::Self_,
508
+ "var_identifier" => Expr::Var(self.text(node)),
509
+ "type_identifier" => Expr::TypeName(self.text(node)),
510
+ _ => Expr::Var(self.text(node)),
511
+ }
512
+ }
513
+
514
+ fn parse_binary(&self, node: Node) -> Expr {
515
+ // binary_operator: primary_expression op primary_expression
516
+ // "operator" is an unnamed child; left/right are field-named
517
+ let op_text = self.find_unnamed_operator(node);
518
+ let op = match op_text.as_str() {
519
+ "+" => BinOp::Add,
520
+ "-" => BinOp::Sub,
521
+ "*" => BinOp::Mul,
522
+ "/" => BinOp::Div,
523
+ "%" => BinOp::Mod,
524
+ "|" => BinOp::BitOr,
525
+ "&" => BinOp::BitAnd,
526
+ "^" => BinOp::Xor,
527
+ "<<" => BinOp::Shl,
528
+ ">>" => BinOp::Shr,
529
+ ".." => BinOp::Range,
530
+ _ => BinOp::Add,
531
+ };
532
+ let mut cursor = node.walk();
533
+ let named: Vec<Node> = node.named_children(&mut cursor).collect();
534
+ let left = named.first()
535
+ .map(|n| { let u = self.unwrap_expr_node(*n); self.parse_primary_expression(u) })
536
+ .unwrap_or(Expr::Int(0));
537
+ let right = named.last()
538
+ .map(|n| { let u = self.unwrap_expr_node(*n); self.parse_primary_expression(u) })
539
+ .unwrap_or(Expr::Int(0));
540
+ Expr::Binary(Box::new(BinaryExpr { op, left, right }))
541
+ }
542
+
543
+ fn parse_unary(&self, node: Node) -> Expr {
544
+ let op_text = self.find_unnamed_operator(node);
545
+ let op = if op_text == "-" { UnOp::Neg } else { UnOp::Pos };
546
+ let operand = node.named_child(0)
547
+ .map(|n| { let u = self.unwrap_expr_node(n); self.parse_primary_expression(u) })
548
+ .unwrap_or(Expr::Int(0));
549
+ Expr::Unary(Box::new(UnaryExpr { op, operand }))
550
+ }
551
+
552
+ fn parse_bool_op(&self, node: Node) -> Expr {
553
+ let op_text = self.find_unnamed_operator(node);
554
+ let op = if op_text == "&&" { BoolOp::And } else { BoolOp::Or };
555
+ let mut cursor = node.walk();
556
+ let named: Vec<Node> = node.named_children(&mut cursor).collect();
557
+ let left = named.first()
558
+ .map(|n| { let u = self.unwrap_expr_node(*n); self.parse_expression(u) })
559
+ .unwrap_or(Expr::Int(0));
560
+ let right = named.last()
561
+ .map(|n| { let u = self.unwrap_expr_node(*n); self.parse_expression(u) })
562
+ .unwrap_or(Expr::Int(0));
563
+ Expr::Bool(Box::new(BoolExpr { op, left, right }))
564
+ }
565
+
566
+ fn parse_compare(&self, node: Node) -> Expr {
567
+ let op_text = self.find_unnamed_operator(node);
568
+ let op = match op_text.as_str() {
569
+ "<" => CmpOp::Lt,
570
+ "<=" => CmpOp::Lte,
571
+ "==" => CmpOp::Eq,
572
+ "!=" => CmpOp::Neq,
573
+ ">=" => CmpOp::Gte,
574
+ ">" => CmpOp::Gt,
575
+ "<>" => CmpOp::NotEq2,
576
+ _ => CmpOp::Eq,
577
+ };
578
+ let mut cursor = node.walk();
579
+ let named: Vec<Node> = node.named_children(&mut cursor).collect();
580
+ let left = named.first()
581
+ .map(|n| { let u = self.unwrap_expr_node(*n); self.parse_primary_expression(u) })
582
+ .unwrap_or(Expr::Int(0));
583
+ let right = named.last()
584
+ .map(|n| { let u = self.unwrap_expr_node(*n); self.parse_primary_expression(u) })
585
+ .unwrap_or(Expr::Int(0));
586
+ Expr::Compare(Box::new(CompareExpr { op, left, right }))
587
+ }
588
+
589
+ fn parse_ternary(&self, node: Node) -> Expr {
590
+ // ternary_expression: expression "?" expression ":" expression
591
+ let mut cursor = node.walk();
592
+ let named: Vec<Node> = node.named_children(&mut cursor).collect();
593
+ let condition = named.first()
594
+ .map(|n| { let u = self.unwrap_expr_node(*n); self.parse_expression(u) })
595
+ .unwrap_or(Expr::Int(0));
596
+ let then = named.get(1)
597
+ .map(|n| { let u = self.unwrap_expr_node(*n); self.parse_expression(u) })
598
+ .unwrap_or(Expr::Int(0));
599
+ let else_ = named.get(2)
600
+ .map(|n| { let u = self.unwrap_expr_node(*n); self.parse_expression(u) })
601
+ .unwrap_or(Expr::Int(0));
602
+ Expr::Ternary(Box::new(TernaryExpr { condition, then, else_ }))
603
+ }
604
+
605
+ fn parse_attribute(&self, node: Node) -> Expr {
606
+ // attribute: primary_expression "." (var_identifier | fn_call)
607
+ let mut cursor = node.walk();
608
+ let named: Vec<Node> = node.named_children(&mut cursor).collect();
609
+ let object = named.first()
610
+ .map(|n| { let u = self.unwrap_expr_node(*n); self.parse_primary_expression(u) })
611
+ .unwrap_or(Expr::Int(0));
612
+ let attr = named.get(1).map(|n| {
613
+ if n.kind() == "fn_call" {
614
+ AttrKind::Method(self.parse_fn_call(*n))
615
+ } else {
616
+ AttrKind::Field(self.text(*n))
617
+ }
618
+ }).unwrap_or(AttrKind::Field(String::new()));
619
+ Expr::Attribute(Box::new(AttributeExpr { object, attr }))
620
+ }
621
+
622
+ fn parse_fn_call(&self, node: Node) -> FnCall {
623
+ // fn_call: fn_identifier fn_argument_list
624
+ let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
625
+ let args = node.named_child(1)
626
+ .map(|args_node| {
627
+ let mut cursor = args_node.walk();
628
+ args_node.named_children(&mut cursor)
629
+ .map(|n| self.parse_arg(n))
630
+ .collect()
631
+ })
632
+ .unwrap_or_default();
633
+ FnCall { name, args }
634
+ }
635
+
636
+ fn parse_arg(&self, node: Node) -> Arg {
637
+ match node.kind() {
638
+ "keyword_argument" => {
639
+ // keyword_argument: var_identifier "=" expression
640
+ let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
641
+ let value = node.named_child(1)
642
+ .map(|n| { let u = self.unwrap_expr_node(n); self.parse_expression(u) })
643
+ .unwrap_or(Expr::Int(0));
644
+ Arg::Keyword { name, value }
645
+ }
646
+ "pair_argument" => {
647
+ // pair_argument: string "=>" expression
648
+ let key = node.named_child(0).map(|n| self.parse_string_raw(n)).unwrap_or_default();
649
+ let value = node.named_child(1)
650
+ .map(|n| { let u = self.unwrap_expr_node(n); self.parse_expression(u) })
651
+ .unwrap_or(Expr::Int(0));
652
+ Arg::Pair { key, value }
653
+ }
654
+ _ => {
655
+ let u = self.unwrap_expr_node(node);
656
+ Arg::Positional(self.parse_expression(u))
657
+ }
658
+ }
659
+ }
660
+
661
+ fn parse_class_call(&self, node: Node) -> ClassCall {
662
+ // class_call: type_identifier class_argument_list
663
+ let type_name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
664
+ let fields = node.named_child(1)
665
+ .map(|args_node| {
666
+ // class_argument_list: "(" (var_identifier ":" expression),* ")"
667
+ // Named children alternate: var_identifier, expression, ...
668
+ let mut cursor = args_node.walk();
669
+ let named: Vec<Node> = args_node.named_children(&mut cursor).collect();
670
+ named.chunks(2).filter_map(|chunk| {
671
+ if chunk.len() == 2 {
672
+ let name = self.text(chunk[0]);
673
+ let u = self.unwrap_expr_node(chunk[1]);
674
+ Some(FieldArg { name, value: self.parse_expression(u) })
675
+ } else {
676
+ None
677
+ }
678
+ }).collect()
679
+ })
680
+ .unwrap_or_default();
681
+ ClassCall { type_name, fields }
682
+ }
683
+
684
+ // ---- string literals --------------------------------------------------
685
+
686
+ fn parse_string(&self, node: Node) -> StringExpr {
687
+ let mut cursor = node.walk();
688
+ let parts = node
689
+ .named_children(&mut cursor)
690
+ .filter_map(|n| match n.kind() {
691
+ "string_content" => Some(StringPart::Text(self.text(n))),
692
+ "interpolation" => {
693
+ n.named_child(0).map(|e| {
694
+ let u = self.unwrap_expr_node(e);
695
+ StringPart::Interp(self.parse_primary_expression(u))
696
+ })
697
+ }
698
+ _ => None,
699
+ })
700
+ .collect();
701
+ StringExpr { parts }
702
+ }
703
+
704
+ fn parse_string_raw(&self, node: Node) -> String {
705
+ let full = self.text(node);
706
+ full.trim_matches('"').to_string()
707
+ }
708
+
709
+ // ---- numeric literals -------------------------------------------------
710
+
711
+ fn parse_integer(&self, node: Node) -> i64 {
712
+ let s = self.text(node).replace('_', "");
713
+ if s.starts_with("0x") || s.starts_with("0X") {
714
+ i64::from_str_radix(&s[2..], 16).unwrap_or(0)
715
+ } else if s.starts_with("0b") || s.starts_with("0B") {
716
+ i64::from_str_radix(&s[2..], 2).unwrap_or(0)
717
+ } else {
718
+ s.parse().unwrap_or(0)
719
+ }
720
+ }
721
+
722
+ fn parse_float(&self, node: Node) -> f64 {
723
+ let s = self.text(node).trim_end_matches(['f', 'F']).replace('_', "");
724
+ s.parse().unwrap_or(0.0)
725
+ }
726
+
727
+ // ---- helpers ----------------------------------------------------------
728
+
729
+ /// Find the text of the first unnamed (punctuation/operator) non-whitespace child.
730
+ fn find_unnamed_operator(&self, node: Node) -> String {
731
+ let mut cursor = node.walk();
732
+ for child in node.children(&mut cursor) {
733
+ if !child.is_named() {
734
+ let t = self.text(child);
735
+ if !t.trim().is_empty() {
736
+ return t;
737
+ }
738
+ }
739
+ }
740
+ String::new()
741
+ }
742
+ }
743
+
744
+ fn is_expression_kind(kind: &str) -> bool {
745
+ matches!(
746
+ kind,
747
+ "binary_operator"
748
+ | "unary_operator"
749
+ | "boolean_operator"
750
+ | "not_operator"
751
+ | "comparison_operator"
752
+ | "ternary_expression"
753
+ | "attribute"
754
+ | "fn_call"
755
+ | "class_call"
756
+ | "parenthesized_expression"
757
+ | "string"
758
+ | "integer"
759
+ | "float"
760
+ | "self"
761
+ | "var_identifier"
762
+ | "type_identifier"
763
+ )
764
+ }
tooling/tree-sitter-plum/Cargo.toml CHANGED
@@ -29,4 +29,4 @@ tree-sitter-language = "0.1"
29
29
  cc = "1.1.22"
30
30
 
31
31
  [dev-dependencies]
32
- tree-sitter = "0.24.5"
32
+ tree-sitter = "0.26"