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 48 Optional,49 50 Required,51 52 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 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 154 }155 156 157 158 159 160 161 162 163 164 165 166 167 168 }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}276277278fn 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 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}