cairo_lang_syntax_codegen/
generator.rs

1use std::fs;
2use std::path::PathBuf;
3
4use genco::prelude::*;
5use xshell::Shell;
6
7use crate::cairo_spec::get_spec;
8use crate::spec::{Member, Node, NodeKind, Variant, Variants};
9
10pub fn project_root() -> PathBuf {
11    // This is the directory of Cargo.toml of the syntax_codegen crate.
12    let dir = env!("CARGO_MANIFEST_DIR");
13    // Pop the "/crates/cairo-lang-syntax-codegen" suffix.
14    let res = PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned();
15    assert!(res.join("Cargo.toml").exists(), "Could not find project root directory.");
16    res
17}
18
19pub fn ensure_file_content(filename: PathBuf, content: String) {
20    if let Ok(old_contents) = fs::read_to_string(&filename) {
21        if old_contents == content {
22            return;
23        }
24    }
25
26    fs::write(&filename, content).unwrap();
27}
28
29pub fn get_codes() -> Vec<(String, String)> {
30    vec![
31        (
32            "crates/cairo-lang-syntax/src/node/ast.rs".into(),
33            reformat_rust_code(generate_ast_code().to_string().unwrap()),
34        ),
35        (
36            "crates/cairo-lang-syntax/src/node/kind.rs".into(),
37            reformat_rust_code(generate_kinds_code().to_string().unwrap()),
38        ),
39        (
40            "crates/cairo-lang-syntax/src/node/key_fields.rs".into(),
41            reformat_rust_code(generate_key_fields_code().to_string().unwrap()),
42        ),
43    ]
44}
45
46pub fn reformat_rust_code(text: String) -> String {
47    // Since rustfmt is used with nightly features, it takes 2 runs to reach a fixed point.
48    reformat_rust_code_inner(reformat_rust_code_inner(text))
49}
50pub fn reformat_rust_code_inner(text: String) -> String {
51    let sh = Shell::new().unwrap();
52    let cmd = sh.cmd("rustfmt").env("RUSTUP_TOOLCHAIN", "nightly-2024-11-23");
53    let cmd_with_args = cmd.arg("--config-path").arg(project_root().join("rustfmt.toml"));
54    let mut stdout = cmd_with_args.stdin(text).read().unwrap();
55    if !stdout.ends_with('\n') {
56        stdout.push('\n');
57    }
58    stdout
59}
60
61fn generate_kinds_code() -> rust::Tokens {
62    let spec = get_spec();
63    let mut tokens = quote! {
64        $("// Autogenerated file. To regenerate, please run `cargo run --bin generate-syntax`.\n")
65        use core::fmt;
66    };
67
68    // SyntaxKind.
69    let kinds = name_tokens(&spec, |k| !matches!(k, NodeKind::Enum { .. }));
70    let token_kinds = name_tokens(&spec, |k| matches!(k, NodeKind::Token { .. }));
71    let keyword_token_kinds =
72        name_tokens(&spec, |k| matches!(k, NodeKind::Token { is_keyword } if *is_keyword));
73    let terminal_kinds = name_tokens(&spec, |k| matches!(k, NodeKind::Terminal { .. }));
74    let keyword_terminal_kinds =
75        name_tokens(&spec, |k| matches!(k, NodeKind::Terminal { is_keyword, .. } if *is_keyword));
76
77    tokens.extend(quote! {
78        #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
79        pub enum SyntaxKind {
80            $(for t in kinds => $t,)
81        }
82        impl SyntaxKind {
83            pub fn is_token(&self) -> bool {
84                matches!(
85                    *self,
86                    $(for t in token_kinds join ( | ) => SyntaxKind::$t)
87                )
88            }
89            pub fn is_terminal(&self) -> bool {
90                matches!(
91                    *self,
92                    $(for t in terminal_kinds join ( | ) => SyntaxKind::$t)
93                )
94            }
95            pub fn is_keyword_token(&self) -> bool {
96                matches!(
97                    *self,
98                    $(for t in keyword_token_kinds join ( | ) => SyntaxKind::$t)
99                )
100            }
101            pub fn is_keyword_terminal(&self) -> bool {
102                matches!(
103                    *self,
104                    $(for t in keyword_terminal_kinds join ( | ) => SyntaxKind::$t)
105                )
106            }
107        }
108        impl fmt::Display for SyntaxKind {
109            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110                write!(f, "{self:?}")
111            }
112        }
113    });
114    tokens
115}
116
117/// Returns an iterator to the names of the tokens matching `predicate`.
118fn name_tokens(spec: &[Node], predicate: impl Fn(&NodeKind) -> bool) -> impl Iterator<Item = &str> {
119    spec.iter().filter(move |n| predicate(&n.kind)).map(|n| n.name.as_str())
120}
121
122fn generate_key_fields_code() -> rust::Tokens {
123    let spec = get_spec();
124    let mut arms = rust::Tokens::new();
125
126    for Node { name, kind } in spec.into_iter() {
127        match kind {
128            NodeKind::Struct { members } | NodeKind::Terminal { members, .. } => {
129                let mut fields = rust::Tokens::new();
130                for (i, member) in members.into_iter().enumerate() {
131                    let field_name = member.name;
132                    if member.key {
133                        fields.extend(quote! { $("/*") $field_name $("*/") children[$i], });
134                    }
135                }
136                arms.extend(quote! {
137                    SyntaxKind::$name => {vec![$fields]},
138                });
139            }
140            NodeKind::List { .. } | NodeKind::SeparatedList { .. } | NodeKind::Token { .. } => {
141                arms.extend(quote! {
142                    SyntaxKind::$name => vec![],
143                });
144            }
145            NodeKind::Enum { .. } => {}
146        }
147    }
148    let tokens = quote! {
149        $("// Autogenerated file. To regenerate, please run `cargo run --bin generate-syntax`.\n")
150        use super::ids::GreenId;
151        use super::kind::SyntaxKind;
152
153        $("/// Gets the vector of children ids that are the indexing key for this SyntaxKind.\n")
154        $("///\n")
155        $("/// Each SyntaxKind has some children that are defined in the spec to be its indexing key\n")
156        $("/// for its stable pointer. See [super::stable_ptr].\n")
157        pub fn get_key_fields(kind: SyntaxKind, children: &[GreenId]) -> Vec<GreenId> {
158            match kind {
159                $arms
160            }
161        }
162    };
163    tokens
164}
165
166fn generate_ast_code() -> rust::Tokens {
167    let spec = get_spec();
168    let mut tokens = quote! {
169        $("// Autogenerated file. To regenerate, please run `cargo run --bin generate-syntax`.\n")
170        #![allow(clippy::match_single_binding)]
171        #![allow(clippy::too_many_arguments)]
172        #![allow(dead_code)]
173        #![allow(unused_variables)]
174        use std::ops::Deref;
175        use std::sync::Arc;
176
177        use cairo_lang_filesystem::span::TextWidth;
178        use cairo_lang_utils::{extract_matches, Intern, LookupIntern};
179        use smol_str::SmolStr;
180
181        use super::element_list::ElementList;
182        use super::green::GreenNodeDetails;
183        use super::kind::SyntaxKind;
184        use super::{
185            GreenId, GreenNode, SyntaxGroup, SyntaxNode, SyntaxStablePtr, SyntaxStablePtrId,
186            Terminal, Token, TypedStablePtr, TypedSyntaxNode,
187        };
188
189        #[path = "ast_ext.rs"]
190        mod ast_ext;
191    };
192    let spec_clone = spec.clone();
193    let all_tokens: Vec<_> =
194        spec_clone.iter().filter(|node| matches!(node.kind, NodeKind::Terminal { .. })).collect();
195    for Node { name, kind } in spec.into_iter() {
196        tokens.extend(match kind {
197            NodeKind::Enum { variants, missing_variant } => {
198                let variants_list = match variants {
199                    Variants::List(variants) => variants,
200                    Variants::AllTokens => all_tokens
201                        .iter()
202                        .map(|node| Variant { name: node.name.clone(), kind: node.name.clone() })
203                        .collect(),
204                };
205                gen_enum_code(name, variants_list, missing_variant)
206            }
207            NodeKind::Struct { members } => gen_struct_code(name, members, false),
208            NodeKind::Terminal { members, .. } => gen_struct_code(name, members, true),
209            NodeKind::Token { .. } => gen_token_code(name),
210            NodeKind::List { element_type } => gen_list_code(name, element_type),
211            NodeKind::SeparatedList { element_type, separator_type } => {
212                gen_separated_list_code(name, element_type, separator_type)
213            }
214        });
215    }
216    tokens
217}
218
219fn gen_list_code(name: String, element_type: String) -> rust::Tokens {
220    // TODO(spapini): Change Deref to Borrow.
221    let ptr_name = format!("{name}Ptr");
222    let green_name = format!("{name}Green");
223    let element_green_name = format!("{element_type}Green");
224    let common_code = gen_common_list_code(&name, &green_name, &ptr_name);
225    quote! {
226        #[derive(Clone, Debug, Eq, Hash, PartialEq)]
227        pub struct $(&name)(ElementList<$(&element_type),1>);
228        impl Deref for $(&name){
229            type Target = ElementList<$(&element_type),1>;
230
231            fn deref(&self) -> &Self::Target {
232                &self.0
233            }
234        }
235        impl $(&name){
236            pub fn new_green(
237                db: &dyn SyntaxGroup, children: Vec<$(&element_green_name)>
238            ) -> $(&green_name) {
239                let width = children.iter().map(|id|
240                    id.0.lookup_intern(db).width()).sum();
241                $(&green_name)(Arc::new(GreenNode {
242                    kind: SyntaxKind::$(&name),
243                    details: GreenNodeDetails::Node {
244                        children: children.iter().map(|x| x.0).collect(),
245                        width,
246                    },
247                }).intern(db))
248            }
249        }
250        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
251        pub struct $(&ptr_name)(pub SyntaxStablePtrId);
252        impl TypedStablePtr for $(&ptr_name) {
253            type SyntaxNode = $(&name);
254            fn untyped(&self) -> SyntaxStablePtrId {
255                self.0
256            }
257            fn lookup(&self, db: &dyn SyntaxGroup) -> $(&name) {
258                $(&name)::from_syntax_node(db, self.0.lookup(db))
259            }
260        }
261        impl From<$(&ptr_name)> for SyntaxStablePtrId {
262            fn from(ptr: $(&ptr_name)) -> Self {
263                ptr.untyped()
264            }
265        }
266        $common_code
267    }
268}
269
270fn gen_separated_list_code(
271    name: String,
272    element_type: String,
273    separator_type: String,
274) -> rust::Tokens {
275    // TODO(spapini): Change Deref to Borrow.
276    let ptr_name = format!("{name}Ptr");
277    let green_name = format!("{name}Green");
278    let element_or_separator_green_name = format!("{name}ElementOrSeparatorGreen");
279    let element_green_name = format!("{element_type}Green");
280    let separator_green_name = format!("{separator_type}Green");
281    let common_code = gen_common_list_code(&name, &green_name, &ptr_name);
282    quote! {
283        #[derive(Clone, Debug, Eq, Hash, PartialEq)]
284        pub struct $(&name)(ElementList<$(&element_type),2>);
285        impl Deref for $(&name){
286            type Target = ElementList<$(&element_type),2>;
287
288            fn deref(&self) -> &Self::Target {
289                &self.0
290            }
291        }
292        impl $(&name){
293            pub fn new_green(
294                db: &dyn SyntaxGroup, children: Vec<$(&element_or_separator_green_name)>
295            ) -> $(&green_name) {
296                let width = children.iter().map(|id|
297                    id.id().lookup_intern(db).width()).sum();
298                $(&green_name)(Arc::new(GreenNode {
299                    kind: SyntaxKind::$(&name),
300                    details: GreenNodeDetails::Node {
301                        children: children.iter().map(|x| x.id()).collect(),
302                        width,
303                    },
304                }).intern(db))
305            }
306        }
307        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
308        pub struct $(&ptr_name)(pub SyntaxStablePtrId);
309        impl TypedStablePtr for $(&ptr_name) {
310            type SyntaxNode = $(&name);
311            fn untyped(&self) -> SyntaxStablePtrId {
312                self.0
313            }
314            fn lookup(&self, db: &dyn SyntaxGroup) -> $(&name) {
315                $(&name)::from_syntax_node(db, self.0.lookup(db))
316            }
317        }
318        impl From<$(&ptr_name)> for SyntaxStablePtrId {
319            fn from(ptr: $(&ptr_name)) -> Self {
320                ptr.untyped()
321            }
322        }
323        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
324        pub enum $(&element_or_separator_green_name) {
325            Separator($(&separator_green_name)),
326            Element($(&element_green_name)),
327        }
328        impl From<$(&separator_green_name)> for $(&element_or_separator_green_name) {
329            fn from(value: $(&separator_green_name)) -> Self {
330                $(&element_or_separator_green_name)::Separator(value)
331            }
332        }
333        impl From<$(&element_green_name)> for $(&element_or_separator_green_name) {
334            fn from(value: $(&element_green_name)) -> Self {
335                $(&element_or_separator_green_name)::Element(value)
336            }
337        }
338        impl $(&element_or_separator_green_name) {
339            fn id(&self) -> GreenId {
340                match self {
341                    $(&element_or_separator_green_name)::Separator(green) => green.0,
342                    $(&element_or_separator_green_name)::Element(green) => green.0,
343                }
344            }
345        }
346        $common_code
347    }
348}
349
350fn gen_common_list_code(name: &str, green_name: &str, ptr_name: &str) -> rust::Tokens {
351    quote! {
352        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
353        pub struct $green_name(pub GreenId);
354        impl TypedSyntaxNode for $name {
355            const OPTIONAL_KIND: Option<SyntaxKind> = Some(SyntaxKind::$name);
356            type StablePtr = $ptr_name;
357            type Green = $green_name;
358            fn missing(db: &dyn SyntaxGroup) -> Self::Green {
359                $green_name(Arc::new(
360                    GreenNode {
361                        kind: SyntaxKind::$name,
362                        details: GreenNodeDetails::Node { children: vec![], width: TextWidth::default() },
363                    }).intern(db)
364                )
365            }
366            fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self {
367                Self(ElementList::new(node))
368            }
369            fn cast(db: &dyn SyntaxGroup, node: SyntaxNode) -> Option<Self> {
370                if node.kind(db) == SyntaxKind::$name {
371                    Some(Self(ElementList::new(node)))
372                } else {
373                    None
374                }
375            }
376            fn as_syntax_node(&self) -> SyntaxNode {
377                self.node.clone()
378            }
379            fn stable_ptr(&self) -> Self::StablePtr {
380                $ptr_name(self.node.0.stable_ptr)
381            }
382        }
383        impl From<&$name> for SyntaxStablePtrId {
384            fn from(node: &$name) -> Self {
385                node.stable_ptr().untyped()
386            }
387        }
388    }
389}
390
391fn gen_enum_code(
392    name: String,
393    variants: Vec<Variant>,
394    missing_variant: Option<Variant>,
395) -> rust::Tokens {
396    let ptr_name = format!("{name}Ptr");
397    let green_name = format!("{name}Green");
398    let mut enum_body = quote! {};
399    let mut from_node_body = quote! {};
400    let mut cast_body = quote! {};
401    let mut ptr_conversions = quote! {};
402    let mut green_conversions = quote! {};
403    for variant in &variants {
404        let n = &variant.name;
405        let k = &variant.kind;
406
407        enum_body.extend(quote! {
408            $n($k),
409        });
410        from_node_body.extend(quote! {
411            SyntaxKind::$k => $(&name)::$n($k::from_syntax_node(db, node)),
412        });
413        cast_body.extend(quote! {
414            SyntaxKind::$k => Some($(&name)::$n($k::from_syntax_node(db, node))),
415        });
416        let variant_ptr = format!("{k}Ptr");
417        ptr_conversions.extend(quote! {
418            impl From<$(&variant_ptr)> for $(&ptr_name) {
419                fn from(value: $(&variant_ptr)) -> Self {
420                    Self(value.0)
421                }
422            }
423        });
424        let variant_green = format!("{k}Green");
425        green_conversions.extend(quote! {
426            impl From<$(&variant_green)> for $(&green_name) {
427                fn from(value: $(&variant_green)) -> Self {
428                    Self(value.0)
429                }
430            }
431        });
432    }
433    let missing_body = match missing_variant {
434        Some(missing) => quote! {
435            $(&green_name)($(missing.kind)::missing(db).0)
436        },
437        None => quote! {
438            panic!("No missing variant.");
439        },
440    };
441    quote! {
442        #[derive(Clone, Debug, Eq, Hash, PartialEq)]
443        pub enum $(&name){
444            $enum_body
445        }
446        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
447        pub struct $(&ptr_name)(pub SyntaxStablePtrId);
448        impl TypedStablePtr for $(&ptr_name) {
449            type SyntaxNode = $(&name);
450            fn untyped(&self) -> SyntaxStablePtrId {
451                self.0
452            }
453            fn lookup(&self, db: &dyn SyntaxGroup) -> $(&name) {
454                $(&name)::from_syntax_node(db, self.0.lookup(db))
455            }
456        }
457        impl From<$(&ptr_name)> for SyntaxStablePtrId {
458            fn from(ptr: $(&ptr_name)) -> Self {
459                ptr.untyped()
460            }
461        }
462        $ptr_conversions
463        $green_conversions
464        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
465        pub struct $(&green_name)(pub GreenId);
466        impl TypedSyntaxNode for $(&name){
467            const OPTIONAL_KIND: Option<SyntaxKind> = None;
468            type StablePtr = $(&ptr_name);
469            type Green = $(&green_name);
470            fn missing(db: &dyn SyntaxGroup) -> Self::Green {
471                $missing_body
472            }
473            fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self {
474                let kind = node.kind(db);
475                match kind{
476                    $from_node_body
477                    _ => panic!(
478                        "Unexpected syntax kind {:?} when constructing {}.",
479                        kind,
480                        $[str]($[const](&name))),
481                }
482            }
483            fn cast(db: &dyn SyntaxGroup, node: SyntaxNode) -> Option<Self> {
484                let kind = node.kind(db);
485                match kind {
486                    $cast_body
487                    _ => None,
488                }
489            }
490            fn as_syntax_node(&self) -> SyntaxNode {
491                match self {
492                    $(for v in &variants => $(&name)::$(&v.name)(x) => x.as_syntax_node(),)
493                }
494            }
495            fn stable_ptr(&self) -> Self::StablePtr {
496                $(&ptr_name)(self.as_syntax_node().0.stable_ptr)
497            }
498        }
499        impl From<&$(&name)> for SyntaxStablePtrId {
500            fn from(node: &$(&name)) -> Self {
501                node.stable_ptr().untyped()
502            }
503        }
504        impl $(&name) {
505            $("/// Checks if a kind of a variant of [")$(&name)$("].\n")
506            pub fn is_variant(kind: SyntaxKind) -> bool {
507                matches!(kind, $(for v in &variants join (|) => SyntaxKind::$(&v.kind)))
508            }
509        }
510    }
511}
512
513fn gen_token_code(name: String) -> rust::Tokens {
514    let green_name = format!("{name}Green");
515    let ptr_name = format!("{name}Ptr");
516
517    quote! {
518        #[derive(Clone, Debug, Eq, Hash, PartialEq)]
519        pub struct $(&name) {
520            node: SyntaxNode,
521        }
522        impl Token for $(&name) {
523            fn new_green(db: &dyn SyntaxGroup, text: SmolStr) -> Self::Green {
524                $(&green_name)(Arc::new(GreenNode {
525                    kind: SyntaxKind::$(&name),
526                    details: GreenNodeDetails::Token(text),
527                }).intern(db))
528            }
529            fn text(&self, db: &dyn SyntaxGroup) -> SmolStr {
530                extract_matches!(&self.node.0.green.lookup_intern(db).details,
531                    GreenNodeDetails::Token).clone()
532            }
533        }
534        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
535        pub struct $(&ptr_name)(pub SyntaxStablePtrId);
536        impl TypedStablePtr for $(&ptr_name) {
537            type SyntaxNode = $(&name);
538            fn untyped(&self) -> SyntaxStablePtrId {
539                self.0
540            }
541            fn lookup(&self, db: &dyn SyntaxGroup) -> $(&name) {
542                $(&name)::from_syntax_node(db, self.0.lookup(db))
543            }
544        }
545        impl From<$(&ptr_name)> for SyntaxStablePtrId {
546            fn from(ptr: $(&ptr_name)) -> Self {
547                ptr.untyped()
548            }
549        }
550        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
551        pub struct $(&green_name)(pub GreenId);
552        impl $(&green_name) {
553            pub fn text(&self, db: &dyn SyntaxGroup) -> SmolStr {
554                extract_matches!(
555                    &self.0.lookup_intern(db).details, GreenNodeDetails::Token).clone()
556            }
557        }
558        impl TypedSyntaxNode for $(&name){
559            const OPTIONAL_KIND: Option<SyntaxKind> = Some(SyntaxKind::$(&name));
560            type StablePtr = $(&ptr_name);
561            type Green = $(&green_name);
562            fn missing(db: &dyn SyntaxGroup) -> Self::Green {
563                $(&green_name)(Arc::new(GreenNode {
564                    kind: SyntaxKind::TokenMissing,
565                    details: GreenNodeDetails::Token("".into()),
566                }).intern(db))
567            }
568            fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self {
569                match node.0.green.lookup_intern(db).details {
570                    GreenNodeDetails::Token(_) => Self { node },
571                    GreenNodeDetails::Node { .. } => panic!(
572                        "Expected a token {:?}, not an internal node",
573                        SyntaxKind::$(&name)
574                    ),
575                }
576            }
577            fn cast(db: &dyn SyntaxGroup, node: SyntaxNode) -> Option<Self> {
578                match node.0.green.lookup_intern(db).details {
579                    GreenNodeDetails::Token(_) => Some(Self { node }),
580                    GreenNodeDetails::Node { .. } => None,
581                }
582            }
583            fn as_syntax_node(&self) -> SyntaxNode {
584                self.node.clone()
585            }
586            fn stable_ptr(&self) -> Self::StablePtr {
587                $(&ptr_name)(self.node.0.stable_ptr)
588            }
589        }
590        impl From<&$(&name)> for SyntaxStablePtrId {
591            fn from(node: &$(&name)) -> Self {
592                node.stable_ptr().untyped()
593            }
594        }
595    }
596}
597
598fn gen_struct_code(name: String, members: Vec<Member>, is_terminal: bool) -> rust::Tokens {
599    let green_name = format!("{name}Green");
600    let mut body = rust::Tokens::new();
601    let mut field_indices = quote! {};
602    let mut args = quote! {};
603    let mut params = quote! {};
604    let mut args_for_missing = quote! {};
605    let mut ptr_getters = quote! {};
606    let mut key_field_index: usize = 0;
607    for (i, Member { name, kind, key }) in members.iter().enumerate() {
608        let index_name = format!("INDEX_{}", name.to_uppercase());
609        field_indices.extend(quote! {
610            pub const $index_name : usize = $i;
611        });
612        let key_name_green = format!("{name}_green");
613        args.extend(quote! {$name.0,});
614        // TODO(spapini): Validate that children SyntaxKinds are as expected.
615
616        let child_green = format!("{kind}Green");
617        params.extend(quote! {$name: $(&child_green),});
618        body.extend(quote! {
619            pub fn $name(&self, db: &dyn SyntaxGroup) -> $kind {
620                $kind::from_syntax_node(db, self.children[$i].clone())
621            }
622        });
623        args_for_missing.extend(quote! {$kind::missing(db).0,});
624
625        if *key {
626            ptr_getters.extend(quote! {
627                pub fn $(&key_name_green)(self, db: &dyn SyntaxGroup) -> $(&child_green) {
628                    let ptr = self.0.lookup_intern(db);
629                    if let SyntaxStablePtr::Child { key_fields, .. } = ptr {
630                        $(&child_green)(key_fields[$key_field_index])
631                    } else {
632                        panic!("Unexpected key field query on root.");
633                    }
634                }
635            });
636            key_field_index += 1;
637        }
638    }
639    let ptr_name = format!("{name}Ptr");
640    let new_green_impl = if is_terminal {
641        let token_name = name.replace("Terminal", "Token");
642        quote! {
643            impl Terminal for $(&name) {
644                const KIND: SyntaxKind = SyntaxKind::$(&name);
645                type TokenType = $(&token_name);
646                fn new_green(
647                    db: &dyn SyntaxGroup,
648                    leading_trivia: TriviaGreen,
649                    token: <<$(&name) as Terminal>::TokenType as TypedSyntaxNode>::Green,
650                    trailing_trivia: TriviaGreen
651                ) -> Self::Green {
652                    let children: Vec<GreenId> = vec![$args];
653                    let width = children.iter().copied().map(|id|
654                        id.lookup_intern(db).width()).sum();
655                    $(&green_name)(Arc::new(GreenNode {
656                        kind: SyntaxKind::$(&name),
657                        details: GreenNodeDetails::Node { children, width },
658                    }).intern(db))
659                }
660                fn text(&self, db: &dyn SyntaxGroup) -> SmolStr {
661                    self.token(db).text(db)
662                }
663            }
664        }
665    } else {
666        quote! {
667            impl $(&name) {
668                $field_indices
669                pub fn new_green(db: &dyn SyntaxGroup, $params) -> $(&green_name) {
670                    let children: Vec<GreenId> = vec![$args];
671                    let width = children.iter().copied().map(|id|
672                        id.lookup_intern(db).width()).sum();
673                    $(&green_name)(Arc::new(GreenNode {
674                        kind: SyntaxKind::$(&name),
675                        details: GreenNodeDetails::Node { children, width },
676                    }).intern(db))
677                }
678            }
679        }
680    };
681    quote! {
682        #[derive(Clone, Debug, Eq, Hash, PartialEq)]
683        pub struct $(&name) {
684            node: SyntaxNode,
685            children: Arc<[SyntaxNode]>,
686        }
687        $new_green_impl
688        impl $(&name) {
689            $body
690        }
691        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
692        pub struct $(&ptr_name)(pub SyntaxStablePtrId);
693        impl $(&ptr_name) {
694            $ptr_getters
695        }
696        impl TypedStablePtr for $(&ptr_name) {
697            type SyntaxNode = $(&name);
698            fn untyped(&self) -> SyntaxStablePtrId {
699                self.0
700            }
701            fn lookup(&self, db: &dyn SyntaxGroup) -> $(&name) {
702                $(&name)::from_syntax_node(db, self.0.lookup(db))
703            }
704        }
705        impl From<$(&ptr_name)> for SyntaxStablePtrId {
706            fn from(ptr: $(&ptr_name)) -> Self {
707                ptr.untyped()
708            }
709        }
710        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
711        pub struct $(&green_name)(pub GreenId);
712        impl TypedSyntaxNode for $(&name) {
713            const OPTIONAL_KIND: Option<SyntaxKind> = Some(SyntaxKind::$(&name));
714            type StablePtr = $(&ptr_name);
715            type Green = $(&green_name);
716            fn missing(db: &dyn SyntaxGroup) -> Self::Green {
717                // Note: A missing syntax element should result in an internal green node
718                // of width 0, with as much structure as possible.
719                $(&green_name)(Arc::new(GreenNode {
720                    kind: SyntaxKind::$(&name),
721                    details: GreenNodeDetails::Node {
722                        children: vec![$args_for_missing],
723                        width: TextWidth::default(),
724                    },
725                }).intern(db))
726            }
727            fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self {
728                let kind = node.kind(db);
729                assert_eq!(kind, SyntaxKind::$(&name), "Unexpected SyntaxKind {:?}. Expected {:?}.", kind, SyntaxKind::$(&name));
730                let children = db.get_children(node.clone());
731                Self { node, children }
732            }
733            fn cast(db: &dyn SyntaxGroup, node: SyntaxNode) -> Option<Self> {
734                let kind = node.kind(db);
735                if kind == SyntaxKind::$(&name) {
736                    Some(Self::from_syntax_node(db, node))
737                } else {
738                    None
739                }
740            }
741            fn as_syntax_node(&self) -> SyntaxNode {
742                self.node.clone()
743            }
744            fn stable_ptr(&self) -> Self::StablePtr {
745                $(&ptr_name)(self.node.0.stable_ptr)
746            }
747        }
748        impl From<&$(&name)> for SyntaxStablePtrId {
749            fn from(node: &$(&name)) -> Self {
750                node.stable_ptr().untyped()
751            }
752        }
753    }
754}