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 153 }154 155 156 157 158 159 160 161 162 163 164 165 166 167 }168 res.nodes.push(AstNodeSrc {169 doc: Vec::new(),170 name,171 traits: Vec::new(),172 fields,173 });174 }175 },176 }177 }178179 deduplicate_fields(&mut res);180 extract_struct_traits(kinds, &mut res);181 extract_enum_traits(&mut res);182 res183}184185fn lower_enum(grammar: &Grammar, rule: &Rule) -> Option<Vec<String>> {186 let alternatives = match rule {187 Rule::Alt(it) => it,188 _ => return None,189 };190 let mut variants = Vec::new();191 for alternative in alternatives {192 match alternative {193 Rule::Node(it) => variants.push(grammar[*it].name.clone()),194 Rule::Token(it) if grammar[*it].name == ";" => (),195 _ => return None,196 }197 }198 Some(variants)199}200fn lower_token_enum(grammar: &Grammar, rule: &Rule) -> Option<Vec<String>> {201 let alternatives = match rule {202 Rule::Alt(it) => it,203 _ => return None,204 };205 let mut variants = Vec::new();206 for alternative in alternatives {207 match alternative {208 Rule::Token(it) => variants.push(grammar[*it].name.clone()),209 _ => return None,210 }211 }212 Some(variants)213}214215fn lower_rule(216 acc: &mut Vec<Field>,217 grammar: &Grammar,218 label: Option<&String>,219 rule: &Rule,220 in_optional: bool,221) {222 if lower_comma_list(acc, grammar, label, rule) {223 return;224 }225226 match rule {227 Rule::Node(node) => {228 let ty = grammar[*node].name.clone();229 let name = label.cloned().unwrap_or_else(|| to_lower_snake_case(&ty));230 let field = Field::Node {231 name,232 ty,233 cardinality: if in_optional {234 Cardinality::Optional235 } else {236 Cardinality::Required237 },238 };239 acc.push(field);240 }241 Rule::Token(token) => {242 assert!(label.is_none(), "uexpected label: {:?}", label);243 let name = grammar[*token].name.clone();244 let field = Field::Token(name);245 acc.push(field);246 }247 Rule::Rep(inner) => {248 if let Rule::Node(node) = &**inner {249 let ty = grammar[*node].name.clone();250 let name = label251 .cloned()252 .unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));253 let field = Field::Node {254 name,255 ty,256 cardinality: Cardinality::Many,257 };258 acc.push(field);259 return;260 }261 todo!("unsupported repitition: {:?}", rule)262 }263 Rule::Labeled { label: l, rule } => {264 assert!(label.is_none());265 lower_rule(acc, grammar, Some(l), rule, in_optional);266 }267 Rule::Seq(rules) | Rule::Alt(rules) => {268 for rule in rules {269 lower_rule(acc, grammar, label, rule, in_optional)270 }271 }272 Rule::Opt(rule) => lower_rule(acc, grammar, label, rule, true),273 }274}275276277fn lower_comma_list(278 acc: &mut Vec<Field>,279 grammar: &Grammar,280 label: Option<&String>,281 rule: &Rule,282) -> bool {283 let rule = match rule {284 Rule::Seq(it) => it,285 _ => return false,286 };287 let (node, repeat, trailing_comma) = match rule.as_slice() {288 [Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_comma)] => {289 (node, repeat, trailing_comma)290 }291 _ => return false,292 };293 let repeat = match &**repeat {294 Rule::Seq(it) => it,295 _ => return false,296 };297 match repeat.as_slice() {298 [comma, Rule::Node(n)] if comma == &**trailing_comma && n == node => (),299 _ => return false,300 }301 let ty = grammar[*node].name.clone();302 let name = label303 .cloned()304 .unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));305 let field = Field::Node {306 name,307 ty,308 cardinality: Cardinality::Many,309 };310 acc.push(field);311 true312}313314fn deduplicate_fields(ast: &mut AstSrc) {315 for node in &mut ast.nodes {316 let mut i = 0;317 'outer: while i < node.fields.len() {318 for j in 0..i {319 let f1 = &node.fields[i];320 let f2 = &node.fields[j];321 if f1 == f2 {322 node.fields.remove(i);323 continue 'outer;324 }325 }326 i += 1;327 }328 }329}330331fn extract_struct_traits(kinds: &KindsSrc, ast: &mut AstSrc) {332 333 let traits: &[(&str, &[&str])] = &[];334335 for node in &mut ast.nodes {336 for (name, methods) in traits {337 extract_struct_trait(kinds, node, name, methods);338 }339 }340}341342fn extract_struct_trait(343 kinds: &KindsSrc,344 node: &mut AstNodeSrc,345 trait_name: &str,346 methods: &[&str],347) {348 let mut to_remove = Vec::new();349 for (i, field) in node.fields.iter().enumerate() {350 let method_name = field.method_name(kinds).to_string();351 if methods.iter().any(|&it| it == method_name) {352 to_remove.push(i);353 }354 }355 if to_remove.len() == methods.len() {356 node.traits.push(trait_name.to_string());357 node.remove_field(to_remove);358 }359}360361fn extract_enum_traits(ast: &mut AstSrc) {362 let enums = ast.enums.clone();363 for enm in &mut ast.enums {364 let nodes = &ast.nodes;365366 let mut variant_traits = enm.variants.iter().map(|var| {367 nodes368 .iter()369 .find_map(|node| {370 if &node.name != var {371 return None;372 }373 Some(node.traits.iter().cloned().collect::<BTreeSet<_>>())374 })375 .unwrap_or_else(|| {376 enums377 .iter()378 .find_map(|node| {379 if &node.name != var {380 return None;381 }382 Some(node.traits.iter().cloned().collect::<BTreeSet<_>>())383 })384 .unwrap_or_else(|| {385 panic!("could not find struct {var} for enum {}::{var}", enm.name)386 })387 })388 });389390 let mut enum_traits = match variant_traits.next() {391 Some(it) => it,392 None => continue,393 };394 for traits in variant_traits {395 enum_traits = enum_traits.intersection(&traits).cloned().collect();396 }397 enm.traits = enum_traits.into_iter().collect();398 }399}