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

difftreelog

source

xtask/src/sourcegen/ast.rs9.0 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	#[allow(dead_code)]66	pub doc: Vec<String>,67	pub name: String,68	pub variants: Vec<String>,69}7071impl Field {72	pub fn is_many(&self) -> bool {73		matches!(74			self,75			Field::Node {76				cardinality: Cardinality::Many,77				..78			}79		)80	}8182	pub fn token_name(&self) -> Option<String> {83		match self {84			Field::Token(token) => Some(token.clone()),85			_ => None,86		}87	}88	pub fn token_kind(&self, kinds: &KindsSrc) -> Option<TokenStream> {89		match self {90			Field::Token(token) => Some(kinds.token(token).expect("token exists").reference()),91			_ => None,92		}93	}94	pub fn is_token_enum(&self, grammar: &AstSrc) -> bool {95		match self {96			Field::Node { ty, .. } => grammar.token_enums.iter().any(|e| &e.name == ty),97			_ => false,98		}99	}100101	pub fn method_name(&self, kinds: &KindsSrc) -> proc_macro2::Ident {102		match self {103			Field::Token(name) => kinds.token(name).expect("token exists").method_name(),104			Field::Node { name, .. } => {105				format_ident!("{}", name)106			}107		}108	}109	pub fn ty(&self) -> proc_macro2::Ident {110		match self {111			Field::Token(_) => format_ident!("SyntaxToken"),112			Field::Node { ty, .. } => format_ident!("{}", ty),113		}114	}115}116117pub fn lower(kinds: &KindsSrc, grammar: &Grammar) -> AstSrc {118	let mut res = AstSrc {119		// tokens,120		..Default::default()121	};122123	let nodes = grammar.iter().collect::<Vec<_>>();124125	for &node in &nodes {126		let name = grammar[node].name.clone();127		let rule = &grammar[node].rule;128		match lower_enum(grammar, rule) {129			Some(variants) => {130				let enum_src = AstEnumSrc {131					doc: Vec::new(),132					name,133					traits: Vec::new(),134					variants,135				};136				res.enums.push(enum_src);137			}138			None => match lower_token_enum(grammar, rule) {139				Some(variants) => {140					let tokens_enum_src = AstTokenEnumSrc {141						doc: Vec::new(),142						name,143						variants,144					};145					res.token_enums.push(tokens_enum_src);146				}147				None => {148					let mut fields = Vec::new();149					lower_rule(&mut fields, grammar, None, rule, false);150					let mut types = HashMap::new();151					for field in fields.iter().filter(|f| f.token_name().is_none()) {152						if let Some(_old) = types.insert(field.ty(), field.method_name(kinds)) {153							// panic!("{name}.{} has same type as {name}.{}, resolve conflict by wrapping one field: {}", old, field.method_name(kinds), field.ty());154						}155						// TODO: check for assignable field types, i.e you can have156						// ```157						// SomeEnum =158						//     SomeItem159						// |   SomeOtherItem160						// ```161						// And check above will fail to detect conflict in162						// ```163						// SomeStruct =164						//     SomeEnum165						//     SomeItem166						// ```167						// Despite generating getters, which will both return SomeEnum168					}169					res.nodes.push(AstNodeSrc {170						doc: Vec::new(),171						name,172						traits: Vec::new(),173						fields,174					});175				}176			},177		}178	}179180	deduplicate_fields(&mut res);181	extract_struct_traits(kinds, &mut res);182	extract_enum_traits(&mut res);183	res184}185186fn lower_enum(grammar: &Grammar, rule: &Rule) -> Option<Vec<String>> {187	let alternatives = match rule {188		Rule::Alt(it) => it,189		_ => return None,190	};191	let mut variants = Vec::new();192	for alternative in alternatives {193		match alternative {194			Rule::Node(it) => variants.push(grammar[*it].name.clone()),195			Rule::Token(it) if grammar[*it].name == ";" => (),196			_ => return None,197		}198	}199	Some(variants)200}201fn lower_token_enum(grammar: &Grammar, rule: &Rule) -> Option<Vec<String>> {202	let alternatives = match rule {203		Rule::Alt(it) => it,204		_ => return None,205	};206	let mut variants = Vec::new();207	for alternative in alternatives {208		match alternative {209			Rule::Token(it) => variants.push(grammar[*it].name.clone()),210			_ => return None,211		}212	}213	Some(variants)214}215216fn lower_rule(217	acc: &mut Vec<Field>,218	grammar: &Grammar,219	label: Option<&String>,220	rule: &Rule,221	in_optional: bool,222) {223	if lower_comma_list(acc, grammar, label, rule) {224		return;225	}226227	match rule {228		Rule::Node(node) => {229			let ty = grammar[*node].name.clone();230			let name = label.cloned().unwrap_or_else(|| to_lower_snake_case(&ty));231			let field = Field::Node {232				name,233				ty,234				cardinality: if in_optional {235					Cardinality::Optional236				} else {237					Cardinality::Required238				},239			};240			acc.push(field);241		}242		Rule::Token(token) => {243			assert!(label.is_none(), "uexpected label: {:?}", label);244			let name = grammar[*token].name.clone();245			let field = Field::Token(name);246			acc.push(field);247		}248		Rule::Rep(inner) => {249			if let Rule::Node(node) = &**inner {250				let ty = grammar[*node].name.clone();251				let name = label252					.cloned()253					.unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));254				let field = Field::Node {255					name,256					ty,257					cardinality: Cardinality::Many,258				};259				acc.push(field);260				return;261			}262			todo!("unsupported repitition: {:?}", rule)263		}264		Rule::Labeled { label: l, rule } => {265			assert!(label.is_none());266			lower_rule(acc, grammar, Some(l), rule, in_optional);267		}268		Rule::Seq(rules) | Rule::Alt(rules) => {269			for rule in rules {270				lower_rule(acc, grammar, label, rule, in_optional)271			}272		}273		Rule::Opt(rule) => lower_rule(acc, grammar, label, rule, true),274	}275}276277// (T (',' T)* ','?)278fn lower_comma_list(279	acc: &mut Vec<Field>,280	grammar: &Grammar,281	label: Option<&String>,282	rule: &Rule,283) -> bool {284	let rule = match rule {285		Rule::Seq(it) => it,286		_ => return false,287	};288	let (node, repeat, trailing_comma) = match rule.as_slice() {289		[Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_comma)] => {290			(node, repeat, trailing_comma)291		}292		_ => return false,293	};294	let repeat = match &**repeat {295		Rule::Seq(it) => it,296		_ => return false,297	};298	match repeat.as_slice() {299		[comma, Rule::Node(n)] if comma == &**trailing_comma && n == node => (),300		_ => return false,301	}302	let ty = grammar[*node].name.clone();303	let name = label304		.cloned()305		.unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));306	let field = Field::Node {307		name,308		ty,309		cardinality: Cardinality::Many,310	};311	acc.push(field);312	true313}314315fn deduplicate_fields(ast: &mut AstSrc) {316	for node in &mut ast.nodes {317		let mut i = 0;318		'outer: while i < node.fields.len() {319			for j in 0..i {320				let f1 = &node.fields[i];321				let f2 = &node.fields[j];322				if f1 == f2 {323					node.fields.remove(i);324					continue 'outer;325				}326			}327			i += 1;328		}329	}330}331332fn extract_struct_traits(kinds: &KindsSrc, ast: &mut AstSrc) {333	// TODO: add common accessor traits here.334	let traits: &[(&str, &[&str])] = &[];335336	for node in &mut ast.nodes {337		for (name, methods) in traits {338			extract_struct_trait(kinds, node, name, methods);339		}340	}341}342343fn extract_struct_trait(344	kinds: &KindsSrc,345	node: &mut AstNodeSrc,346	trait_name: &str,347	methods: &[&str],348) {349	let mut to_remove = Vec::new();350	for (i, field) in node.fields.iter().enumerate() {351		let method_name = field.method_name(kinds).to_string();352		if methods.iter().any(|&it| it == method_name) {353			to_remove.push(i);354		}355	}356	if to_remove.len() == methods.len() {357		node.traits.push(trait_name.to_string());358		node.remove_field(to_remove);359	}360}361362fn extract_enum_traits(ast: &mut AstSrc) {363	let enums = ast.enums.clone();364	for enm in &mut ast.enums {365		let nodes = &ast.nodes;366367		let mut variant_traits = enm.variants.iter().map(|var| {368			nodes369				.iter()370				.find_map(|node| {371					if &node.name != var {372						return None;373					}374					Some(node.traits.iter().cloned().collect::<BTreeSet<_>>())375				})376				.unwrap_or_else(|| {377					enums378						.iter()379						.find_map(|node| {380							if &node.name != var {381								return None;382							}383							Some(node.traits.iter().cloned().collect::<BTreeSet<_>>())384						})385						.unwrap_or_else(|| {386							panic!("could not find struct {var} for enum {}::{var}", enm.name)387						})388				})389		});390391		let mut enum_traits = match variant_traits.next() {392			Some(it) => it,393			None => continue,394		};395		for traits in variant_traits {396			enum_traits = enum_traits.intersection(&traits).cloned().collect();397		}398		enm.traits = enum_traits.into_iter().collect();399	}400}