plum
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 | } |