1use std::collections::{BTreeSet, HashMap};23use proc_macro2::TokenStream;4use quote::format_ident;5use ungrammar::{Grammar, Rule};67use super::{8 KindsSrc,9 util::{pluralize, to_lower_snake_case},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 Self::Node {76 cardinality: Cardinality::Many,77 ..78 }79 )80 }8182 pub fn token_name(&self) -> Option<String> {83 match self {84 Self::Token(token) => Some(token.clone()),85 Self::Node { .. } => None,86 }87 }88 pub fn token_kind(&self, kinds: &KindsSrc) -> Option<TokenStream> {89 match self {90 Self::Token(token) => Some(kinds.token(token).expect("token exists").reference()),91 Self::Node { .. } => None,92 }93 }94 pub fn is_token_enum(&self, grammar: &AstSrc) -> bool {95 match self {96 Self::Node { ty, .. } => grammar.token_enums.iter().any(|e| &e.name == ty),97 Self::Token(_) => false,98 }99 }100101 pub fn method_name(&self, kinds: &KindsSrc) -> proc_macro2::Ident {102 match self {103 Self::Token(name) => kinds.token(name).expect("token exists").method_name(),104 Self::Node { name, .. } => {105 format_ident!("{}", name)106 }107 }108 }109 pub fn ty(&self) -> proc_macro2::Ident {110 match self {111 Self::Token(_) => format_ident!("SyntaxToken"),112 Self::Node { ty, .. } => format_ident!("{}", ty),113 }114 }115}116117pub fn lower(kinds: &KindsSrc, grammar: &Grammar) -> AstSrc {118 let mut res = AstSrc::default();119120 let nodes = grammar.iter().collect::<Vec<_>>();121122 for &node in &nodes {123 let name = grammar[node].name.clone();124 let rule = &grammar[node].rule;125 match lower_enum(grammar, rule) {126 Some(variants) => {127 let enum_src = AstEnumSrc {128 doc: Vec::new(),129 name,130 traits: Vec::new(),131 variants,132 };133 res.enums.push(enum_src);134 }135 None => {136 if let Some(variants) = lower_token_enum(grammar, rule) {137 let tokens_enum_src = AstTokenEnumSrc {138 doc: Vec::new(),139 name,140 variants,141 };142 res.token_enums.push(tokens_enum_src);143 } else {144 let mut fields = Vec::new();145 lower_rule(&mut fields, grammar, None, rule, false);146 let mut types = HashMap::new();147 for field in fields.iter().filter(|f| f.token_name().is_none()) {148 if let Some(_old) = types.insert(field.ty(), field.method_name(kinds)) {149 150 }151 152 153 154 155 156 157 158 159 160 161 162 163 164 }165 res.nodes.push(AstNodeSrc {166 doc: Vec::new(),167 name,168 traits: Vec::new(),169 fields,170 });171 }172 }173 }174 }175176 deduplicate_fields(&mut res);177 extract_struct_traits(kinds, &mut res);178 extract_enum_traits(&mut res);179 res180}181182fn lower_enum(grammar: &Grammar, rule: &Rule) -> Option<Vec<String>> {183 let alternatives = match rule {184 Rule::Alt(it) => it,185 _ => return None,186 };187 let mut variants = Vec::new();188 for alternative in alternatives {189 match alternative {190 Rule::Node(it) => variants.push(grammar[*it].name.clone()),191 Rule::Token(it) if grammar[*it].name == ";" => (),192 _ => return None,193 }194 }195 Some(variants)196}197fn lower_token_enum(grammar: &Grammar, rule: &Rule) -> Option<Vec<String>> {198 let alternatives = match rule {199 Rule::Alt(it) => it,200 _ => return None,201 };202 let mut variants = Vec::new();203 for alternative in alternatives {204 match alternative {205 Rule::Token(it) => variants.push(grammar[*it].name.clone()),206 _ => return None,207 }208 }209 Some(variants)210}211212fn lower_rule(213 acc: &mut Vec<Field>,214 grammar: &Grammar,215 label: Option<&String>,216 rule: &Rule,217 in_optional: bool,218) {219 if lower_comma_list(acc, grammar, label, rule) {220 return;221 }222223 match rule {224 Rule::Node(node) => {225 let ty = grammar[*node].name.clone();226 let name = label.cloned().unwrap_or_else(|| to_lower_snake_case(&ty));227 let field = Field::Node {228 name,229 ty,230 cardinality: if in_optional {231 Cardinality::Optional232 } else {233 Cardinality::Required234 },235 };236 acc.push(field);237 }238 Rule::Token(token) => {239 assert!(label.is_none(), "uexpected label: {label:?}");240 let name = grammar[*token].name.clone();241 let field = Field::Token(name);242 acc.push(field);243 }244 Rule::Rep(inner) => {245 if let Rule::Node(node) = &**inner {246 let ty = grammar[*node].name.clone();247 let name = label248 .cloned()249 .unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));250 let field = Field::Node {251 name,252 ty,253 cardinality: Cardinality::Many,254 };255 acc.push(field);256 return;257 }258 todo!("unsupported repitition: {:?}", rule)259 }260 Rule::Labeled { label: l, rule } => {261 assert!(label.is_none());262 lower_rule(acc, grammar, Some(l), rule, in_optional);263 }264 Rule::Seq(rules) | Rule::Alt(rules) => {265 for rule in rules {266 lower_rule(acc, grammar, label, rule, in_optional);267 }268 }269 Rule::Opt(rule) => lower_rule(acc, grammar, label, rule, true),270 }271}272273274fn lower_comma_list(275 acc: &mut Vec<Field>,276 grammar: &Grammar,277 label: Option<&String>,278 rule: &Rule,279) -> bool {280 let rule = match rule {281 Rule::Seq(it) => it,282 _ => return false,283 };284 let (node, repeat, trailing_comma) = match rule.as_slice() {285 [286 Rule::Node(node),287 Rule::Rep(repeat),288 Rule::Opt(trailing_comma),289 ] => (node, repeat, trailing_comma),290 _ => return false,291 };292 let repeat = match &**repeat {293 Rule::Seq(it) => it,294 _ => return false,295 };296 match repeat.as_slice() {297 [comma, Rule::Node(n)] if comma == &**trailing_comma && n == node => (),298 _ => return false,299 }300 let ty = grammar[*node].name.clone();301 let name = label302 .cloned()303 .unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));304 let field = Field::Node {305 name,306 ty,307 cardinality: Cardinality::Many,308 };309 acc.push(field);310 true311}312313fn deduplicate_fields(ast: &mut AstSrc) {314 for node in &mut ast.nodes {315 let mut i = 0;316 'outer: while i < node.fields.len() {317 for j in 0..i {318 let f1 = &node.fields[i];319 let f2 = &node.fields[j];320 if f1 == f2 {321 node.fields.remove(i);322 continue 'outer;323 }324 }325 i += 1;326 }327 }328}329330fn extract_struct_traits(kinds: &KindsSrc, ast: &mut AstSrc) {331 332 let traits: &[(&str, &[&str])] = &[];333334 for node in &mut ast.nodes {335 for (name, methods) in traits {336 extract_struct_trait(kinds, node, name, methods);337 }338 }339}340341fn extract_struct_trait(342 kinds: &KindsSrc,343 node: &mut AstNodeSrc,344 trait_name: &str,345 methods: &[&str],346) {347 let mut to_remove = Vec::new();348 for (i, field) in node.fields.iter().enumerate() {349 let method_name = field.method_name(kinds).to_string();350 if methods.iter().any(|&it| it == method_name) {351 to_remove.push(i);352 }353 }354 if to_remove.len() == methods.len() {355 node.traits.push(trait_name.to_string());356 node.remove_field(to_remove);357 }358}359360fn extract_enum_traits(ast: &mut AstSrc) {361 let enums = ast.enums.clone();362 for enm in &mut ast.enums {363 let nodes = &ast.nodes;364365 let mut variant_traits = enm.variants.iter().map(|var| {366 nodes367 .iter()368 .find_map(|node| {369 if &node.name != var {370 return None;371 }372 Some(node.traits.iter().cloned().collect::<BTreeSet<_>>())373 })374 .unwrap_or_else(|| {375 enums376 .iter()377 .find_map(|node| {378 if &node.name != var {379 return None;380 }381 Some(node.traits.iter().cloned().collect::<BTreeSet<_>>())382 })383 .unwrap_or_else(|| {384 panic!("could not find struct {var} for enum {}::{var}", enm.name)385 })386 })387 });388389 let mut enum_traits = match variant_traits.next() {390 Some(it) => it,391 None => continue,392 };393 for traits in variant_traits {394 enum_traits = enum_traits.intersection(&traits).cloned().collect();395 }396 enm.traits = enum_traits.into_iter().collect();397 }398}