git.delta.rocks / jrsonnet / refs/commits / c678cf8e698f

difftreelog

source

xtask/src/sourcegen/mod.rs21.1 KiBsourcehistory
1use std::{2	collections::{BTreeSet, HashSet},3	path::PathBuf,4};56use anyhow::Result;7use ast::{AstEnumSrc, AstNodeSrc, AstSrc, Cardinality, Field};8use itertools::Itertools;9use proc_macro2::{Punct, Spacing, TokenStream};10use quote::{format_ident, quote};11use ungrammar::{Grammar, Rule};12use util::{13	ensure_file_contents, pluralize, reformat, to_lower_snake_case, to_pascal_case,14	to_upper_snake_case,15};1617mod ast;18mod util;1920pub fn generate_ungrammar() -> Result<()> {21	let grammar: Grammar = include_str!(concat!(22		env!("CARGO_MANIFEST_DIR"),23		"/../crates/jrsonnet-rowan-parser/jsonnet.ungram"24	))25	.parse()?;2627	let mut kinds: KindsSrc = KindsSrc {28		punct: puncts![29			"||" => "OR";30			"&&" => "AND";31			"|" => "BIT_OR";32			"^" => "BIT_XOR";33			"&" => "BIT_AND";34			"==" => "EQ";35			"!=" => "NE";36			"<" => "LT";37			">" => "GT";38			"<=" => "LE";39			">=" => "GE";40			"<<" => "LHS";41			">>" => "RHS";42			"+" => "PLUS";43			"-" => "MINUS";44			"*" => "MUL";45			"/" => "DIV";46			"%" => "MODULO";47			"!" => "NOT";48			"~" => "BIT_NOT";49			"[" => "L_BRACK";50			"]" => "R_BRACK";51			"(" => "L_PAREN";52			")" => "R_PAREN";53			"{" => "L_BRACE";54			"}" => "R_BRACE";55			":" => "COLON";56			"::" => "COLONCOLON";57			":::" => "COLONCOLONCOLON";58			";" => "SEMI";59			"." => "DOT";60			"..." => "DOTDOTDOT";61			"," => "COMMA";62			"$" => "DOLLAR";63			"=" => "ASSIGN";64			"?" => "QUESTION_MARK";65			"$intrinsicThisFile" => "INTRINSIC_THIS_FILE";66			"$intrinsicId" => "INTRINSIC_ID";67			"$intrinsic" => "INTRINSIC";68		],69		keywords: vec![],70		literals: literals![71			"NUMBER" => r"(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?";72			"STRING_DOUBLE" => "\"(?s:[^\"\\\\]|\\\\.)*\"";73			"STRING_SINGLE" => "'(?s:[^'\\\\]|\\\\.)*'";74			"STRING_DOUBLE_VERBATIM" => "@\"(?:[^\"]|\"\")*\"";75			"STRING_SINGLE_VERBATIM" => "@'(?:[^']|'')*'";76			"STRING_BLOCK" => r"\|\|\|";7778			"IDENT" => r"[_a-zA-Z][_a-zA-Z0-9]*";79			"WHITESPACE" => r"[ \t\n\r]+";80			"SINGLE_LINE_SLASH_COMMENT" => r"//[^\r\n]*(\r\n|\n)?";81			"SINGLE_LINE_HASH_COMMENT" => r"#[^\r\n]*(\r\n|\n)?";82			"MULTI_LINE_COMMENT" => r"/\*([^*]|\*[^/])*\*/";83		],84		nodes: vec![],85	};8687	let ast = lower(&kinds, &grammar);8889	for node in &ast.nodes {90		let name = to_upper_snake_case(&node.name);91		if !kinds.is_literal(&name) {92			kinds.nodes.push(name);93		}94	}95	for enum_ in &ast.enums {96		let name = to_upper_snake_case(&enum_.name);97		if !kinds.is_literal(&name) {98			kinds.nodes.push(name);99		}100	}101	for token in grammar.tokens() {102		let token = &grammar[token];103		let token = &token.name.clone();104		let name = to_upper_snake_case(token);105		if !kinds.is_punct(token) && !kinds.is_literal(&name) {106			kinds.keywords.push(token.to_owned());107		}108	}109110	let syntax_kinds = generate_syntax_kinds(&kinds)?;111112	let tokens = generate_tokens(&ast)?;113114	let nodes = generate_nodes(&kinds, &ast)?;115	ensure_file_contents(116		&PathBuf::from(concat!(117			env!("CARGO_MANIFEST_DIR"),118			"/../crates/jrsonnet-rowan-parser/src/generated/syntax_kinds.rs",119		)),120		&syntax_kinds,121	)?;122	ensure_file_contents(123		&PathBuf::from(concat!(124			env!("CARGO_MANIFEST_DIR"),125			"/../crates/jrsonnet-rowan-parser/src/generated/tokens.rs",126		)),127		&tokens,128	)?;129	ensure_file_contents(130		&PathBuf::from(concat!(131			env!("CARGO_MANIFEST_DIR"),132			"/../crates/jrsonnet-rowan-parser/src/generated/nodes.rs",133		)),134		&nodes,135	)?;136	Ok(())137}138139fn generate_tokens(grammar: &AstSrc) -> Result<String> {140	let tokens = grammar.tokens.iter().map(|token| {141		let name = format_ident!("{}", token);142		let kind = format_ident!("{}", to_upper_snake_case(token));143		quote! {144			#[derive(Debug, Clone, PartialEq, Eq, Hash)]145			pub struct #name {146				pub(crate) syntax: SyntaxToken,147			}148			impl std::fmt::Display for #name {149				fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {150					std::fmt::Display::fmt(&self.syntax, f)151				}152			}153			impl AstToken for #name {154				fn can_cast(kind: SyntaxKind) -> bool { kind == #kind }155				fn cast(syntax: SyntaxToken) -> Option<Self> {156					if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }157				}158				fn syntax(&self) -> &SyntaxToken { &self.syntax }159			}160		}161	});162163	Ok(reformat(164		&quote! {165			use crate::{SyntaxKind::{self, *}, SyntaxToken, ast::AstToken};166			#(#tokens)*167		}168		.to_string(),169	)?170	.replace("#[derive", "\n#[derive"))171}172173fn generate_syntax_kinds(grammar: &KindsSrc) -> Result<String> {174	let (single_byte_tokens_values, single_byte_tokens): (Vec<_>, Vec<_>) = grammar175		.punct176		.iter()177		.filter(|(token, _name)| token.len() == 1)178		.map(|(token, name)| (token.chars().next().unwrap(), format_ident!("{}", name)))179		.unzip();180181	let punctuation_values = grammar182		.punct183		.iter()184		.map(|(token, _name)| escape_token_macro(token));185	let punctuation = grammar186		.punct187		.iter()188		.map(|(_token, name)| format_ident!("{}", name))189		.collect::<Vec<_>>();190	let punctuation_enum = grammar191		.punct192		.iter()193		.map(|(token, name)| {194			let id = format_ident!("{}", name);195			quote! {196				#[token(#token)]197				#id198			}199		})200		.collect::<Vec<_>>();201202	let x = |name: &str| format_ident!("{}_KW", to_upper_snake_case(name));203	let full_keywords_values = &grammar.keywords;204	let full_keywords = full_keywords_values.iter().map(|s| x(s.as_str()));205206	let all_keywords_values = grammar.keywords.to_vec();207	let all_keywords_idents = all_keywords_values.iter().map(|kw| format_ident!("{}", kw));208	let all_keywords = all_keywords_values209		.iter()210		.map(|s| x(&**s))211		.collect::<Vec<_>>();212	let all_keywords_enum = all_keywords_values213		.iter()214		.map(|s| {215			let id = x(&**s);216			quote! {217				#[token(#s)]218				#id219			}220		})221		.collect::<Vec<_>>();222223	let tokens_enum = grammar224		.literals225		.iter()226		.map(|l| {227			let regex = &l.regex;228			let id = format_ident!("{}", l.name);229			let lexer = l230				.lexer231				.as_ref()232				.map(|l| {233					let id: TokenStream = l.parse().expect("path");234					quote! {235						, #id236					}237				})238				.unwrap_or_else(|| quote! {});239			quote! {240				#[regex(#regex #lexer)]241				#id242			}243		})244		.collect::<Vec<_>>();245246	let nodes = grammar247		.nodes248		.iter()249		.map(|name| format_ident!("{}", name))250		.collect::<Vec<_>>();251252	let ast = quote! {253		#![allow(bad_style, missing_docs, unreachable_pub)]254		use logos::Logos;255256		/// The kind of syntax node, e.g. `IDENT`, `USE_KW`, or `STRUCT`.257		#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Logos)]258		#[repr(u16)]259		pub enum SyntaxKind {260			#[doc(hidden)]261			TOMBSTONE,262			#[doc(hidden)]263			EOF,264			#(#punctuation_enum,)*265			#(#all_keywords_enum,)*266			#(#tokens_enum,)*267			#[error]268			ERROR,269			#(#nodes,)*270			#[doc(hidden)]271			__LAST,272		}273		use self::SyntaxKind::*;274275		impl SyntaxKind {276			pub fn is_keyword(self) -> bool {277				match self {278					#(#all_keywords)|* => true,279					_ => false,280				}281			}282283			pub fn is_punct(self) -> bool {284				match self {285					#(#punctuation)|* => true,286					_ => false,287				}288			}289290			pub fn from_keyword(ident: &str) -> Option<SyntaxKind> {291				let kw = match ident {292					#(#full_keywords_values => #full_keywords,)*293					_ => return None,294				};295				Some(kw)296			}297298			pub fn from_char(c: char) -> Option<SyntaxKind> {299				let tok = match c {300					#(#single_byte_tokens_values => #single_byte_tokens,)*301					_ => return None,302				};303				Some(tok)304			}305306			pub fn from_raw(r: u16) -> Self {307				assert!(r < Self::__LAST as u16);308				unsafe { std::mem::transmute(r) }309			}310			pub fn into_raw(self) -> u16 {311				self as u16312			}313		}314315		#[macro_export]316		macro_rules! T {317			#([#punctuation_values] => { $crate::SyntaxKind::#punctuation };)*318			#([#all_keywords_idents] => { $crate::SyntaxKind::#all_keywords };)*319			[lifetime_ident] => { $crate::SyntaxKind::LIFETIME_IDENT };320			[ident] => { $crate::SyntaxKind::IDENT };321			[shebang] => { $crate::SyntaxKind::SHEBANG };322		}323		pub use T;324	};325326	reformat(&ast.to_string())327}328329pub struct KindsSrc {330	pub punct: Vec<(String, String)>,331	pub keywords: Vec<String>,332	pub literals: Vec<LiteralKind>,333	pub nodes: Vec<String>,334}335336pub struct LiteralKind {337	name: String,338	regex: String,339	lexer: Option<String>,340}341342#[macro_export]343macro_rules! literals {344	($($name:expr => $regex:expr $(, $lexer:expr)?);* $(;)?) => {345		vec![346			$(LiteralKind {347				name: $name.to_owned(),348				regex: $regex.to_owned(),349				lexer: None $(.or_else(|| Some($lexer.to_string())))?,350			}),*351		]352	};353}354355#[macro_export]356macro_rules! puncts {357	($($tok:expr => $name:expr);* $(;)?) => {358		vec![359			$(($tok.to_owned(), $name.to_owned())),*360		]361	};362}363use crate::{literals, puncts};364365impl KindsSrc {366	pub fn is_punct(&self, tok: &str) -> bool {367		self.punct.iter().any(|(t, _)| *t == tok)368	}369	pub fn is_literal(&self, tok: &str) -> bool {370		self.literals.iter().any(|l| l.name == tok)371	}372373	fn get_punct_name(&self, tok: &str) -> Option<&str> {374		self.punct375			.iter()376			.find(|(t, _)| *t == tok)377			.map(|(_, n)| n.as_str())378	}379}380381fn generate_nodes(kinds: &KindsSrc, grammar: &AstSrc) -> Result<String> {382	let (node_defs, node_boilerplate_impls): (Vec<_>, Vec<_>) = grammar383		.nodes384		.iter()385		.map(|node| {386			let name = format_ident!("{}", node.name);387			let kind = format_ident!("{}", to_upper_snake_case(&node.name));388			let traits = node.traits.iter().map(|trait_name| {389				let trait_name = format_ident!("{}", trait_name);390				quote!(impl ast::#trait_name for #name {})391			});392393			let methods = node.fields.iter().map(|field| {394				let method_name = field.method_name(kinds);395				let ty = field.ty();396397				if field.is_many() {398					quote! {399						pub fn #method_name(&self) -> AstChildren<#ty> {400							support::children(&self.syntax)401						}402					}403				} else if let Some(token_kind) = field.token_kind() {404					quote! {405						pub fn #method_name(&self) -> Option<#ty> {406							support::token(&self.syntax, #token_kind)407						}408					}409				} else {410					quote! {411						pub fn #method_name(&self) -> Option<#ty> {412							support::child(&self.syntax)413						}414					}415				}416			});417			(418				quote! {419					#[pretty_doc_comment_placeholder_workaround]420					#[derive(Debug, Clone, PartialEq, Eq, Hash)]421					pub struct #name {422						pub(crate) syntax: SyntaxNode,423					}424425					#(#traits)*426427					impl #name {428						#(#methods)*429					}430				},431				quote! {432					impl AstNode for #name {433						fn can_cast(kind: SyntaxKind) -> bool {434							kind == #kind435						}436						fn cast(syntax: SyntaxNode) -> Option<Self> {437							if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }438						}439						fn syntax(&self) -> &SyntaxNode { &self.syntax }440					}441				},442			)443		})444		.unzip();445446	let (enum_defs, enum_boilerplate_impls): (Vec<_>, Vec<_>) = grammar447		.enums448		.iter()449		.map(|en| {450			let variants: Vec<_> = en451				.variants452				.iter()453				.map(|var| format_ident!("{}", var))454				.collect();455			let name = format_ident!("{}", en.name);456			let kinds: Vec<_> = variants457				.iter()458				.map(|name| format_ident!("{}", to_upper_snake_case(&name.to_string())))459				.collect();460			let traits = en.traits.iter().map(|trait_name| {461				let trait_name = format_ident!("{}", trait_name);462				quote!(impl ast::#trait_name for #name {})463			});464465			let ast_node = quote! {466				impl AstNode for #name {467					fn can_cast(kind: SyntaxKind) -> bool {468						match kind {469							#(#kinds)|* => true,470							_ => false,471						}472					}473					fn cast(syntax: SyntaxNode) -> Option<Self> {474						let res = match syntax.kind() {475							#(476							#kinds => #name::#variants(#variants { syntax }),477							)*478							_ => return None,479						};480						Some(res)481					}482					fn syntax(&self) -> &SyntaxNode {483						match self {484							#(485							#name::#variants(it) => &it.syntax,486							)*487						}488					}489				}490			};491492			(493				quote! {494					#[pretty_doc_comment_placeholder_workaround]495					#[derive(Debug, Clone, PartialEq, Eq, Hash)]496					pub enum #name {497						#(#variants(#variants),)*498					}499500					#(#traits)*501				},502				quote! {503					#(504						impl From<#variants> for #name {505							fn from(node: #variants) -> #name {506								#name::#variants(node)507							}508						}509					)*510					#ast_node511				},512			)513		})514		.unzip();515516	let (any_node_defs, any_node_boilerplate_impls): (Vec<_>, Vec<_>) = grammar517		.nodes518		.iter()519		.flat_map(|node| node.traits.iter().map(move |t| (t, node)))520		.into_group_map()521		.into_iter()522		.sorted_by_key(|(k, _)| *k)523		.map(|(trait_name, nodes)| {524			let name = format_ident!("Any{}", trait_name);525			let trait_name = format_ident!("{}", trait_name);526			let kinds: Vec<_> = nodes527				.iter()528				.map(|name| format_ident!("{}", to_upper_snake_case(&name.name.to_string())))529				.collect();530531			(532				quote! {533					#[pretty_doc_comment_placeholder_workaround]534					#[derive(Debug, Clone, PartialEq, Eq, Hash)]535					pub struct #name {536						pub(crate) syntax: SyntaxNode,537					}538					impl ast::#trait_name for #name {}539				},540				quote! {541					impl #name {542						#[inline]543						pub fn new<T: ast::#trait_name>(node: T) -> #name {544							#name {545								syntax: node.syntax().clone()546							}547						}548					}549					impl AstNode for #name {550						fn can_cast(kind: SyntaxKind) -> bool {551							match kind {552								#(#kinds)|* => true,553								_ => false,554							}555						}556						fn cast(syntax: SyntaxNode) -> Option<Self> {557							Self::can_cast(syntax.kind()).then(|| #name { syntax })558						}559						fn syntax(&self) -> &SyntaxNode {560							&self.syntax561						}562					}563				},564			)565		})566		.unzip();567568	let enum_names = grammar.enums.iter().map(|it| &it.name);569	let node_names = grammar.nodes.iter().map(|it| &it.name);570571	let display_impls = enum_names572		.chain(node_names.clone())573		.map(|it| format_ident!("{}", it))574		.map(|name| {575			quote! {576				impl std::fmt::Display for #name {577					fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {578						std::fmt::Display::fmt(self.syntax(), f)579					}580				}581			}582		});583584	let defined_nodes: HashSet<_> = node_names.collect();585586	for node in kinds587		.nodes588		.iter()589		.map(|kind| to_pascal_case(kind))590		.filter(|name| !defined_nodes.iter().any(|&it| it == name))591	{592		drop(node)593		// FIXME: restore this594		// eprintln!("Warning: node {} not defined in ast source", node);595	}596597	let ast = quote! {598		#![allow(non_snake_case)]599		use crate::{600			SyntaxNode, SyntaxToken, SyntaxKind::{self, *},601			ast::{self, AstNode, AstChildren, support},602			T,603		};604605		#(#node_defs)*606		#(#enum_defs)*607		#(#any_node_defs)*608		#(#node_boilerplate_impls)*609		#(#enum_boilerplate_impls)*610		#(#any_node_boilerplate_impls)*611		#(#display_impls)*612	};613614	let ast = ast.to_string().replace("T ! [", "T![");615616	let mut res = String::with_capacity(ast.len() * 2);617618	let mut docs = grammar619		.nodes620		.iter()621		.map(|it| &it.doc)622		.chain(grammar.enums.iter().map(|it| &it.doc));623624	for chunk in ast.split("# [pretty_doc_comment_placeholder_workaround] ") {625		res.push_str(chunk);626		if let Some(doc) = docs.next() {627			write_doc_comment(doc, &mut res);628		}629	}630631	let res = reformat(&res)?;632	Ok(res.replace("#[derive", "\n#[derive"))633}634635fn write_doc_comment(contents: &[String], dest: &mut String) {636	use std::fmt::Write;637	for line in contents {638		writeln!(dest, "///{}", line).unwrap();639	}640}641642fn lower(kinds: &KindsSrc, grammar: &Grammar) -> AstSrc {643	let tokens = "Whitespace Comment String StringVerbantim StringBlock Number Ident"644		.split_ascii_whitespace()645		.map(|it| it.to_string())646		.collect::<Vec<_>>();647648	let mut res = AstSrc {649		tokens,650		..Default::default()651	};652653	let nodes = grammar.iter().collect::<Vec<_>>();654655	for &node in &nodes {656		let name = grammar[node].name.clone();657		let rule = &grammar[node].rule;658		match lower_enum(grammar, rule) {659			Some(variants) => {660				let enum_src = AstEnumSrc {661					doc: Vec::new(),662					name,663					traits: Vec::new(),664					variants,665				};666				res.enums.push(enum_src);667			}668			None => {669				let mut fields = Vec::new();670				lower_rule(&mut fields, grammar, None, rule);671				res.nodes.push(AstNodeSrc {672					doc: Vec::new(),673					name,674					traits: Vec::new(),675					fields,676				});677			}678		}679	}680681	deduplicate_fields(&mut res);682	extract_enums(&mut res);683	extract_struct_traits(kinds, &mut res);684	extract_enum_traits(&mut res);685	res686}687688fn lower_enum(grammar: &Grammar, rule: &Rule) -> Option<Vec<String>> {689	let alternatives = match rule {690		Rule::Alt(it) => it,691		_ => return None,692	};693	let mut variants = Vec::new();694	for alternative in alternatives {695		match alternative {696			Rule::Node(it) => variants.push(grammar[*it].name.clone()),697			Rule::Token(it) if grammar[*it].name == ";" => (),698			_ => return None,699		}700	}701	Some(variants)702}703704fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, rule: &Rule) {705	if lower_comma_list(acc, grammar, label, rule) {706		return;707	}708709	match rule {710		Rule::Node(node) => {711			let ty = grammar[*node].name.clone();712			let name = label.cloned().unwrap_or_else(|| to_lower_snake_case(&ty));713			let field = Field::Node {714				name,715				ty,716				cardinality: Cardinality::Optional,717			};718			acc.push(field);719		}720		Rule::Token(token) => {721			assert!(label.is_none(), "uexpected label: {:?}", label);722			let name = grammar[*token].name.clone();723			let field = Field::Token(name);724			acc.push(field);725		}726		Rule::Rep(inner) => {727			if let Rule::Node(node) = &**inner {728				let ty = grammar[*node].name.clone();729				let name = label730					.cloned()731					.unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));732				let field = Field::Node {733					name,734					ty,735					cardinality: Cardinality::Many,736				};737				acc.push(field);738				return;739			}740			todo!("unsupported repitition: {:?}", rule)741		}742		Rule::Labeled { label: l, rule } => {743			assert!(label.is_none());744			lower_rule(acc, grammar, Some(l), rule);745		}746		Rule::Seq(rules) | Rule::Alt(rules) => {747			for rule in rules {748				lower_rule(acc, grammar, label, rule)749			}750		}751		Rule::Opt(rule) => lower_rule(acc, grammar, label, rule),752	}753}754755// (T (',' T)* ','?)756fn lower_comma_list(757	acc: &mut Vec<Field>,758	grammar: &Grammar,759	label: Option<&String>,760	rule: &Rule,761) -> bool {762	let rule = match rule {763		Rule::Seq(it) => it,764		_ => return false,765	};766	let (node, repeat, trailing_comma) = match rule.as_slice() {767		[Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_comma)] => {768			(node, repeat, trailing_comma)769		}770		_ => return false,771	};772	let repeat = match &**repeat {773		Rule::Seq(it) => it,774		_ => return false,775	};776	match repeat.as_slice() {777		[comma, Rule::Node(n)] if comma == &**trailing_comma && n == node => (),778		_ => return false,779	}780	let ty = grammar[*node].name.clone();781	let name = label782		.cloned()783		.unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));784	let field = Field::Node {785		name,786		ty,787		cardinality: Cardinality::Many,788	};789	acc.push(field);790	true791}792793fn deduplicate_fields(ast: &mut AstSrc) {794	for node in &mut ast.nodes {795		let mut i = 0;796		'outer: while i < node.fields.len() {797			for j in 0..i {798				let f1 = &node.fields[i];799				let f2 = &node.fields[j];800				if f1 == f2 {801					node.fields.remove(i);802					continue 'outer;803				}804			}805			i += 1;806		}807	}808}809810fn extract_enums(ast: &mut AstSrc) {811	for node in &mut ast.nodes {812		for enm in &ast.enums {813			let mut to_remove = Vec::new();814			for (i, field) in node.fields.iter().enumerate() {815				let ty = field.ty().to_string();816				if enm.variants.iter().any(|it| it == &ty) {817					to_remove.push(i);818				}819			}820			if to_remove.len() == enm.variants.len() {821				node.remove_field(to_remove);822				let ty = enm.name.clone();823				let name = to_lower_snake_case(&ty);824				node.fields.push(Field::Node {825					name,826					ty,827					cardinality: Cardinality::Optional,828				});829			}830		}831	}832}833834fn extract_struct_traits(kinds: &KindsSrc, ast: &mut AstSrc) {835	// TODO: add common accessor traits here.836	let traits: &[(&str, &[&str])] = &[];837838	for node in &mut ast.nodes {839		for (name, methods) in traits {840			extract_struct_trait(kinds, node, name, methods);841		}842	}843}844845fn extract_struct_trait(846	kinds: &KindsSrc,847	node: &mut AstNodeSrc,848	trait_name: &str,849	methods: &[&str],850) {851	let mut to_remove = Vec::new();852	for (i, field) in node.fields.iter().enumerate() {853		let method_name = field.method_name(kinds).to_string();854		if methods.iter().any(|&it| it == method_name) {855			to_remove.push(i);856		}857	}858	if to_remove.len() == methods.len() {859		node.traits.push(trait_name.to_string());860		node.remove_field(to_remove);861	}862}863864fn extract_enum_traits(ast: &mut AstSrc) {865	let enums = ast.enums.clone();866	for enm in &mut ast.enums {867		if enm.name == "Stmt" {868			continue;869		}870		let nodes = &ast.nodes;871872		let mut variant_traits = enm.variants.iter().map(|var| {873			nodes874				.iter()875				.find_map(|node| {876					if &node.name != var {877						return None;878					}879					Some(node.traits.iter().cloned().collect::<BTreeSet<_>>())880				})881				.unwrap_or_else(|| {882					enums883						.iter()884						.find_map(|node| {885							if &node.name != var {886								return None;887							}888							Some(node.traits.iter().cloned().collect::<BTreeSet<_>>())889						})890						.unwrap_or_else(|| {891							panic!("{}", {892								&format!(893									"Could not find a struct `{}` for enum `{}::{}`",894									var, enm.name, var895								)896							})897						})898				})899		});900901		let mut enum_traits = match variant_traits.next() {902			Some(it) => it,903			None => continue,904		};905		for traits in variant_traits {906			enum_traits = enum_traits.intersection(&traits).cloned().collect();907		}908		enm.traits = enum_traits.into_iter().collect();909	}910}911912pub fn escape_token_macro(token: &str) -> TokenStream {913	if "{}[]()$".contains(token) {914		let c = token.chars().next().unwrap();915		quote! { #c }916	} else if token.contains('$') {917		quote! { #token }918	} else {919		let cs = token.chars().map(|c| Punct::new(c, Spacing::Joint));920		quote! { #(#cs)* }921	}922}