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 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 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}262263264fn 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 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}