plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
3d79c9f
— Peter John
2026-07-23T18:45:42+05:30
feat(plum-core): parse obj.field assignment targets into AssignTarget::Field
- plum-core/src/ast.rs +9 -1
- plum-core/src/parser.rs +27 -5
plum-core/src/ast.rs
CHANGED
|
@@ -152,9 +152,17 @@ pub enum Stmt {
|
|
|
152
152
|
Expr(Expr),
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
#[derive(Debug, Clone, PartialEq)]
|
|
156
|
+
pub enum AssignTarget {
|
|
157
|
+
Var(String),
|
|
158
|
+
/// `object.field = value` — `object`'s evaluated type must be a class; `field`
|
|
159
|
+
/// is that class's field name being written.
|
|
160
|
+
Field(Box<Expr>, String),
|
|
161
|
+
}
|
|
162
|
+
|
|
155
163
|
#[derive(Debug, Clone, PartialEq)]
|
|
156
164
|
pub struct Assign {
|
|
157
|
-
pub targets: Vec<
|
|
165
|
+
pub targets: Vec<AssignTarget>,
|
|
158
166
|
pub values: Vec<Expr>,
|
|
159
167
|
}
|
|
160
168
|
|
plum-core/src/parser.rs
CHANGED
|
@@ -349,13 +349,19 @@ impl<'a> AstParser<'a> {
|
|
|
349
349
|
}
|
|
350
350
|
|
|
351
351
|
fn parse_assign(&self, node: Node) -> Assign {
|
|
352
|
-
// assign: commaSep1(var_identifier) "=" commaSep1(expression)
|
|
352
|
+
// assign: commaSep1(choice(var_identifier, field_target)) "=" commaSep1(expression)
|
|
353
|
-
// Named children are all
|
|
353
|
+
// Named children are all targets (var_identifier | field_target) then all
|
|
354
|
-
// We split at the first
|
|
354
|
+
// expressions. We split at the first child that is neither.
|
|
355
355
|
let mut cursor = node.walk();
|
|
356
356
|
let named: Vec<Node> = node.named_children(&mut cursor).collect();
|
|
357
|
+
let split = named
|
|
358
|
+
.iter()
|
|
357
|
-
|
|
359
|
+
.position(|n| n.kind() != "var_identifier" && n.kind() != "field_target")
|
|
360
|
+
.unwrap_or(named.len());
|
|
358
|
-
let targets = named[..split]
|
|
361
|
+
let targets = named[..split]
|
|
362
|
+
.iter()
|
|
363
|
+
.map(|n| self.parse_assign_target(*n))
|
|
364
|
+
.collect();
|
|
359
365
|
let values = named[split..]
|
|
360
366
|
.iter()
|
|
361
367
|
.map(|n| { let u = self.unwrap_expr_node(*n); self.parse_expression(u) })
|
|
@@ -363,6 +369,22 @@ impl<'a> AstParser<'a> {
|
|
|
363
369
|
Assign { targets, values }
|
|
364
370
|
}
|
|
365
371
|
|
|
372
|
+
fn parse_assign_target(&self, node: Node) -> AssignTarget {
|
|
373
|
+
match node.kind() {
|
|
374
|
+
"field_target" => {
|
|
375
|
+
// field_target: object: primary_expression "." member: fn_identifier
|
|
376
|
+
let object_node = node.child_by_field_name("object").expect("field_target has an object");
|
|
377
|
+
let member = node
|
|
378
|
+
.child_by_field_name("member")
|
|
379
|
+
.map(|n| self.text(n))
|
|
380
|
+
.unwrap_or_default();
|
|
381
|
+
let object = self.parse_primary_expression(self.unwrap_expr_node(object_node));
|
|
382
|
+
AssignTarget::Field(Box::new(object), member)
|
|
383
|
+
}
|
|
384
|
+
_ => AssignTarget::Var(self.text(node)),
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
366
388
|
fn parse_for(&self, node: Node) -> For {
|
|
367
389
|
// for: "for" commaSep1(var_identifier) "in" primary_expression body
|
|
368
390
|
// Named children: var_identifier+, primary_expression (iter), body
|