diff --git a/Cargo.toml b/Cargo.toml index 3375ceb..6fb6605 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ doctest = false test = false [patch.'crates-io'] -grammer = { git = "https://github.com/lykenware/grammer", rev = "6f6f1320336d84b75805907fb302a28cb6d4cfb0" } +grammer = { git = "https://github.com/lykenware/grammer", rev = "7c360f270d6482d3bc499b05fe2a3f46eb68a2d7" } [workspace] members = [ diff --git a/src/generate/rust.rs b/src/generate/rust.rs index 3bd302f..2c726f5 100644 --- a/src/generate/rust.rs +++ b/src/generate/rust.rs @@ -1,10 +1,10 @@ use crate::generate::src::{quote, Src}; use grammer::context::{Context, IRule, IStr}; use grammer::forest::NodeShape; -use grammer::rule::{Field, Fields, MatchesEmpty, Rule, RuleWithNamedFields, SepKind}; +use grammer::rule::{Fields, MatchesEmpty, Rule, RuleWithFields, SepKind}; use grammer::{proc_macro, scannerless}; -use indexmap::{IndexMap, IndexSet}; +use indexmap::{indexmap, IndexMap, IndexSet}; use std::borrow::Cow; use std::fmt::{self, Write as _}; use std::hash::Hash; @@ -78,104 +78,183 @@ impl RustInputPat for proc_macro::FlatTokenPat { } struct RuleMap<'a> { - named: &'a IndexMap, + named: &'a IndexMap, anon: IndexSet, } -struct Variant<'a> { - rule: IRule, - name: IStr, - fields: &'a Fields, +struct RustField { + ty: Src, + + /// Whether the field might not always be present, i.e. whether it's wrapped + /// in `Option<...>` (this is not encoded in `ty` to avoid repeated wrapping). + refutable: bool, + + /// Field paths into the tuples generated by `traverse!`. + // FIXME(eddyb) find a better way to do this than field paths. + paths: Vec>, +} + +enum RustAdt { + Struct(IndexMap), + Enum(IndexMap)>), } -trait RuleWithNamedFieldsMethods { - fn find_variant_fields(&self, cx: &Context) -> Option>>; +trait RuleWithFieldsMethods { + fn rust_fields(self, cx: &Context) -> IndexMap; + fn rust_adt(self, cx: &Context) -> RustAdt; } -impl RuleWithNamedFieldsMethods for RuleWithNamedFields { - fn find_variant_fields(&self, cx: &Context) -> Option>> { - if let Rule::Or(cases) = &cx[self.rule] { - if self.fields.is_empty() { - return None; +impl RuleWithFieldsMethods for RuleWithFields { + fn rust_fields(self, cx: &Context) -> IndexMap { + let children = match &cx[self.fields] { + Fields::Leaf(None) => return indexmap! {}, + Fields::Leaf(Some(field)) => { + // FIXME(eddyb) support this properly (see issue #128). + assert_eq!(cx[field.sub], Fields::Leaf(None)); + + return indexmap! { field.name => self.rule.leaf_rust_field(cx) }; } + Fields::Aggregate(children) => children, + }; + let child_fields = |rule, i| { + let child = RuleWithFields { + rule, + fields: children + .get(i) + .cloned() + .unwrap_or_else(|| cx.intern(Fields::Leaf(None))), + }; + let mut fields = child.rust_fields(cx); + for field in fields.values_mut() { + for path in &mut field.paths { + path.insert(0, i); + } + } + fields + }; - let mut variants = vec![None; cases.len()]; - for (&name, field) in &self.fields { - for (path, subfields) in &field.paths { - if let [variant] = path[..] { - let old = variants[variant].replace((name, subfields)); - if old.is_some() { - return None; + match cx[self.rule] { + Rule::Empty + | Rule::Eat(_) + | Rule::Call(_) + | Rule::RepeatMany(..) + | Rule::RepeatMore(..) => unreachable!(), + Rule::Concat([left, right]) => { + let mut fields = child_fields(left, 0); + for (name, field) in child_fields(right, 1) { + assert!(!fields.contains_key(&name), "duplicate field {}", &cx[name]); + fields.insert(name, field); + } + fields + } + Rule::Or(ref cases) => { + let child_fields = |i| { + let mut fields = child_fields(cases[i], i); + for field in fields.values_mut() { + field.refutable = true; + } + fields + }; + let mut fields = child_fields(0); + for i in 1..cases.len() { + for (name, field) in child_fields(i) { + use indexmap::map::Entry; + + match fields.entry(name) { + Entry::Occupied(entry) => { + let entry = entry.into_mut(); + + // HACK(eddyb) find a way to compare `Src` w/o + // printing (`to_ugly_string`). + if field.ty.to_ugly_string() != entry.ty.to_ugly_string() { + entry.ty = quote!(()); + } + + entry.paths.extend(field.paths); + } + Entry::Vacant(entry) => { + entry.insert(field); + } } - } else { - return None; } } + fields } - cases - .iter() - .cloned() - .zip(variants) - .map(|(rule, name_and_fields)| { - let (name, fields) = name_and_fields?; - Some(Variant { rule, name, fields }) - }) - .collect() - } else { - None + Rule::Opt(rule) => { + let mut fields = child_fields(rule, 0); + for field in fields.values_mut() { + field.refutable = true; + } + fields + } + } + } + + fn rust_adt(self, cx: &Context) -> RustAdt { + match (&cx[self.rule], &cx[self.fields]) { + (Rule::Or(cases), Fields::Aggregate(children)) => { + let variants: Option> = cases + .iter() + .enumerate() + .map(|(i, &rule)| match cx[children[i]] { + Fields::Leaf(Some(field)) => { + let child = RuleWithFields { + rule, + fields: field.sub, + }; + Some((field.name, (rule, child.rust_fields(cx)))) + } + _ => None, + }) + .collect(); + + if let Some(variants) = variants { + // Make sure no name collision happened between variants. + if variants.len() == cases.len() { + return RustAdt::Enum(variants); + } + } + } + _ => {} } + + RustAdt::Struct(self.rust_fields(cx)) } } trait RuleMethods: Sized { - fn field_type(self, cx: &Context, field: &Field) -> Src; - fn field_path_type(self, cx: &Context, path: &[usize]) -> Src; + fn leaf_rust_field(self, cx: &Context) -> RustField; fn node_kind(self, cx: &Context, rules: &mut RuleMap<'_>) -> NodeKind; } impl RuleMethods for IRule { - fn field_type(self, cx: &Context, field: &Field) -> Src { - let (first_path, first_subfields) = field.paths.get_index(0).unwrap(); - // FIXME(eddyb) support this properly. - assert!(first_subfields.is_empty()); - let ty = self.field_path_type(cx, first_path); - if field.paths.len() > 1 { - // HACK(eddyb) find a way to compare `Src` w/o printing (`to_ugly_string`). - let ty_string = ty.to_ugly_string(); - for (path, subfields) in field.paths.iter().skip(1) { - // FIXME(eddyb) support this properly. - assert!(subfields.is_empty()); - if self.field_path_type(cx, path).to_ugly_string() != ty_string { - return quote!(()); - } - } - } - ty - } + fn leaf_rust_field(self, cx: &Context) -> RustField { + let ty = match cx[self] { + Rule::Empty | Rule::Eat(_) | Rule::Concat(_) | Rule::Or(_) => quote!(()), - fn field_path_type(self, cx: &Context, path: &[usize]) -> Src { - match cx[self] { - Rule::Empty | Rule::Eat(_) => { - assert_eq!(path, []); - quote!(()) - } Rule::Call(r) => { let ident = Src::ident(&cx[r]); quote!(#ident<'a, 'i, I>) } - Rule::Concat(rules) => { - if path.is_empty() { - return quote!(()); + + Rule::Opt(rule) => { + let mut field = rule.leaf_rust_field(cx); + for path in &mut field.paths { + path.insert(0, 0); } - rules[path[0]].field_path_type(cx, &path[1..]) + field.refutable = true; + return field; } - Rule::Or(ref cases) => cases[path[0]].field_path_type(cx, &path[1..]), - Rule::Opt(rule) => [rule][path[0]].field_path_type(cx, &path[1..]), + Rule::RepeatMany(elem, _) | Rule::RepeatMore(elem, _) => { - assert_eq!(path, []); - let elem = elem.field_path_type(cx, &[]); + let elem = elem.leaf_rust_field(cx).ty; quote!([#elem]) } + }; + RustField { + ty, + refutable: false, + paths: vec![vec![]], } } @@ -289,7 +368,7 @@ impl GrammarGenerateMethods for grammer:: .parse::() .unwrap(); - for (&name, rule) in rules.named { + for (&name, &rule) in rules.named { out += declare_rule(name, rule, cx, &mut rules) + impl_parse_with(cx, name); } @@ -866,7 +945,7 @@ where fn declare_rule( name: IStr, - rule: &RuleWithNamedFields, + rule: RuleWithFields, cx: &Context, rules: &mut RuleMap<'_>, ) -> Src @@ -874,71 +953,68 @@ where Pat: RustInputPat, { let ident = Src::ident(&cx[name]); - let variants = rule.find_variant_fields(cx); - let variants: Option<&[Variant<'_>]> = variants.as_ref().map(|x| &**x); + let rust_adt = rule.rust_adt(cx); - let field_handle_ty = |rule: IRule, field| { - let ty = rule.field_type(cx, field); + let field_handle_ty = |field: &RustField| { + let ty = &field.ty; let handle_ty = quote!(Handle<'a, 'i, I, #ty>); - if rule.field_is_refutable(cx, field) { + if field.refutable { quote!(Option<#handle_ty>) } else { handle_ty } }; - let rule_ty_def = if let Some(variants) = variants { - let variants = variants.iter().map(|v| { - let variant_ident = Src::ident(&cx[v.name]); - if v.fields.is_empty() { - let field_ty = v.rule.field_path_type(cx, &[]); - quote!(#variant_ident(Handle<'a, 'i, I, #field_ty>)) + let rule_ty_def = match &rust_adt { + RustAdt::Enum(variants) => { + let variants = variants.iter().map(|(&v_name, (v_rule, v_fields))| { + let variant_ident = Src::ident(&cx[v_name]); + if v_fields.is_empty() { + // FIXME(eddyb) this should be reflected in `RustAdt` + let field_ty = field_handle_ty(&v_rule.leaf_rust_field(cx)); + quote!(#variant_ident(#field_ty)) + } else { + let fields_ident = v_fields.keys().map(|&name| Src::ident(&cx[name])); + let fields_ty = v_fields.values().map(field_handle_ty); + quote!(#variant_ident { + #(#fields_ident: #fields_ty),* + }) + } + }); + quote!( + #[allow(non_camel_case_types)] + pub enum #ident<'a, 'i, I: gll::grammer::input::Input> { + #(#variants),* + } + ) + } + RustAdt::Struct(fields) => { + let fields_ident = fields.keys().map(|&name| Src::ident(&cx[name])); + let fields_ty = fields.values().map(field_handle_ty); + let marker_field = if fields.is_empty() { + Some(quote!(_marker: PhantomData<(&'a (), &'i (), I)>,)) } else { - let fields_ident = v.fields.keys().map(|&name| Src::ident(&cx[name])); - let fields_ty = v - .fields - .values() - .map(|field| field_handle_ty(v.rule, field)); - quote!(#variant_ident { - #(#fields_ident: #fields_ty),* - }) - } - }); - quote!( - #[allow(non_camel_case_types)] - pub enum #ident<'a, 'i, I: gll::grammer::input::Input> { - #(#variants),* - } - ) - } else { - let fields_ident = rule.fields.keys().map(|&name| Src::ident(&cx[name])); - let fields_ty = rule - .fields - .values() - .map(|field| field_handle_ty(rule.rule, field)); - let marker_field = if rule.fields.is_empty() { - Some(quote!(_marker: PhantomData<(&'a (), &'i (), I)>,)) - } else { - None - }; - quote!( - #[allow(non_camel_case_types)] - pub struct #ident<'a, 'i, I: gll::grammer::input::Input> { - #(pub #fields_ident: #fields_ty),* - #marker_field - } - ) + None + }; + quote!( + #[allow(non_camel_case_types)] + pub struct #ident<'a, 'i, I: gll::grammer::input::Input> { + #(pub #fields_ident: #fields_ty),* + #marker_field + } + ) + } }; rule_ty_def - + rule_debug_impls(cx, name, &rule, variants) - + impl_rule_from_forest(name, &rule, variants, cx, rules) - + impl_rule_one_and_all(name, &rule, variants, cx, rules) + + rule_debug_impls(cx, name, &rust_adt) + + impl_rule_from_forest(name, rule.rule, &rust_adt, cx, rules) + + impl_rule_one_and_all(name, rule.rule, &rust_adt, cx, rules) } fn impl_rule_from_forest( name: IStr, - rule: &RuleWithNamedFields, - variants: Option<&[Variant<'_>]>, + rule: IRule, + rust_adt: &RustAdt, cx: &Context, rules: &mut RuleMap<'_>, ) -> Src @@ -946,8 +1022,8 @@ where Pat: RustInputPat, { let ident = Src::ident(&cx[name]); - let field_handle_expr = |rule: IRule, field: &Field| { - let paths_expr = field.paths.keys().map(|path| { + let field_handle_expr = |field: &RustField| { + let paths_expr = field.paths.iter().map(|path| { // HACK(eddyb) workaround `quote!(#i)` producing `0usize`. let path = path .iter() @@ -955,7 +1031,7 @@ where .map(::proc_macro2::Literal::usize_unsuffixed); quote!(_r #(.#path)*) }); - if rule.field_is_refutable(cx, field) { + if field.refutable { quote!(None #(.or(#paths_expr))* .map(|node| Handle { node, forest, @@ -971,36 +1047,34 @@ where } }; - let methods = if let Some(variants) = variants { - // HACK(eddyb) only collected to a `Vec` to avoid `rules` borrow conflicts. - let variants_shape = variants - .iter() - .map(|v| v.rule.generate_traverse_shape(false, cx, rules)) - .collect::>(); - let variants_from_forest_ident = variants - .iter() - .map(|v| Src::ident(format!("{}_from_forest", &cx[v.name]))); - let variants_body = variants.iter().map(|v| { - let variant_ident = Src::ident(&cx[v.name]); - if v.fields.is_empty() { - quote!(#ident::#variant_ident(Handle { - node: _node, - forest, - _marker: PhantomData, - })) - } else { - let fields_ident = v.fields.keys().map(|&name| Src::ident(&cx[name])); - let fields_expr = v - .fields - .values() - .map(|field| field_handle_expr(v.rule, field)); - quote!(#ident::#variant_ident { - #(#fields_ident: #fields_expr),* - }) - } - }); + let methods = match rust_adt { + RustAdt::Enum(variants) => { + // HACK(eddyb) only collected to a `Vec` to avoid `rules` borrow conflicts. + let variants_shape = variants + .values() + .map(|(v_rule, _)| v_rule.generate_traverse_shape(false, cx, rules)) + .collect::>(); + let variants_from_forest_ident = variants + .keys() + .map(|&v_name| Src::ident(format!("{}_from_forest", &cx[v_name]))); + let variants_body = variants.iter().map(|(&v_name, (_, v_fields))| { + let variant_ident = Src::ident(&cx[v_name]); + if v_fields.is_empty() { + quote!(#ident::#variant_ident(Handle { + node: _node, + forest, + _marker: PhantomData, + })) + } else { + let fields_ident = v_fields.keys().map(|&name| Src::ident(&cx[name])); + let fields_expr = v_fields.values().map(field_handle_expr); + quote!(#ident::#variant_ident { + #(#fields_ident: #fields_expr),* + }) + } + }); - quote!(#( + quote!(#( #[allow(non_snake_case)] fn #variants_from_forest_ident( forest: &'a gll::grammer::forest::ParseForest<'i, _G, I>, @@ -1010,30 +1084,29 @@ where #variants_body } )*) - } else { - let shape = rule.rule.generate_traverse_shape(false, cx, rules); - let fields_ident = rule.fields.keys().map(|&name| Src::ident(&cx[name])); - let fields_expr = rule - .fields - .values() - .map(|field| field_handle_expr(rule.rule, field)); - let marker_field = if rule.fields.is_empty() { - Some(quote!(_marker: { let _ = forest; PhantomData },)) - } else { - None - }; - quote!( - fn from_forest( - forest: &'a gll::grammer::forest::ParseForest<'i, _G, I>, - _node: Node<'i, _P>, - _r: traverse!(typeof(Node<'i, _P>) #shape), - ) -> Self { - #ident { - #(#fields_ident: #fields_expr),* - #marker_field + } + RustAdt::Struct(fields) => { + let shape = rule.generate_traverse_shape(false, cx, rules); + let fields_ident = fields.keys().map(|&name| Src::ident(&cx[name])); + let fields_expr = fields.values().map(field_handle_expr); + let marker_field = if fields.is_empty() { + Some(quote!(_marker: { let _ = forest; PhantomData },)) + } else { + None + }; + quote!( + fn from_forest( + forest: &'a gll::grammer::forest::ParseForest<'i, _G, I>, + _node: Node<'i, _P>, + _r: traverse!(typeof(Node<'i, _P>) #shape), + ) -> Self { + #ident { + #(#fields_ident: #fields_expr),* + #marker_field + } } - } - ) + ) + } }; quote!(impl<'a, 'i, I: gll::grammer::input::Input> #ident<'a, 'i, I> { @@ -1043,8 +1116,8 @@ where fn impl_rule_one_and_all( name: IStr, - rule: &RuleWithNamedFields, - variants: Option<&[Variant<'_>]>, + rule: IRule, + rust_adt: &RustAdt, cx: &Context, rules: &mut RuleMap<'_>, ) -> Src @@ -1052,80 +1125,83 @@ where Pat: RustInputPat, { let ident = Src::ident(&cx[name]); - let (one, all) = if let Some(variants) = variants { - // FIXME(eddyb) figure out a more efficient way to reuse - // iterators with `quote!(...)` than `.collect::>()`. - let i_ident = (0..variants.len()) - .map(|i| Src::ident(format!("_{}", i))) - .collect::>(); - let variants_from_forest_ident = variants - .iter() - .map(|v| Src::ident(format!("{}_from_forest", &cx[v.name]))) - .collect::>(); - let variants_kind = variants - .iter() - .map(|v| v.rule.node_kind(cx, rules)) - .collect::>(); - let variants_kind_src = variants_kind - .iter() - .map(|kind| kind.to_src()) - .collect::>(); - let variants_shape = variants - .iter() - .map(|v| v.rule.generate_traverse_shape(false, cx, rules)) - .collect::>(); - - ( - quote!( - let node = forest.one_choice(node)?; - match node.kind { - #(#variants_kind_src => { - let r = traverse!(one(forest, node) #variants_shape); - #ident::#variants_from_forest_ident(self.forest, node, r) - })* - _ => unreachable!() - } - ), - quote!( - #[derive(Clone)] - enum Iter<#(#i_ident),*> { - #(#i_ident(#i_ident)),* - } - impl)*> Iterator for Iter<#(#i_ident),*> - { - type Item = T; - fn next(&mut self) -> Option { - match self { - #(Iter::#i_ident(iter) => iter.next()),* + let (one, all) = match rust_adt { + RustAdt::Enum(variants) => { + // FIXME(eddyb) figure out a more efficient way to reuse + // iterators with `quote!(...)` than `.collect::>()`. + let i_ident = (0..variants.len()) + .map(|i| Src::ident(format!("_{}", i))) + .collect::>(); + let variants_from_forest_ident = variants + .keys() + .map(|&v_name| Src::ident(format!("{}_from_forest", &cx[v_name]))) + .collect::>(); + let variants_kind = variants + .values() + .map(|(v_rule, _)| v_rule.node_kind(cx, rules)) + .collect::>(); + let variants_kind_src = variants_kind + .iter() + .map(|kind| kind.to_src()) + .collect::>(); + let variants_shape = variants + .values() + .map(|(v_rule, _)| v_rule.generate_traverse_shape(false, cx, rules)) + .collect::>(); + + ( + quote!( + let node = forest.one_choice(node)?; + match node.kind { + #(#variants_kind_src => { + let r = traverse!(one(forest, node) #variants_shape); + #ident::#variants_from_forest_ident(self.forest, node, r) + })* + _ => unreachable!() + } + ), + quote!( + #[derive(Clone)] + enum Iter<#(#i_ident),*> { + #(#i_ident(#i_ident)),* + } + impl)*> Iterator for Iter<#(#i_ident),*> + { + type Item = T; + fn next(&mut self) -> Option { + match self { + #(Iter::#i_ident(iter) => iter.next()),* + } } } - } - forest.all_choices(node).flat_map(move |node| { - match node.kind { - #(#variants_kind_src => Iter::#i_ident( - traverse!(all(forest) #variants_shape) - .apply(node) - .map(move |r| #ident::#variants_from_forest_ident(self.forest, node, r)) - ),)* - _ => unreachable!(), - } - }) - ), - ) - } else { - let shape = rule.rule.generate_traverse_shape(false, cx, rules); - ( - quote!( - let r = traverse!(one(forest, node) #shape); - #ident::from_forest(self.forest, node, r) - ), - quote!( - traverse!(all(forest) #shape) - .apply(node) - .map(move |r| #ident::from_forest(self.forest, node, r)) - ), - ) + forest.all_choices(node).flat_map(move |node| { + match node.kind { + #(#variants_kind_src => Iter::#i_ident( + traverse!(all(forest) #variants_shape) + .apply(node) + .map(move |r| #ident::#variants_from_forest_ident(self.forest, node, r)) + ),)* + _ => unreachable!(), + } + }) + ), + ) + } + RustAdt::Struct(_) => { + let shape = rule.generate_traverse_shape(false, cx, rules); + ( + quote!( + let r = traverse!(one(forest, node) #shape); + #ident::from_forest(self.forest, node, r) + ), + quote!( + traverse!(all(forest) #shape) + .apply(node) + .map(move |r| #ident::from_forest(self.forest, node, r)) + ), + ) + } }; quote!(impl<'a, 'i, I> Handle<'a, 'i, I, #ident<'a, 'i, I>> @@ -1148,85 +1224,76 @@ where }) } -fn rule_debug_impls( - cx: &Context, - name: IStr, - rule: &RuleWithNamedFields, - variants: Option<&[Variant<'_>]>, -) -> Src { - rule_debug_impl(cx, name, rule, variants) - + rule_handle_debug_impl(cx, name, !rule.fields.is_empty()) +fn rule_debug_impls(cx: &Context, name: IStr, rust_adt: &RustAdt) -> Src { + rule_debug_impl(cx, name, rust_adt) + rule_handle_debug_impl(cx, name, rust_adt) } -fn rule_debug_impl( - cx: &Context, - name: IStr, - rule: &RuleWithNamedFields, - variants: Option<&[Variant<'_>]>, -) -> Src { +fn rule_debug_impl(cx: &Context, name: IStr, rust_adt: &RustAdt) -> Src { let name = &cx[name]; let ident = Src::ident(name); - let body = if let Some(variants) = variants { - let variants_pat = variants.iter().map(|v| { - let variant_ident = Src::ident(&cx[v.name]); - if v.fields.is_empty() { - quote!(#ident::#variant_ident(x)) - } else { - let fields_ident = v.fields.keys().map(|&name| Src::ident(&cx[name])); - let fields_var_ident = v - .fields - .keys() - .map(|&field_name| Src::ident(format!("f_{}", &cx[field_name]))); - quote!(#ident::#variant_ident { - #(#fields_ident: #fields_var_ident,)* - }) - } - }); - let variants_body = variants.iter().map(|v| { - let variant_path_str = format!("{}::{}", name, &cx[v.name]); - if v.fields.is_empty() { - quote!(f.debug_tuple(#variant_path_str).field(x).finish(),) - } else { - let fields_debug = v.fields.iter().map(|(field_name, field)| { - let field_name = &cx[*field_name]; - let field_var_ident = Src::ident(format!("f_{}", field_name)); - if v.rule.field_is_refutable(cx, field) { - quote!(if let Some(field) = #field_var_ident { - d.field(#field_name, field); - }) - } else { - quote!(d.field(#field_name, #field_var_ident);) - } - }); + let body = match rust_adt { + RustAdt::Enum(variants) => { + let variants_pat = variants.iter().map(|(&v_name, (_, v_fields))| { + let variant_ident = Src::ident(&cx[v_name]); + if v_fields.is_empty() { + quote!(#ident::#variant_ident(x)) + } else { + let fields_ident = v_fields.keys().map(|&name| Src::ident(&cx[name])); + let fields_var_ident = v_fields + .keys() + .map(|&field_name| Src::ident(format!("f_{}", &cx[field_name]))); + quote!(#ident::#variant_ident { + #(#fields_ident: #fields_var_ident,)* + }) + } + }); + let variants_body = variants.iter().map(|(&v_name, (_, v_fields))| { + let variant_path_str = format!("{}::{}", name, &cx[v_name]); + if v_fields.is_empty() { + quote!(f.debug_tuple(#variant_path_str).field(x).finish(),) + } else { + let fields_debug = v_fields.iter().map(|(field_name, field)| { + let field_name = &cx[*field_name]; + let field_var_ident = Src::ident(format!("f_{}", field_name)); + if field.refutable { + quote!(if let Some(field) = #field_var_ident { + d.field(#field_name, field); + }) + } else { + quote!(d.field(#field_name, #field_var_ident);) + } + }); - quote!({ - let mut d = f.debug_struct(#variant_path_str); - #(#fields_debug)* - d.finish() - }) - } - }); + quote!({ + let mut d = f.debug_struct(#variant_path_str); + #(#fields_debug)* + d.finish() + }) + } + }); - quote!(match self { - #(#variants_pat => #variants_body)* - }) - } else { - let fields_debug = rule.fields.iter().map(|(field_name, field)| { - let field_name = &cx[*field_name]; - let field_ident = Src::ident(field_name); - if rule.rule.field_is_refutable(cx, field) { - quote!(if let Some(ref field) = self.#field_ident { - d.field(#field_name, field); - }) - } else { - quote!(d.field(#field_name, &self.#field_ident);) - } - }); - quote!( - let mut d = f.debug_struct(#name); - #(#fields_debug)* - d.finish() - ) + quote!(match self { + #(#variants_pat => #variants_body)* + }) + } + RustAdt::Struct(fields) => { + let fields_debug = fields.iter().map(|(&field_name, field)| { + let field_name = &cx[field_name]; + let field_ident = Src::ident(field_name); + if field.refutable { + quote!(if let Some(ref field) = self.#field_ident { + d.field(#field_name, field); + }) + } else { + quote!(d.field(#field_name, &self.#field_ident);) + } + }); + quote!( + let mut d = f.debug_struct(#name); + #(#fields_debug)* + d.finish() + ) + } }; quote!(impl fmt::Debug for #ident<'_, '_, I> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1235,9 +1302,13 @@ fn rule_debug_impl( }) } -fn rule_handle_debug_impl(cx: &Context, name: IStr, has_fields: bool) -> Src { +fn rule_handle_debug_impl(cx: &Context, name: IStr, rust_adt: &RustAdt) -> Src { let ident = Src::ident(&cx[name]); - let body = if !has_fields { + let is_opaque = match rust_adt { + RustAdt::Struct(fields) => fields.is_empty(), + _ => false, + }; + let body = if is_opaque { quote!() } else { quote!( @@ -1280,7 +1351,7 @@ where let mut code_label_arms = vec![]; for (&name, rule) in rules.named { let code_label = Rc::new(CodeLabel::NamedRule(cx[name].to_string())); - let rules = if rule.fields.is_empty() { + let rules = if cx[rule.fields] == Fields::Leaf(None) { None } else { Some(&mut *rules) diff --git a/src/parse_grammar.rs b/src/parse_grammar.rs index 6e732eb..e46421f 100644 --- a/src/parse_grammar.rs +++ b/src/parse_grammar.rs @@ -35,7 +35,7 @@ pub fn parse_grammar>( } impl Or<'_, '_, TokenStream> { - fn lower>(self, cx: &Context) -> rule::RuleWithNamedFields { + fn lower>(self, cx: &Context) -> rule::RuleWithFields { let mut rules = self.rules.map(|rule| rule.unwrap().one().unwrap()); let first = rules.next().unwrap().lower(cx); rules.fold(first, |a, b| (a | b.lower(cx)).finish(cx)) @@ -43,7 +43,7 @@ impl Or<'_, '_, TokenStream> { } impl Concat<'_, '_, TokenStream> { - fn lower>(self, cx: &Context) -> rule::RuleWithNamedFields { + fn lower>(self, cx: &Context) -> rule::RuleWithFields { self.rules .map(|rule| rule.unwrap().one().unwrap()) .fold(rule::empty().finish(cx), |a, b| { @@ -53,7 +53,7 @@ impl Concat<'_, '_, TokenStream> { } impl Rule<'_, '_, TokenStream> { - fn lower>(self, cx: &Context) -> rule::RuleWithNamedFields { + fn lower>(self, cx: &Context) -> rule::RuleWithFields { let mut rule = self.rule.one().unwrap().lower(cx); if let Some(modifier) = self.modifier { rule = modifier.one().unwrap().lower(cx, rule); @@ -70,7 +70,7 @@ impl Rule<'_, '_, TokenStream> { } impl Primary<'_, '_, TokenStream> { - fn lower>(self, cx: &Context) -> rule::RuleWithNamedFields { + fn lower>(self, cx: &Context) -> rule::RuleWithFields { match self { Primary::Eat(pat) => rule::eat(pat.one().unwrap().lower(cx)).finish(cx), Primary::Call(name) => { @@ -91,8 +91,8 @@ impl Modifier<'_, '_, TokenStream> { fn lower>( self, cx: &Context, - rule: rule::RuleWithNamedFields, - ) -> rule::RuleWithNamedFields { + rule: rule::RuleWithFields, + ) -> rule::RuleWithFields { match self { Modifier::Opt(_) => rule.opt().finish(cx), Modifier::Repeat { repeat, sep, kind } => {