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