plum

#treesitter#compiler#wasm

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

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


plum-core/src/parser.rs
0a830c8 1
use tree_sitter::Node;
0a830c8 2
use crate::ast::*;
0a830c8 3
0a830c8 4
pub struct AstParser<'a> {
0a830c8 5
    source: &'a [u8],
0a830c8 6
}
0a830c8 7
0a830c8 8
impl<'a> AstParser<'a> {
0a830c8 9
    pub fn new(source: &'a str) -> Self {
0a830c8 10
        AstParser { source: source.as_bytes() }
0a830c8 11
    }
0a830c8 12
0a830c8 13
    fn text(&self, node: Node) -> String {
0a830c8 14
        node.utf8_text(self.source).unwrap_or("").to_string()
0a830c8 15
    }
0a830c8 16
0a830c8 17
    /// Peel transparent `expression` / `primary_expression` wrapper nodes.
3d6f280 18
    fn unwrapExprNode<'b>(&self, node: Node<'b>) -> Node<'b> {
0a830c8 19
        match node.kind() {
0a830c8 20
            "expression" | "primary_expression" => {
3d6f280 21
                node.named_child(0).map(|c| self.unwrapExprNode(c)).unwrap_or(node)
0a830c8 22
            }
0a830c8 23
            _ => node,
0a830c8 24
        }
0a830c8 25
    }
0a830c8 26
0a830c8 27
    /// Collect named children of `node` that have the given `kind`.
3d6f280 28
    fn childrenOfKind(&self, node: Node<'a>, kind: &str) -> Vec<Node<'a>> {
0a830c8 29
        let mut cursor = node.walk();
0a830c8 30
        node.named_children(&mut cursor)
0a830c8 31
            .filter(|n| n.kind() == kind)
0a830c8 32
            .collect()
0a830c8 33
    }
0a830c8 34
0a830c8 35
    // ---- top level --------------------------------------------------------
0a830c8 36
3d6f280 37
    pub fn parseSource(&self, node: Node) -> Source {
0a830c8 38
        assert_eq!(node.kind(), "source");
0a830c8 39
        let mut module = None;
0a830c8 40
        let mut imports = Vec::new();
0a830c8 41
        let mut items = Vec::new();
0a830c8 42
        let mut cursor = node.walk();
0a830c8 43
        for child in node.named_children(&mut cursor) {
0a830c8 44
            match child.kind() {
3d6f280 45
                "module" => module = Some(self.parseModule(child)),
3d6f280 46
                "import" => imports.push(self.parseImport(child)),
4df1312 47
                "class" => {
3d6f280 48
                    let c = self.parseClass(child);
3d6f280 49
                    let nested = self.collectNestedFns(child, &c.name);
4df1312 50
                    items.push(Item::Class(c));
4df1312 51
                    items.extend(nested.into_iter().map(Item::Fn));
4df1312 52
                }
3d6f280 53
                "trait" => items.push(Item::Trait(self.parseTrait(child))),
4df1312 54
                "enum" => {
3d6f280 55
                    let e = self.parseEnum(child);
3d6f280 56
                    let nested = self.collectNestedFns(child, &e.name);
4df1312 57
                    items.push(Item::Enum(e));
4df1312 58
                    items.extend(nested.into_iter().map(Item::Fn));
4df1312 59
                }
3d6f280 60
                "fn" => items.push(Item::Fn(self.parseFn(child))),
3d6f280 61
                "const" => items.push(Item::Const(self.parseConst(child))),
0a830c8 62
                _ => {}
0a830c8 63
            }
0a830c8 64
        }
0a830c8 65
        Source { module, imports, items }
0a830c8 66
    }
0a830c8 67
3d6f280 68
    fn parseModule(&self, node: Node) -> Module {
0a830c8 69
        // module: "module" mod_identifier
0a830c8 70
        // named_child(0) = mod_identifier
0a830c8 71
        let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
0a830c8 72
        Module { name }
0a830c8 73
    }
0a830c8 74
3d6f280 75
    fn parseImport(&self, node: Node) -> Import {
0a830c8 76
        // import: "import" url — url is the only named child
0a830c8 77
        let path = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
0a830c8 78
        Import { path }
0a830c8 79
    }
0a830c8 80
0a830c8 81
    // ---- class / trait / enum ---------------------------------------------
0a830c8 82
4df1312 83
    /// Collects any `fn` named children nested directly inside a class/enum body and
4df1312 84
    /// parses each as an ordinary top-level `Fn`, with `type_param` forced to `owner`
4df1312 85
    /// regardless of whatever the nested `fn` itself parsed (a nested method's receiver
4df1312 86
    /// is implicit from its enclosing declaration; if it also carries its own explicit,
4df1312 87
    /// redundant `<Receiver>` annotation, that's simply overridden, not treated as a
4df1312 88
    /// conflict/error).
3d6f280 89
    fn collectNestedFns(&self, node: Node, owner: &str) -> Vec<Fn> {
3d6f280 90
        self.childrenOfKind(node, "fn")
4df1312 91
            .into_iter()
4df1312 92
            .map(|n| {
3d6f280 93
                let mut f = self.parseFn(n);
4df1312 94
                f.type_param = Some(owner.to_string());
4df1312 95
                f
4df1312 96
            })
4df1312 97
            .collect()
4df1312 98
    }
4df1312 99
3d6f280 100
    fn parseClass(&self, node: Node) -> Class {
be16cd8 101
        // class: "type" type_identifier generics? ("(" type_identifier,* ")")? "=" body
be16cd8 102
        // Named children in order: type_identifier (name), generics? (declaration), type_identifier* (implements), field*
0a830c8 103
        let mut cursor = node.walk();
0a830c8 104
        let named: Vec<Node> = node.named_children(&mut cursor).collect();
0a830c8 105
0a830c8 106
        let name = named.first().map(|n| self.text(*n)).unwrap_or_default();
0a830c8 107
be16cd8 108
        // Skip the optional `generics` declaration node before looking for implements.
be16cd8 109
        let after_generics = if named.get(1).map(|n| n.kind()) == Some("generics") { 2 } else { 1 };
be16cd8 110
0a830c8 111
        // implements = type_identifiers that appear before any `field` node
be16cd8 112
        let implements: Vec<String> = named[after_generics..]
0a830c8 113
            .iter()
0a830c8 114
            .take_while(|n| n.kind() == "type_identifier")
0a830c8 115
            .map(|n| self.text(*n))
0a830c8 116
            .collect();
0a830c8 117
3d6f280 118
        let generics = self.parseGenericsField(node);
0a830c8 119
0a830c8 120
        let fields: Vec<Field> = named
0a830c8 121
            .iter()
0a830c8 122
            .filter(|n| n.kind() == "field")
3d6f280 123
            .map(|n| self.parseField(*n))
0a830c8 124
            .collect();
0a830c8 125
0a830c8 126
        Class { name, implements, generics, fields }
0a830c8 127
    }
0a830c8 128
3d6f280 129
    fn parseGenericsField(&self, node: Node) -> Vec<GenericParam> {
be16cd8 130
        // generics: "[" generic_type,* "]"  where  generic_type: generic (":" sep1(type_identifier, "+"))?
22140cf 131
        //
be16cd8 132
        // `generic_type` is `inline`d in the grammar, so the `generics` node has NO
be16cd8 133
        // `generic_type` children — its named children are the single-uppercase-letter
be16cd8 134
        // `generic` nodes, each optionally followed by their bound `type_identifier`
be16cd8 135
        // nodes, all flattened together. Reconstruct each `GenericParam` by starting a
be16cd8 136
        // new one at every `generic` node and attaching any following
be16cd8 137
        // `type_identifier`s as its bounds until the next `generic` node.
3d6f280 138
        let Some(generics_node) = self.childrenOfKind(node, "generics").into_iter().next() else {
22140cf 139
            return Vec::new();
22140cf 140
        };
22140cf 141
        let mut cursor = generics_node.walk();
22140cf 142
        let mut params: Vec<GenericParam> = Vec::new();
22140cf 143
        for child in generics_node.named_children(&mut cursor) {
22140cf 144
            match child.kind() {
be16cd8 145
                "generic" => {
22140cf 146
                    params.push(GenericParam { name: self.text(child), bounds: Vec::new() });
22140cf 147
                }
22140cf 148
                "type_identifier" => {
22140cf 149
                    if let Some(last) = params.last_mut() {
22140cf 150
                        last.bounds.push(self.text(child));
22140cf 151
                    }
22140cf 152
                }
22140cf 153
                _ => {}
22140cf 154
            }
22140cf 155
        }
22140cf 156
        params
0a830c8 157
    }
0a830c8 158
3d6f280 159
    fn parseField(&self, node: Node) -> Field {
0a830c8 160
        // class_field (aliased to field): var_identifier ":" type
0a830c8 161
        let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
0a830c8 162
        let ty = node
0a830c8 163
            .named_child(1)
3d6f280 164
            .map(|n| self.parseType(n))
0a830c8 165
            .unwrap_or(Type { name: String::new(), generics: vec![] });
0a830c8 166
        Field { name, ty }
0a830c8 167
    }
0a830c8 168
3d6f280 169
    fn parseTrait(&self, node: Node) -> Trait {
0a830c8 170
        // trait: "trait" type_identifier generics? "=" body
0a830c8 171
        let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
3d6f280 172
        let generics = self.parseGenericsField(node);
3d6f280 173
        let methods = self.childrenOfKind(node, "field")
0a830c8 174
            .into_iter()
3d6f280 175
            .map(|f| self.parseTraitMethod(f))
0a830c8 176
            .collect();
0a830c8 177
        Trait { name, generics, methods }
0a830c8 178
    }
0a830c8 179
3d6f280 180
    fn parseTraitMethod(&self, node: Node) -> TraitMethod {
be16cd8 181
        // trait_field (aliased to field): fn_identifier "(" params ")" ("->" type)?
0a830c8 182
        let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
3d6f280 183
        let params = self.collectParamsFrom(node);
0d64aff 184
3d6f280 185
        // As with `parseFn`'s `returns` field, the grammar's `returns` field
0d64aff 186
        // wraps the whole `optional(seq("->", $.type))`, so
0d64aff 187
        // `child_by_field_name("returns")` resolves to the anonymous "->"
3d6f280 188
        // token, not the `type` node. Unlike `parseFn`, a trait method has
0d64aff 189
        // no receiver annotation, so there's at most one `type`-kind named
0d64aff 190
        // child here, and it's unambiguously the return type when present.
0d64aff 191
        let returns = node
0d64aff 192
            .named_children(&mut node.walk())
0d64aff 193
            .find(|n| n.kind() == "type")
3d6f280 194
            .map(|n| self.parseType(n));
0d64aff 195
0a830c8 196
        TraitMethod { name, params, returns }
0a830c8 197
    }
0a830c8 198
3d6f280 199
    fn parseEnum(&self, node: Node) -> Enum {
0a830c8 200
        let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
3d6f280 201
        let params = self.childrenOfKind(node, "enum_param")
fbfbd7b 202
            .into_iter()
3d6f280 203
            .map(|n| self.parseEnumParam(n))
fbfbd7b 204
            .collect();
3d6f280 205
        let variants = self.childrenOfKind(node, "field")
0a830c8 206
            .into_iter()
3d6f280 207
            .map(|f| self.parseEnumVariant(f))
0a830c8 208
            .collect();
fbfbd7b 209
        Enum { name, params, variants }
fbfbd7b 210
    }
fbfbd7b 211
3d6f280 212
    fn parseEnumParam(&self, node: Node) -> EnumParam {
fbfbd7b 213
        // enum_param: var_identifier ":" type
fbfbd7b 214
        let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
fbfbd7b 215
        let ty = node
fbfbd7b 216
            .named_child(1)
3d6f280 217
            .map(|n| self.parseType(n))
fbfbd7b 218
            .unwrap_or(Type { name: String::new(), generics: vec![] });
fbfbd7b 219
        EnumParam { name, ty }
0a830c8 220
    }
0a830c8 221
3d6f280 222
    fn parseEnumVariant(&self, node: Node) -> EnumVariant {
fbfbd7b 223
        // enum_field (aliased to field): "|" type_identifier
fbfbd7b 224
        //   ("[" (type_identifier | generic),* "]")?     -- existing: generic type payload
fbfbd7b 225
        //   | ("(" expression,* ")")?                    -- new: discriminant value literals
fbfbd7b 226
        // named children after the name: either type_identifier/generic (fields) or
fbfbd7b 227
        // expression (values) — the two are disjoint child-kind sets, never mixed.
0a830c8 228
        let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
fbfbd7b 229
        let rest: Vec<Node> = (1..node.named_child_count())
0a830c8 230
            .filter_map(|i| node.named_child(i as u32))
fbfbd7b 231
            .collect();
fbfbd7b 232
        let fields: Vec<String> = rest.iter()
be16cd8 233
            .filter(|n| matches!(n.kind(), "type_identifier" | "generic"))
fbfbd7b 234
            .map(|n| self.text(*n))
fbfbd7b 235
            .collect();
fbfbd7b 236
        let values: Vec<Expr> = rest.iter()
fbfbd7b 237
            .filter(|n| n.kind() == "expression")
3d6f280 238
            .map(|n| { let u = self.unwrapExprNode(*n); self.parseExpression(u) })
0a830c8 239
            .collect();
fbfbd7b 240
        EnumVariant { name, fields, values }
0a830c8 241
    }
0a830c8 242
0a830c8 243
    // ---- functions --------------------------------------------------------
0a830c8 244
3d6f280 245
    fn parseFn(&self, node: Node) -> Fn {
2b8194c 246
        // fn: "fun" fn_identifier "(" param,* ")" ("->" type)? "=" body_or_expr
2b8194c 247
        // Named children: fn_identifier, param*, type?, body/expr
2b8194c 248
        //
2b8194c 249
        // `type_param` (the method's receiver, e.g. `Cat` in a method nested inside
2b8194c 250
        // `type Cat = ...`) is never set here — a bare `fn` node has no receiver of
3d6f280 251
        // its own; `parseSource`'s `collectNestedFns` forces it afterward for any
2b8194c 252
        // `fn` nested inside a `class`/`enum` body. There is no top-level
2b8194c 253
        // `<Receiver>` annotation syntax to parse.
0a830c8 254
        let mut cursor = node.walk();
0a830c8 255
        let named: Vec<Node> = node.named_children(&mut cursor).collect();
0a830c8 256
0a830c8 257
        let name = named.first().map(|n| self.text(*n)).unwrap_or_default();
0a830c8 258
0a830c8 259
        let params: Vec<Param> = named
0a830c8 260
            .iter()
0a830c8 261
            .filter(|n| n.kind() == "param")
3d6f280 262
            .map(|n| self.parseParam(*n))
0a830c8 263
            .collect();
0a830c8 264
2b8194c 265
        // The grammar's `returns` field wraps the whole `optional(seq("->", $.type))`,
2b8194c 266
        // so `child_by_field_name("returns")` resolves to the anonymous `"->"` token,
2b8194c 267
        // not the `type` node — find the `type`-kind named child instead.
2ccae10 268
        let returns = named
2ccae10 269
            .iter()
2b8194c 270
            .find(|n| n.kind() == "type")
3d6f280 271
            .map(|n| self.parseType(*n));
0a830c8 272
0a830c8 273
        // body is the last named child — it is either a `body` node (block)
0000000 274
        // or an expression node when the body is a single expression. Genuinely
0000000 275
        // absent (`extern fun foo(...)` with no `=`) parses as `FnBody::Extern`;
0000000 276
        // whether that's actually valid here is `plum-checker`'s job, not the
0000000 277
        // parser's — it must agree with `is_extern` below.
0a830c8 278
        let body = named.last().and_then(|last| {
0a830c8 279
            match last.kind() {
0a830c8 280
                // Skip non-body trailing nodes
be16cd8 281
                "fn_identifier" | "type" | "param" | "self" => None,
3d6f280 282
                "body" => Some(FnBody::Block(self.parseBlock(*last))),
0a830c8 283
                _ => {
3d6f280 284
                    let unwrapped = self.unwrapExprNode(*last);
3d6f280 285
                    Some(FnBody::Expr(self.parseExpression(unwrapped)))
0a830c8 286
                }
0a830c8 287
            }
0000000 288
        }).unwrap_or(FnBody::Extern);
0a830c8 289
0000000 290
        let is_extern = node.child_by_field_name("externKw").is_some();
0000000 291
0000000 292
        Fn { name, type_param: None, is_extern, params, returns, body }
0a830c8 293
    }
0a830c8 294
3d6f280 295
    fn parseConst(&self, node: Node) -> Const {
0a830c8 296
        // const: const_identifier "=" expression
0a830c8 297
        let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
0a830c8 298
        let value = node
0a830c8 299
            .named_child(1)
0a830c8 300
            .map(|n| {
3d6f280 301
                let unwrapped = self.unwrapExprNode(n);
3d6f280 302
                self.parseExpression(unwrapped)
0a830c8 303
            })
0a830c8 304
            .unwrap_or(Expr::Int(0));
0a830c8 305
        Const { name, value }
0a830c8 306
    }
0a830c8 307
0a830c8 308
    // ---- params / return type ---------------------------------------------
0a830c8 309
0a830c8 310
    /// Collect `param` named children from any node that has them.
3d6f280 311
    fn collectParamsFrom(&self, node: Node) -> Vec<Param> {
3d6f280 312
        self.childrenOfKind(node, "param")
0a830c8 313
            .into_iter()
3d6f280 314
            .map(|n| self.parseParam(n))
0a830c8 315
            .collect()
0a830c8 316
    }
0a830c8 317
3d6f280 318
    fn parseParam(&self, node: Node) -> Param {
d7e5ff4 319
        // param: var_identifier ":" (type | variadic_type | fn_value_type) ("=" expression)?
0a830c8 320
        let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
d7e5ff4 321
        let ty = node.named_child(1).map(|n| match n.kind() {
d7e5ff4 322
            "variadic_type" => {
0a830c8 323
                let inner = n.named_child(0)
3d6f280 324
                    .map(|t| self.parseType(t))
0a830c8 325
                    .unwrap_or(Type { name: String::new(), generics: vec![] });
0a830c8 326
                ParamType::Variadic(inner)
0a830c8 327
            }
3d6f280 328
            "fn_value_type" => self.parseFnValueType(n),
3d6f280 329
            _ => ParamType::Type(self.parseType(n)),
0a830c8 330
        }).unwrap_or(ParamType::Type(Type { name: String::new(), generics: vec![] }));
0a830c8 331
        let default = node.named_child(2).map(|n| {
3d6f280 332
            let unwrapped = self.unwrapExprNode(n);
3d6f280 333
            self.parseExpression(unwrapped)
0a830c8 334
        });
0a830c8 335
        Param { name, ty, default }
0a830c8 336
    }
0a830c8 337
3d6f280 338
    fn parseFnValueType(&self, node: Node) -> ParamType {
d7e5ff4 339
        // fn_value_type: "fn" "(" field("params", type,*) ")" ("->" field("returns", type))?
d7e5ff4 340
        // The "returns" field (if present) is a distinct field from "params", so the
d7e5ff4 341
        // two are disambiguated unambiguously by field name, not by counting/position
d7e5ff4 342
        // among same-kind "type" children — the same idiom `fn`'s own `returns` field
d7e5ff4 343
        // already uses.
d7e5ff4 344
        let returns_node = node.child_by_field_name("returns");
3d6f280 345
        let param_types: Vec<Type> = self.childrenOfKind(node, "type")
d7e5ff4 346
            .into_iter()
d7e5ff4 347
            .filter(|n| Some(*n) != returns_node)
3d6f280 348
            .map(|n| self.parseType(n))
d7e5ff4 349
            .collect();
3d6f280 350
        let ret = returns_node.map(|n| Box::new(self.parseType(n)));
d7e5ff4 351
        ParamType::Fn(param_types, ret)
d7e5ff4 352
    }
d7e5ff4 353
3d6f280 354
    fn parseType(&self, node: Node) -> Type {
0000000 355
        // type: type_identifier ("[" type,* "]")? | generic | "[" "]" element:type
0000000 356
        // A slice type (e.g. `[]Byte`) is flattened to a single reserved name
0000000 357
        // `"[]" + element_name` rather than a real generics list — the checker
0000000 358
        // treats it as a fixed builtin (only `[]Byte` is accepted), not a
0000000 359
        // monomorphized generic, so there's no template to carry args for.
0000000 360
        if let Some(element) = node.child_by_field_name("element") {
0000000 361
            let elem = self.parseType(element);
0000000 362
            return Type { name: format!("[]{}", elem.name), generics: vec![] };
0000000 363
        }
0a830c8 364
        // named_child(0) = type_identifier
0a830c8 365
        let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_else(|| self.text(node));
0a830c8 366
        let mut cursor = node.walk();
0a830c8 367
        let generics: Vec<Type> = node
0a830c8 368
            .named_children(&mut cursor)
0a830c8 369
            .skip(1)
0a830c8 370
            .filter(|n| n.kind() == "type")
3d6f280 371
            .map(|n| self.parseType(n))
0a830c8 372
            .collect();
0a830c8 373
        Type { name, generics }
0a830c8 374
    }
0a830c8 375
0a830c8 376
    // ---- statements -------------------------------------------------------
0a830c8 377
3d6f280 378
    fn parseBlock(&self, node: Node) -> Block {
0a830c8 379
        let mut cursor = node.walk();
0a830c8 380
        let stmts = node
0a830c8 381
            .named_children(&mut cursor)
3d6f280 382
            .filter_map(|n| self.parseStmt(n))
0a830c8 383
            .collect();
0a830c8 384
        Block { stmts }
0a830c8 385
    }
0a830c8 386
3d6f280 387
    fn parseStmt(&self, node: Node) -> Option<Stmt> {
3d6f280 388
        let node = self.unwrapExprNode(node);
0a830c8 389
        Some(match node.kind() {
3d6f280 390
            "assign" => Stmt::Assign(self.parseAssign(node)),
0a830c8 391
            "break" => Stmt::Break,
0a830c8 392
            "continue" => Stmt::Continue,
0a830c8 393
            "return" => {
0a830c8 394
                let expr = node.named_child(0).map(|n| {
3d6f280 395
                    let u = self.unwrapExprNode(n);
3d6f280 396
                    self.parseExpression(u)
0a830c8 397
                });
0a830c8 398
                Stmt::Return(expr)
0a830c8 399
            }
0a830c8 400
            "todo" => Stmt::Todo,
0a830c8 401
            "assert" => {
0a830c8 402
                let expr = node.named_child(0)
3d6f280 403
                    .map(|n| { let u = self.unwrapExprNode(n); self.parseExpression(u) })
0a830c8 404
                    .unwrap_or(Expr::Int(0));
0a830c8 405
                Stmt::Assert(expr)
0a830c8 406
            }
3d6f280 407
            "for" => Stmt::For(self.parseFor(node)),
3d6f280 408
            "while" => Stmt::While(self.parseWhile(node)),
3d6f280 409
            "if" => Stmt::If(self.parseIf(node)),
3d6f280 410
            "match" => Stmt::Match(self.parseMatch(node)),
3d6f280 411
            kind if isExpressionKind(kind) => Stmt::Expr(self.parseExpression(node)),
0a830c8 412
            _ => return None,
0a830c8 413
        })
0a830c8 414
    }
0a830c8 415
3d6f280 416
    fn parseAssign(&self, node: Node) -> Assign {
3d79c9f 417
        // assign: commaSep1(choice(var_identifier, field_target)) "=" commaSep1(expression)
3d79c9f 418
        // Named children are all targets (var_identifier | field_target) then all
3d79c9f 419
        // expressions. We split at the first child that is neither.
0a830c8 420
        let mut cursor = node.walk();
0a830c8 421
        let named: Vec<Node> = node.named_children(&mut cursor).collect();
3d79c9f 422
        let split = named
3d79c9f 423
            .iter()
3d79c9f 424
            .position(|n| n.kind() != "var_identifier" && n.kind() != "field_target")
3d79c9f 425
            .unwrap_or(named.len());
3d79c9f 426
        let targets = named[..split]
3d79c9f 427
            .iter()
3d6f280 428
            .map(|n| self.parseAssignTarget(*n))
3d79c9f 429
            .collect();
0a830c8 430
        let values = named[split..]
0a830c8 431
            .iter()
3d6f280 432
            .map(|n| { let u = self.unwrapExprNode(*n); self.parseExpression(u) })
0a830c8 433
            .collect();
0000000 434
        let declare = node.child_by_field_name("op").map(|n| self.text(n) == ":=").unwrap_or(false);
0000000 435
        Assign { targets, values, declare }
0a830c8 436
    }
0a830c8 437
3d6f280 438
    fn parseAssignTarget(&self, node: Node) -> AssignTarget {
3d79c9f 439
        match node.kind() {
3d79c9f 440
            "field_target" => {
3d79c9f 441
                // field_target: object: primary_expression "." member: fn_identifier
3d79c9f 442
                let object_node = node.child_by_field_name("object").expect("field_target has an object");
3d79c9f 443
                let member = node
3d79c9f 444
                    .child_by_field_name("member")
3d79c9f 445
                    .map(|n| self.text(n))
3d79c9f 446
                    .unwrap_or_default();
3d6f280 447
                let object = self.parsePrimaryExpression(self.unwrapExprNode(object_node));
3d79c9f 448
                AssignTarget::Field(Box::new(object), member)
3d79c9f 449
            }
3d79c9f 450
            _ => AssignTarget::Var(self.text(node)),
3d79c9f 451
        }
3d79c9f 452
    }
3d79c9f 453
3d6f280 454
    fn parseFor(&self, node: Node) -> For {
0a830c8 455
        // for: "for" commaSep1(var_identifier) "in" primary_expression body
0a830c8 456
        // Named children: var_identifier+, primary_expression (iter), body
0a830c8 457
        let mut cursor = node.walk();
0a830c8 458
        let named: Vec<Node> = node.named_children(&mut cursor).collect();
0a830c8 459
0a830c8 460
        let split = named.iter().position(|n| n.kind() != "var_identifier").unwrap_or(0);
0a830c8 461
        let vars = named[..split].iter().map(|n| self.text(*n)).collect();
0a830c8 462
0a830c8 463
        let iter = named.get(split)
3d6f280 464
            .map(|n| { let u = self.unwrapExprNode(*n); self.parsePrimaryExpression(u) })
0a830c8 465
            .unwrap_or(Expr::Int(0));
0a830c8 466
0a830c8 467
        let body = named.last()
0a830c8 468
            .filter(|n| n.kind() == "body")
3d6f280 469
            .map(|n| self.parseBlock(*n))
0a830c8 470
            .unwrap_or(Block { stmts: vec![] });
0a830c8 471
0a830c8 472
        For { vars, iter, body }
0a830c8 473
    }
0a830c8 474
3d6f280 475
    fn parseWhile(&self, node: Node) -> While {
0a830c8 476
        // while: "while" expression body
0a830c8 477
        let mut cursor = node.walk();
0a830c8 478
        let named: Vec<Node> = node.named_children(&mut cursor).collect();
0a830c8 479
        let condition = named.first()
3d6f280 480
            .map(|n| { let u = self.unwrapExprNode(*n); self.parseExpression(u) })
0a830c8 481
            .unwrap_or(Expr::Int(0));
0a830c8 482
        let body = named.last()
0a830c8 483
            .filter(|n| n.kind() == "body")
3d6f280 484
            .map(|n| self.parseBlock(*n))
0a830c8 485
            .unwrap_or(Block { stmts: vec![] });
0a830c8 486
        While { condition, body }
0a830c8 487
    }
0a830c8 488
3d6f280 489
    fn parseIf(&self, node: Node) -> If {
0a830c8 490
        // if: "if" expression body else_if* else?
0a830c8 491
        let mut cursor = node.walk();
0a830c8 492
        let named: Vec<Node> = node.named_children(&mut cursor).collect();
0a830c8 493
0a830c8 494
        let condition = named.first()
3d6f280 495
            .map(|n| { let u = self.unwrapExprNode(*n); self.parseExpression(u) })
0a830c8 496
            .unwrap_or(Expr::Int(0));
0a830c8 497
        let body = named.get(1)
0a830c8 498
            .filter(|n| n.kind() == "body")
3d6f280 499
            .map(|n| self.parseBlock(*n))
0a830c8 500
            .unwrap_or(Block { stmts: vec![] });
0a830c8 501
        let else_ifs = named.iter()
0a830c8 502
            .filter(|n| n.kind() == "else_if")
3d6f280 503
            .map(|n| self.parseElseIf(*n))
0a830c8 504
            .collect();
0a830c8 505
        let else_ = named.iter()
0a830c8 506
            .find(|n| n.kind() == "else")
0a830c8 507
            .and_then(|n| n.named_child(0))
3d6f280 508
            .map(|n| self.parseBlock(n));
0a830c8 509
        If { condition, body, else_ifs, else_ }
0a830c8 510
    }
0a830c8 511
3d6f280 512
    fn parseElseIf(&self, node: Node) -> ElseIf {
0a830c8 513
        // else_if: "else if" expression body
0a830c8 514
        let mut cursor = node.walk();
0a830c8 515
        let named: Vec<Node> = node.named_children(&mut cursor).collect();
0a830c8 516
        let condition = named.first()
3d6f280 517
            .map(|n| { let u = self.unwrapExprNode(*n); self.parseExpression(u) })
0a830c8 518
            .unwrap_or(Expr::Int(0));
0a830c8 519
        let body = named.last()
0a830c8 520
            .filter(|n| n.kind() == "body")
3d6f280 521
            .map(|n| self.parseBlock(*n))
0a830c8 522
            .unwrap_or(Block { stmts: vec![] });
0a830c8 523
        ElseIf { condition, body }
0a830c8 524
    }
0a830c8 525
3d6f280 526
    fn parseMatch(&self, node: Node) -> Match {
0a830c8 527
        // match: "match" commaSep1(expression) "is" case+
0a830c8 528
        let mut cursor = node.walk();
0a830c8 529
        let named: Vec<Node> = node.named_children(&mut cursor).collect();
0a830c8 530
        let split = named.iter().position(|n| n.kind() == "case").unwrap_or(named.len());
0a830c8 531
        let subjects = named[..split]
0a830c8 532
            .iter()
3d6f280 533
            .map(|n| { let u = self.unwrapExprNode(*n); self.parseExpression(u) })
0a830c8 534
            .collect();
0a830c8 535
        let cases = named[split..]
0a830c8 536
            .iter()
0a830c8 537
            .filter(|n| n.kind() == "case")
3d6f280 538
            .map(|n| self.parseCase(*n))
0a830c8 539
            .collect();
0a830c8 540
        Match { subjects, cases }
0a830c8 541
    }
0a830c8 542
3d6f280 543
    fn parseCase(&self, node: Node) -> Case {
660674c 544
        // case: commaSep1(case_pattern) "=>" (expression | body)
0a830c8 545
        let mut cursor = node.walk();
0a830c8 546
        let named: Vec<Node> = node.named_children(&mut cursor).collect();
0a830c8 547
        let patterns = named.iter()
0a830c8 548
            .filter(|n| n.kind() == "case_pattern")
3d6f280 549
            .map(|n| self.parseCasePattern(*n))
0a830c8 550
            .collect();
0a830c8 551
        let body = named.iter()
660674c 552
            .find(|n| n.kind() != "case_pattern")
660674c 553
            .map(|n| {
660674c 554
                if n.kind() == "body" {
3d6f280 555
                    self.parseBlock(*n)
660674c 556
                } else {
3d6f280 557
                    let unwrapped = self.unwrapExprNode(*n);
3d6f280 558
                    Block { stmts: vec![Stmt::Expr(self.parseExpression(unwrapped))] }
660674c 559
                }
660674c 560
            })
0a830c8 561
            .unwrap_or(Block { stmts: vec![] });
0a830c8 562
        Case { patterns, body }
0a830c8 563
    }
0a830c8 564
3d6f280 565
    fn parseCasePattern(&self, node: Node) -> CasePattern {
0a830c8 566
        // case_pattern wraps: class_pattern | string | integer | float | dotted_name | "_"
0a830c8 567
        let inner = node.named_child(0).unwrap_or(node);
0a830c8 568
        match inner.kind() {
0a830c8 569
            "class_pattern" => {
0a830c8 570
                // class_pattern: dotted_name "(" case_pattern,* ")"
0a830c8 571
                let name = inner.named_child(0).map(|n| self.text(n)).unwrap_or_default();
0a830c8 572
                let fields = (1..inner.named_child_count())
0a830c8 573
                    .filter_map(|i| inner.named_child(i as u32))
0a830c8 574
                    .filter(|n| n.kind() == "case_pattern")
3d6f280 575
                    .map(|n| self.parseCasePattern(n))
0a830c8 576
                    .collect();
0a830c8 577
                CasePattern::Class { name, fields }
0a830c8 578
            }
3d6f280 579
            "string" => CasePattern::String(self.parseStringRaw(inner)),
3d6f280 580
            "integer" => CasePattern::Int(self.parseInteger(inner)),
3d6f280 581
            "float" => CasePattern::Float(self.parseFloat(inner)),
0a830c8 582
            "dotted_name" => CasePattern::Name(self.text(inner)),
0a830c8 583
            _ => {
0a830c8 584
                let t = self.text(inner);
0a830c8 585
                if t == "_" { CasePattern::Wildcard } else { CasePattern::Name(t) }
0a830c8 586
            }
0a830c8 587
        }
0a830c8 588
    }
0a830c8 589
0a830c8 590
    // ---- expressions ------------------------------------------------------
0a830c8 591
3d6f280 592
    pub fn parseExpression(&self, node: Node) -> Expr {
3d6f280 593
        let node = self.unwrapExprNode(node);
0a830c8 594
        match node.kind() {
3d6f280 595
            "comparison_operator" => self.parseCompare(node),
0a830c8 596
            "not_operator" => {
0a830c8 597
                let arg = node.named_child(0)
3d6f280 598
                    .map(|n| { let u = self.unwrapExprNode(n); self.parseExpression(u) })
0a830c8 599
                    .unwrap_or(Expr::Int(0));
0a830c8 600
                Expr::Not(Box::new(arg))
0a830c8 601
            }
3d6f280 602
            "boolean_operator" => self.parseBoolOp(node),
3d6f280 603
            "ternary_expression" => self.parseTernary(node),
3d6f280 604
            "closure" => Expr::Closure(Box::new(self.parseClosure(node))),
3d6f280 605
            _ => self.parsePrimaryExpression(node),
0a830c8 606
        }
0a830c8 607
    }
0a830c8 608
3d6f280 609
    fn parseClosure(&self, node: Node) -> Closure {
4ba0db3 610
        // closure: "|" var_identifier,* "|" (expression | body)
3d6f280 611
        let params: Vec<String> = self.childrenOfKind(node, "var_identifier")
d7e5ff4 612
            .into_iter()
d7e5ff4 613
            .map(|n| self.text(n))
d7e5ff4 614
            .collect();
4ba0db3 615
        // The body is either an indented `body` block or a single inline expression
4ba0db3 616
        // (`|v| v`); normalize the inline form into a one-statement block so codegen and
4ba0db3 617
        // the checker only ever see a `Block`.
3d6f280 618
        let body = match self.childrenOfKind(node, "body").into_iter().next() {
3d6f280 619
            Some(block_node) => self.parseBlock(block_node),
4ba0db3 620
            None => match node.child_by_field_name("body") {
4ba0db3 621
                Some(expr_node) => {
3d6f280 622
                    let unwrapped = self.unwrapExprNode(expr_node);
3d6f280 623
                    Block { stmts: vec![Stmt::Expr(self.parseExpression(unwrapped))] }
4ba0db3 624
                }
4ba0db3 625
                None => Block { stmts: vec![] },
4ba0db3 626
            },
4ba0db3 627
        };
d7e5ff4 628
        Closure { params, body }
d7e5ff4 629
    }
d7e5ff4 630
3d6f280 631
    pub fn parsePrimaryExpression(&self, node: Node) -> Expr {
3d6f280 632
        let node = self.unwrapExprNode(node);
0a830c8 633
        match node.kind() {
3d6f280 634
            "binary_operator" => self.parseBinary(node),
3d6f280 635
            "unary_operator" => self.parseUnary(node),
3d6f280 636
            "attribute" => self.parseAttribute(node),
3d6f280 637
            "fn_call" => Expr::FnCall(self.parseFnCall(node)),
3d6f280 638
            "class_call" => Expr::ClassCall(self.parseClassCall(node)),
0a830c8 639
            "parenthesized_expression" => {
0a830c8 640
                // parenthesized_expression: "{" expression "}"
0a830c8 641
                let inner = node.named_child(0)
3d6f280 642
                    .map(|n| { let u = self.unwrapExprNode(n); self.parseExpression(u) })
0a830c8 643
                    .unwrap_or(Expr::Int(0));
0a830c8 644
                Expr::Paren(Box::new(inner))
0a830c8 645
            }
3d6f280 646
            "string" => Expr::String(self.parseString(node)),
3d6f280 647
            "integer" => Expr::Int(self.parseInteger(node)),
3d6f280 648
            "float" => Expr::Float(self.parseFloat(node)),
0a830c8 649
            "self" => Expr::Self_,
0a830c8 650
            "var_identifier" => Expr::Var(self.text(node)),
0a830c8 651
            "type_identifier" => Expr::TypeName(self.text(node)),
0000000 652
            // A SCREAMING_CASE const reference (e.g. `MAX_FLOAT_VALUE`) — reuses the
0000000 653
            // `TypeName` path, which already resolves a matching top-level const's
0000000 654
            // real type/value (see plum-checker's `inferExpr` and
0000000 655
            // plum-wasm-codegen's `CURRENT_CONSTS`).
0000000 656
            "const_identifier" => Expr::TypeName(self.text(node)),
0a830c8 657
            _ => Expr::Var(self.text(node)),
0a830c8 658
        }
0a830c8 659
    }
0a830c8 660
3d6f280 661
    fn parseBinary(&self, node: Node) -> Expr {
0a830c8 662
        // binary_operator: primary_expression op primary_expression
0a830c8 663
        // "operator" is an unnamed child; left/right are field-named
3d6f280 664
        let op_text = self.findUnnamedOperator(node);
0a830c8 665
        let op = match op_text.as_str() {
0a830c8 666
            "+" => BinOp::Add,
0a830c8 667
            "-" => BinOp::Sub,
0a830c8 668
            "*" => BinOp::Mul,
0a830c8 669
            "/" => BinOp::Div,
0a830c8 670
            "%" => BinOp::Mod,
0a830c8 671
            "|" => BinOp::BitOr,
0a830c8 672
            "&" => BinOp::BitAnd,
0a830c8 673
            "^" => BinOp::Xor,
0a830c8 674
            "<<" => BinOp::Shl,
0a830c8 675
            ">>" => BinOp::Shr,
0a830c8 676
            _ => BinOp::Add,
0a830c8 677
        };
0a830c8 678
        let mut cursor = node.walk();
0a830c8 679
        let named: Vec<Node> = node.named_children(&mut cursor).collect();
0a830c8 680
        let left = named.first()
3d6f280 681
            .map(|n| { let u = self.unwrapExprNode(*n); self.parsePrimaryExpression(u) })
0a830c8 682
            .unwrap_or(Expr::Int(0));
0a830c8 683
        let right = named.last()
3d6f280 684
            .map(|n| { let u = self.unwrapExprNode(*n); self.parsePrimaryExpression(u) })
0a830c8 685
            .unwrap_or(Expr::Int(0));
0a830c8 686
        Expr::Binary(Box::new(BinaryExpr { op, left, right }))
0a830c8 687
    }
0a830c8 688
3d6f280 689
    fn parseUnary(&self, node: Node) -> Expr {
3d6f280 690
        let op_text = self.findUnnamedOperator(node);
0a830c8 691
        let op = if op_text == "-" { UnOp::Neg } else { UnOp::Pos };
0a830c8 692
        let operand = node.named_child(0)
3d6f280 693
            .map(|n| { let u = self.unwrapExprNode(n); self.parsePrimaryExpression(u) })
0a830c8 694
            .unwrap_or(Expr::Int(0));
0a830c8 695
        Expr::Unary(Box::new(UnaryExpr { op, operand }))
0a830c8 696
    }
0a830c8 697
3d6f280 698
    fn parseBoolOp(&self, node: Node) -> Expr {
3d6f280 699
        let op_text = self.findUnnamedOperator(node);
0a830c8 700
        let op = if op_text == "&&" { BoolOp::And } else { BoolOp::Or };
0a830c8 701
        let mut cursor = node.walk();
0a830c8 702
        let named: Vec<Node> = node.named_children(&mut cursor).collect();
0a830c8 703
        let left = named.first()
3d6f280 704
            .map(|n| { let u = self.unwrapExprNode(*n); self.parseExpression(u) })
0a830c8 705
            .unwrap_or(Expr::Int(0));
0a830c8 706
        let right = named.last()
3d6f280 707
            .map(|n| { let u = self.unwrapExprNode(*n); self.parseExpression(u) })
0a830c8 708
            .unwrap_or(Expr::Int(0));
0a830c8 709
        Expr::Bool(Box::new(BoolExpr { op, left, right }))
0a830c8 710
    }
0a830c8 711
3d6f280 712
    fn parseCompare(&self, node: Node) -> Expr {
3d6f280 713
        let op_text = self.findUnnamedOperator(node);
0a830c8 714
        let op = match op_text.as_str() {
0a830c8 715
            "<" => CmpOp::Lt,
0a830c8 716
            "<=" => CmpOp::Lte,
0a830c8 717
            "==" => CmpOp::Eq,
0a830c8 718
            "!=" => CmpOp::Neq,
0a830c8 719
            ">=" => CmpOp::Gte,
0a830c8 720
            ">" => CmpOp::Gt,
0a830c8 721
            "<>" => CmpOp::NotEq2,
0a830c8 722
            _ => CmpOp::Eq,
0a830c8 723
        };
0a830c8 724
        let mut cursor = node.walk();
0a830c8 725
        let named: Vec<Node> = node.named_children(&mut cursor).collect();
0a830c8 726
        let left = named.first()
3d6f280 727
            .map(|n| { let u = self.unwrapExprNode(*n); self.parsePrimaryExpression(u) })
0a830c8 728
            .unwrap_or(Expr::Int(0));
0a830c8 729
        let right = named.last()
3d6f280 730
            .map(|n| { let u = self.unwrapExprNode(*n); self.parsePrimaryExpression(u) })
0a830c8 731
            .unwrap_or(Expr::Int(0));
0a830c8 732
        Expr::Compare(Box::new(CompareExpr { op, left, right }))
0a830c8 733
    }
0a830c8 734
3d6f280 735
    fn parseTernary(&self, node: Node) -> Expr {
0a830c8 736
        // ternary_expression: expression "?" expression ":" expression
0a830c8 737
        let mut cursor = node.walk();
0a830c8 738
        let named: Vec<Node> = node.named_children(&mut cursor).collect();
0a830c8 739
        let condition = named.first()
3d6f280 740
            .map(|n| { let u = self.unwrapExprNode(*n); self.parseExpression(u) })
0a830c8 741
            .unwrap_or(Expr::Int(0));
0a830c8 742
        let then = named.get(1)
3d6f280 743
            .map(|n| { let u = self.unwrapExprNode(*n); self.parseExpression(u) })
0a830c8 744
            .unwrap_or(Expr::Int(0));
0a830c8 745
        let else_ = named.get(2)
3d6f280 746
            .map(|n| { let u = self.unwrapExprNode(*n); self.parseExpression(u) })
0a830c8 747
            .unwrap_or(Expr::Int(0));
0a830c8 748
        Expr::Ternary(Box::new(TernaryExpr { condition, then, else_ }))
0a830c8 749
    }
0a830c8 750
3d6f280 751
    fn parseAttribute(&self, node: Node) -> Expr {
f502d22 752
        // attribute: primary_expression "." fn_identifier fn_argument_list?
f502d22 753
        // The member name is always fn_identifier (a superset of var_identifier); an
f502d22 754
        // optional trailing argument list distinguishes a method call from field access.
0a830c8 755
        let mut cursor = node.walk();
0a830c8 756
        let named: Vec<Node> = node.named_children(&mut cursor).collect();
0a830c8 757
        let object = named.first()
3d6f280 758
            .map(|n| { let u = self.unwrapExprNode(*n); self.parsePrimaryExpression(u) })
0a830c8 759
            .unwrap_or(Expr::Int(0));
f502d22 760
        let member = named.get(1).map(|n| self.text(*n)).unwrap_or_default();
f502d22 761
        let attr = match named.get(2) {
f502d22 762
            Some(args_node) => {
f502d22 763
                let mut acursor = args_node.walk();
f502d22 764
                let args = args_node.named_children(&mut acursor)
3d6f280 765
                    .map(|n| self.parseArg(n))
f502d22 766
                    .collect();
f502d22 767
                AttrKind::Method(FnCall { name: member, args })
0a830c8 768
            }
f502d22 769
            None => AttrKind::Field(member),
f502d22 770
        };
0a830c8 771
        Expr::Attribute(Box::new(AttributeExpr { object, attr }))
0a830c8 772
    }
0a830c8 773
3d6f280 774
    fn parseFnCall(&self, node: Node) -> FnCall {
f502d22 775
        // fn_call: var_identifier fn_argument_list (the callee lexes as var_identifier
f502d22 776
        // to avoid an identifier-token tie with all-lowercase, no-underscore names)
0a830c8 777
        let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
0a830c8 778
        let args = node.named_child(1)
0a830c8 779
            .map(|args_node| {
0a830c8 780
                let mut cursor = args_node.walk();
0a830c8 781
                args_node.named_children(&mut cursor)
3d6f280 782
                    .map(|n| self.parseArg(n))
0a830c8 783
                    .collect()
0a830c8 784
            })
0a830c8 785
            .unwrap_or_default();
0a830c8 786
        FnCall { name, args }
0a830c8 787
    }
0a830c8 788
3d6f280 789
    fn parseArg(&self, node: Node) -> Arg {
0a830c8 790
        match node.kind() {
0a830c8 791
            "keyword_argument" => {
0a830c8 792
                // keyword_argument: var_identifier "=" expression
0a830c8 793
                let name = node.named_child(0).map(|n| self.text(n)).unwrap_or_default();
0a830c8 794
                let value = node.named_child(1)
3d6f280 795
                    .map(|n| { let u = self.unwrapExprNode(n); self.parseExpression(u) })
0a830c8 796
                    .unwrap_or(Expr::Int(0));
0a830c8 797
                Arg::Keyword { name, value }
0a830c8 798
            }
0a830c8 799
            "pair_argument" => {
0a830c8 800
                // pair_argument: string "=>" expression
3d6f280 801
                let key = node.named_child(0).map(|n| self.parseStringRaw(n)).unwrap_or_default();
0a830c8 802
                let value = node.named_child(1)
3d6f280 803
                    .map(|n| { let u = self.unwrapExprNode(n); self.parseExpression(u) })
0a830c8 804
                    .unwrap_or(Expr::Int(0));
0a830c8 805
                Arg::Pair { key, value }
0a830c8 806
            }
0a830c8 807
            _ => {
3d6f280 808
                let u = self.unwrapExprNode(node);
3d6f280 809
                Arg::Positional(self.parseExpression(u))
0a830c8 810
            }
0a830c8 811
        }
0a830c8 812
    }
0a830c8 813
3d6f280 814
    fn parseClassCall(&self, node: Node) -> ClassCall {
0000000 815
        // class_call: type_identifier ("[" type,* "]")? class_argument_list
0000000 816
        let type_name = node.child_by_field_name("type").map(|n| self.text(n)).unwrap_or_default();
0000000 817
        // Filtering by NODE KIND (rather than the "generics" field, which
0000000 818
        // tree-sitter attaches once per repeated element, not as a single
0000000 819
        // group) — mirrors `parseType`'s own handling of a type's nested
0000000 820
        // generics list, and sidesteps the optional bracket entirely: if it
0000000 821
        // wasn't written, there are simply no "type" children to find.
0000000 822
        let generics: Vec<Type> = {
0000000 823
            let mut cursor = node.walk();
0000000 824
            node.named_children(&mut cursor)
0000000 825
                .filter(|n| n.kind() == "type")
0000000 826
                .map(|n| self.parseType(n))
0000000 827
                .collect()
0000000 828
        };
0000000 829
        let fields = node.child_by_field_name("arguments")
0a830c8 830
            .map(|args_node| {
0a830c8 831
                // class_argument_list: "(" (var_identifier ":" expression),* ")"
0a830c8 832
                // Named children alternate: var_identifier, expression, ...
0a830c8 833
                let mut cursor = args_node.walk();
0a830c8 834
                let named: Vec<Node> = args_node.named_children(&mut cursor).collect();
0a830c8 835
                named.chunks(2).filter_map(|chunk| {
0a830c8 836
                    if chunk.len() == 2 {
0a830c8 837
                        let name = self.text(chunk[0]);
3d6f280 838
                        let u = self.unwrapExprNode(chunk[1]);
3d6f280 839
                        Some(FieldArg { name, value: self.parseExpression(u) })
0a830c8 840
                    } else {
0a830c8 841
                        None
0a830c8 842
                    }
0a830c8 843
                }).collect()
0a830c8 844
            })
0a830c8 845
            .unwrap_or_default();
0000000 846
        ClassCall { type_name, fields, generics }
0a830c8 847
    }
0a830c8 848
0a830c8 849
    // ---- string literals --------------------------------------------------
0a830c8 850
3d6f280 851
    fn parseString(&self, node: Node) -> StringExpr {
0a830c8 852
        let mut cursor = node.walk();
0a830c8 853
        let parts = node
0a830c8 854
            .named_children(&mut cursor)
0a830c8 855
            .filter_map(|n| match n.kind() {
0000000 856
                "string_content" => Some(StringPart::Text(decodeEscapes(&self.text(n)))),
0a830c8 857
                "interpolation" => {
0a830c8 858
                    n.named_child(0).map(|e| {
3d6f280 859
                        let u = self.unwrapExprNode(e);
3d6f280 860
                        StringPart::Interp(self.parsePrimaryExpression(u))
0a830c8 861
                    })
0a830c8 862
                }
0a830c8 863
                _ => None,
0a830c8 864
            })
0a830c8 865
            .collect();
0a830c8 866
        StringExpr { parts }
0a830c8 867
    }
0a830c8 868
3d6f280 869
    fn parseStringRaw(&self, node: Node) -> String {
0a830c8 870
        let full = self.text(node);
0a830c8 871
        full.trim_matches('"').to_string()
0a830c8 872
    }
0a830c8 873
0a830c8 874
    // ---- numeric literals -------------------------------------------------
0a830c8 875
3d6f280 876
    fn parseInteger(&self, node: Node) -> i64 {
0a830c8 877
        let s = self.text(node).replace('_', "");
0a830c8 878
        if s.starts_with("0x") || s.starts_with("0X") {
0a830c8 879
            i64::from_str_radix(&s[2..], 16).unwrap_or(0)
0a830c8 880
        } else if s.starts_with("0b") || s.starts_with("0B") {
0a830c8 881
            i64::from_str_radix(&s[2..], 2).unwrap_or(0)
0a830c8 882
        } else {
0a830c8 883
            s.parse().unwrap_or(0)
0a830c8 884
        }
0a830c8 885
    }
0a830c8 886
3d6f280 887
    fn parseFloat(&self, node: Node) -> f64 {
0a830c8 888
        let s = self.text(node).trim_end_matches(['f', 'F']).replace('_', "");
0a830c8 889
        s.parse().unwrap_or(0.0)
0a830c8 890
    }
0a830c8 891
0a830c8 892
    // ---- helpers ----------------------------------------------------------
0a830c8 893
0a830c8 894
    /// Find the text of the first unnamed (punctuation/operator) non-whitespace child.
3d6f280 895
    fn findUnnamedOperator(&self, node: Node) -> String {
0a830c8 896
        let mut cursor = node.walk();
0a830c8 897
        for child in node.children(&mut cursor) {
0a830c8 898
            if !child.is_named() {
0a830c8 899
                let t = self.text(child);
0a830c8 900
                if !t.trim().is_empty() {
0a830c8 901
                    return t;
0a830c8 902
                }
0a830c8 903
            }
0a830c8 904
        }
0a830c8 905
        String::new()
0a830c8 906
    }
0a830c8 907
}
0a830c8 908
0000000 909
/// Decodes a string literal's raw source text (the grammar's `escape_sequence`
0000000 910
/// is matched at the lexer level but never actually interpreted anywhere — the
0000000 911
/// parser just handed back the literal source bytes, backslashes and all)
0000000 912
/// into its real content: `\n`/`\t`/`\\`/`\"`/etc single-char escapes, `\NNN`
0000000 913
/// (1-3 decimal digits), `\xXX`, `\uXXXX`, `\UXXXXXXXX`. An unrecognized escape
0000000 914
/// (including `\N{...}`) is passed through unchanged rather than erroring —
0000000 915
/// this only ever runs on text the grammar already accepted as a valid
0000000 916
/// `escape_sequence`, so "unrecognized" only means "not decoded yet."
0000000 917
fn decodeEscapes(s: &str) -> String {
0000000 918
    let chars: Vec<char> = s.chars().collect();
0000000 919
    let mut out = String::with_capacity(chars.len());
0000000 920
    let mut i = 0;
0000000 921
    while i < chars.len() {
0000000 922
        if chars[i] != '\\' || i + 1 >= chars.len() {
0000000 923
            out.push(chars[i]);
0000000 924
            i += 1;
0000000 925
            continue;
0000000 926
        }
0000000 927
        let next = chars[i + 1];
0000000 928
        match next {
0000000 929
            'n' => { out.push('\n'); i += 2; }
0000000 930
            't' => { out.push('\t'); i += 2; }
0000000 931
            'r' => { out.push('\r'); i += 2; }
0000000 932
            'a' => { out.push('\u{07}'); i += 2; }
0000000 933
            'b' => { out.push('\u{08}'); i += 2; }
0000000 934
            'f' => { out.push('\u{0C}'); i += 2; }
0000000 935
            'v' => { out.push('\u{0B}'); i += 2; }
0000000 936
            '\\' => { out.push('\\'); i += 2; }
0000000 937
            '\'' => { out.push('\''); i += 2; }
0000000 938
            '"' => { out.push('"'); i += 2; }
0000000 939
            '\n' => { i += 2; } // escaped literal newline: line continuation, emits nothing
0000000 940
            'x' => match decodeHexEscape(&chars, i + 2, 2) {
0000000 941
                Some((ch, consumed)) => { out.push(ch); i += 2 + consumed; }
0000000 942
                None => { out.push(chars[i]); i += 1; }
0000000 943
            },
0000000 944
            'u' => match decodeHexEscape(&chars, i + 2, 4) {
0000000 945
                Some((ch, consumed)) => { out.push(ch); i += 2 + consumed; }
0000000 946
                None => { out.push(chars[i]); i += 1; }
0000000 947
            },
0000000 948
            'U' => match decodeHexEscape(&chars, i + 2, 8) {
0000000 949
                Some((ch, consumed)) => { out.push(ch); i += 2 + consumed; }
0000000 950
                None => { out.push(chars[i]); i += 1; }
0000000 951
            },
0000000 952
            d if d.is_ascii_digit() => {
0000000 953
                let mut j = i + 1;
0000000 954
                while j < chars.len() && j < i + 4 && chars[j].is_ascii_digit() {
0000000 955
                    j += 1;
0000000 956
                }
0000000 957
                let digits: String = chars[i + 1..j].iter().collect();
0000000 958
                match digits.parse::<u32>().ok().and_then(char::from_u32) {
0000000 959
                    Some(ch) => { out.push(ch); i = j; }
0000000 960
                    None => { out.push(chars[i]); i += 1; }
0000000 961
                }
0000000 962
            }
0000000 963
            _ => { out.push(chars[i]); i += 1; } // e.g. `\N{...}` — pass through raw
0000000 964
        }
0000000 965
    }
0000000 966
    out
0000000 967
}
0000000 968
0000000 969
/// Decodes exactly `width` hex digits starting at `start` into a `char`, if
0000000 970
/// `start..start+width` are all present and form a valid codepoint. Returns
0000000 971
/// `(decoded_char, width)` on success so the caller advances past all of them.
0000000 972
fn decodeHexEscape(chars: &[char], start: usize, width: usize) -> Option<(char, usize)> {
0000000 973
    if start + width > chars.len() {
0000000 974
        return None;
0000000 975
    }
0000000 976
    let hex: String = chars[start..start + width].iter().collect();
0000000 977
    u32::from_str_radix(&hex, 16).ok().and_then(char::from_u32).map(|ch| (ch, width))
0000000 978
}
0000000 979
3d6f280 980
fn isExpressionKind(kind: &str) -> bool {
0a830c8 981
    matches!(
0a830c8 982
        kind,
0a830c8 983
        "binary_operator"
0a830c8 984
            | "unary_operator"
0a830c8 985
            | "boolean_operator"
0a830c8 986
            | "not_operator"
0a830c8 987
            | "comparison_operator"
0a830c8 988
            | "ternary_expression"
0a830c8 989
            | "attribute"
0a830c8 990
            | "fn_call"
0a830c8 991
            | "class_call"
0a830c8 992
            | "parenthesized_expression"
0a830c8 993
            | "string"
0a830c8 994
            | "integer"
0a830c8 995
            | "float"
0a830c8 996
            | "self"
0a830c8 997
            | "var_identifier"
0a830c8 998
            | "type_identifier"
0a830c8 999
    )
0a830c8 1000
}