git.delta.rocks / jrsonnet / refs/commits / 5ad3c0601af8

difftreelog

source

xtask/src/sourcegen/ast.rs8.6 KiBsourcehistory
1use std::collections::{BTreeSet, HashMap};23use proc_macro2::TokenStream;4use quote::format_ident;5use ungrammar::{Grammar, Rule};67use super::{8	util::{pluralize, to_lower_snake_case},9	KindsSrc,10};1112impl AstNodeSrc {13	pub fn remove_field(&mut self, to_remove: Vec<usize>) {14		to_remove.into_iter().rev().for_each(|idx| {15			self.fields.remove(idx);16		});17	}18}1920#[allow(dead_code)]21#[derive(Default, Debug)]22pub struct AstSrc {23	pub nodes: Vec<AstNodeSrc>,24	pub enums: Vec<AstEnumSrc>,25	pub token_enums: Vec<AstTokenEnumSrc>,26}27#[derive(Debug)]28pub struct AstNodeSrc {29	pub doc: Vec<String>,30	pub name: String,31	pub traits: Vec<String>,32	pub fields: Vec<Field>,33}3435#[derive(Debug, Eq, PartialEq)]36pub enum Field {37	Token(String),38	Node {39		name: String,40		ty: String,41		cardinality: Cardinality,42	},43}4445#[derive(Debug, Eq, PartialEq)]46pub enum Cardinality {47	/// This field may not exist in code48	Optional,49	/// This field should exist in correctly parsed code50	Required,51	/// There may be multiple field values of this kind52	Many,53}5455#[derive(Debug, Clone)]56pub struct AstEnumSrc {57	pub doc: Vec<String>,58	pub name: String,59	pub traits: Vec<String>,60	pub variants: Vec<String>,61}6263#[derive(Debug, Clone)]64pub struct AstTokenEnumSrc {65	pub doc: Vec<String>,66	pub name: String,67	pub variants: Vec<String>,68}6970impl Field {71	pub fn is_many(&self) -> bool {72		matches!(73			self,74			Field::Node {75				cardinality: Cardinality::Many,76				..77			}78		)79	}8081	pub fn token_name(&self) -> Option<String> {82		match self {83			Field::Token(token) => Some(token.clone()),84			_ => None,85		}86	}87	pub fn token_kind(&self, kinds: &KindsSrc) -> Option<TokenStream> {88		match self {89			Field::Token(token) => Some(kinds.token(token).expect("token exists").reference()),90			_ => None,91		}92	}93	pub fn is_token_enum(&self, grammar: &AstSrc) -> bool {94		match self {95			Field::Node { ty, .. } => grammar.token_enums.iter().any(|e| &e.name == ty),96			_ => false,97		}98	}99100	pub fn method_name(&self, kinds: &KindsSrc) -> proc_macro2::Ident {101		match self {102			Field::Token(name) => kinds.token(name).expect("token exists").method_name(),103			Field::Node { name, .. } => {104				format_ident!("{}", name)105			}106		}107	}108	pub fn ty(&self) -> proc_macro2::Ident {109		match self {110			Field::Token(_) => format_ident!("SyntaxToken"),111			Field::Node { ty, .. } => format_ident!("{}", ty),112		}113	}114}115116pub fn lower(kinds: &KindsSrc, grammar: &Grammar) -> AstSrc {117	let mut res = AstSrc {118		// tokens,119		..Default::default()120	};121122	let nodes = grammar.iter().collect::<Vec<_>>();123124	for &node in &nodes {125		let name = grammar[node].name.clone();126		let rule = &grammar[node].rule;127		match lower_enum(grammar, rule) {128			Some(variants) => {129				let enum_src = AstEnumSrc {130					doc: Vec::new(),131					name,132					traits: Vec::new(),133					variants,134				};135				res.enums.push(enum_src);136			}137			None => match lower_token_enum(grammar, rule) {138				Some(variants) => {139					let tokens_enum_src = AstTokenEnumSrc {140						doc: Vec::new(),141						name,142						variants,143					};144					res.token_enums.push(tokens_enum_src);145				}146				None => {147					let mut fields = Vec::new();148					lower_rule(&mut fields, grammar, None, rule, false);149					let mut types = HashMap::new();150					for field in fields.iter().filter(|f| f.token_name().is_none()) {151						if let Some(old) = types.insert(field.ty(), field.method_name(kinds)) {152							panic!("{name}.{} has same type as {name}.{}, resolve conflict by wrapping one field: {}", old, field.method_name(kinds), field.ty());153						}154					}155					res.nodes.push(AstNodeSrc {156						doc: Vec::new(),157						name,158						traits: Vec::new(),159						fields,160					});161				}162			},163		}164	}165166	deduplicate_fields(&mut res);167	extract_struct_traits(kinds, &mut res);168	extract_enum_traits(&mut res);169	res170}171172fn lower_enum(grammar: &Grammar, rule: &Rule) -> Option<Vec<String>> {173	let alternatives = match rule {174		Rule::Alt(it) => it,175		_ => return None,176	};177	let mut variants = Vec::new();178	for alternative in alternatives {179		match alternative {180			Rule::Node(it) => variants.push(grammar[*it].name.clone()),181			Rule::Token(it) if grammar[*it].name == ";" => (),182			_ => return None,183		}184	}185	Some(variants)186}187fn lower_token_enum(grammar: &Grammar, rule: &Rule) -> Option<Vec<String>> {188	let alternatives = match rule {189		Rule::Alt(it) => it,190		_ => return None,191	};192	let mut variants = Vec::new();193	for alternative in alternatives {194		match alternative {195			Rule::Token(it) => variants.push(grammar[*it].name.clone()),196			_ => return None,197		}198	}199	Some(variants)200}201202fn lower_rule(203	acc: &mut Vec<Field>,204	grammar: &Grammar,205	label: Option<&String>,206	rule: &Rule,207	in_optional: bool,208) {209	if lower_comma_list(acc, grammar, label, rule) {210		return;211	}212213	match rule {214		Rule::Node(node) => {215			let ty = grammar[*node].name.clone();216			let name = label.cloned().unwrap_or_else(|| to_lower_snake_case(&ty));217			let field = Field::Node {218				name,219				ty,220				cardinality: if in_optional {221					Cardinality::Optional222				} else {223					Cardinality::Required224				},225			};226			acc.push(field);227		}228		Rule::Token(token) => {229			assert!(label.is_none(), "uexpected label: {:?}", label);230			let name = grammar[*token].name.clone();231			let field = Field::Token(name);232			acc.push(field);233		}234		Rule::Rep(inner) => {235			if let Rule::Node(node) = &**inner {236				let ty = grammar[*node].name.clone();237				let name = label238					.cloned()239					.unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));240				let field = Field::Node {241					name,242					ty,243					cardinality: Cardinality::Many,244				};245				acc.push(field);246				return;247			}248			todo!("unsupported repitition: {:?}", rule)249		}250		Rule::Labeled { label: l, rule } => {251			assert!(label.is_none());252			lower_rule(acc, grammar, Some(l), rule, in_optional);253		}254		Rule::Seq(rules) | Rule::Alt(rules) => {255			for rule in rules {256				lower_rule(acc, grammar, label, rule, in_optional)257			}258		}259		Rule::Opt(rule) => lower_rule(acc, grammar, label, rule, true),260	}261}262263// (T (',' T)* ','?)264fn lower_comma_list(265	acc: &mut Vec<Field>,266	grammar: &Grammar,267	label: Option<&String>,268	rule: &Rule,269) -> bool {270	let rule = match rule {271		Rule::Seq(it) => it,272		_ => return false,273	};274	let (node, repeat, trailing_comma) = match rule.as_slice() {275		[Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_comma)] => {276			(node, repeat, trailing_comma)277		}278		_ => return false,279	};280	let repeat = match &**repeat {281		Rule::Seq(it) => it,282		_ => return false,283	};284	match repeat.as_slice() {285		[comma, Rule::Node(n)] if comma == &**trailing_comma && n == node => (),286		_ => return false,287	}288	let ty = grammar[*node].name.clone();289	let name = label290		.cloned()291		.unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));292	let field = Field::Node {293		name,294		ty,295		cardinality: Cardinality::Many,296	};297	acc.push(field);298	true299}300301fn deduplicate_fields(ast: &mut AstSrc) {302	for node in &mut ast.nodes {303		let mut i = 0;304		'outer: while i < node.fields.len() {305			for j in 0..i {306				let f1 = &node.fields[i];307				let f2 = &node.fields[j];308				if f1 == f2 {309					node.fields.remove(i);310					continue 'outer;311				}312			}313			i += 1;314		}315	}316}317318fn extract_struct_traits(kinds: &KindsSrc, ast: &mut AstSrc) {319	// TODO: add common accessor traits here.320	let traits: &[(&str, &[&str])] = &[];321322	for node in &mut ast.nodes {323		for (name, methods) in traits {324			extract_struct_trait(kinds, node, name, methods);325		}326	}327}328329fn extract_struct_trait(330	kinds: &KindsSrc,331	node: &mut AstNodeSrc,332	trait_name: &str,333	methods: &[&str],334) {335	let mut to_remove = Vec::new();336	for (i, field) in node.fields.iter().enumerate() {337		let method_name = field.method_name(kinds).to_string();338		if methods.iter().any(|&it| it == method_name) {339			to_remove.push(i);340		}341	}342	if to_remove.len() == methods.len() {343		node.traits.push(trait_name.to_string());344		node.remove_field(to_remove);345	}346}347348fn extract_enum_traits(ast: &mut AstSrc) {349	let enums = ast.enums.clone();350	for enm in &mut ast.enums {351		let nodes = &ast.nodes;352353		let mut variant_traits = enm.variants.iter().map(|var| {354			nodes355				.iter()356				.find_map(|node| {357					if &node.name != var {358						return None;359					}360					Some(node.traits.iter().cloned().collect::<BTreeSet<_>>())361				})362				.unwrap_or_else(|| {363					enums364						.iter()365						.find_map(|node| {366							if &node.name != var {367								return None;368							}369							Some(node.traits.iter().cloned().collect::<BTreeSet<_>>())370						})371						.unwrap_or_else(|| {372							panic!("could not find struct {var} for enum {}::{var}", enm.name)373						})374				})375		});376377		let mut enum_traits = match variant_traits.next() {378			Some(it) => it,379			None => continue,380		};381		for traits in variant_traits {382			enum_traits = enum_traits.intersection(&traits).cloned().collect();383		}384		enm.traits = enum_traits.into_iter().collect();385	}386}