diff --git a/rusty_lr_parser/src/emit.rs b/rusty_lr_parser/src/emit.rs index 4278f638..966c07b0 100644 --- a/rusty_lr_parser/src/emit.rs +++ b/rusty_lr_parser/src/emit.rs @@ -1150,33 +1150,13 @@ impl Grammar { } } } - fn tokenstream_contains_ident( - stream: TokenStream, - ident: &Ident, - ) -> bool { - for t in stream { - match t { - proc_macro2::TokenTree::Ident(i) if &i == ident => { - return true - } - proc_macro2::TokenTree::Group(g) => { - if tokenstream_contains_ident(g.stream(), ident) { - return true; - } - } - _ => {} - } - } - false - } if let Some(stack_name) = stack_name { // if variable was not used at this reduce action, // we can use `truncate` instead of `pop` for optimization // so check it here let mapto = if let Some(mapto) = &token.mapto { - if tokenstream_contains_ident(reduce_action.clone(), mapto) - { + if reduce_action.contains_ident(mapto) { Some(mapto.clone()) } else { None @@ -1196,10 +1176,7 @@ impl Grammar { // if variable was not used at this reduce action, // we can use `truncate` instead of `pop` for optimization // so check it here - if tokenstream_contains_ident( - reduce_action.clone(), - &location_varname, - ) { + if reduce_action.contains_ident(&location_varname) { Some(location_varname) } else { None @@ -1309,6 +1286,7 @@ impl Grammar { // typename is defined, reduce action must be defined if let Some(stack_name) = &stack_names_for_nonterm[nonterm_idx] { + let body = &reduce_action.body; fn_reduce_for_each_rule_stream.extend(quote! { #[doc = #rule_debug_str] #[inline] @@ -1328,13 +1306,14 @@ impl Grammar { #extract_data_stream - let __res = #reduce_action ; + let __res = #body; __data_stack.#stack_name.push(__res); Ok(#returns_non_empty) } }); } else { + let body = &reduce_action.body; fn_reduce_for_each_rule_stream.extend(quote! { #[doc = #rule_debug_str] #[inline] @@ -1354,7 +1333,7 @@ impl Grammar { #extract_data_stream - #reduce_action + #body Ok(#returns_non_empty) } diff --git a/rusty_lr_parser/src/grammar.rs b/rusty_lr_parser/src/grammar.rs index 89987e5d..b3a80b0a 100644 --- a/rusty_lr_parser/src/grammar.rs +++ b/rusty_lr_parser/src/grammar.rs @@ -16,6 +16,7 @@ use rusty_lr_core::Token; use crate::error::ArgError; use crate::error::ParseArgError; use crate::error::ParseError; +use crate::nonterminal_info::CustomReduceAction; use crate::nonterminal_info::NonTerminalInfo; use crate::nonterminal_info::ReduceAction; use crate::nonterminal_info::Rule; @@ -719,7 +720,51 @@ impl Grammar { } let new_reduce_action = rename_tokenstream_recursive(reduce_action); - Some(ReduceAction::Custom(new_reduce_action)) + + // check if this reduce action can be identity action; i.e., { $1 } + fn tokenstream_contains_unique_ident(ts: TokenStream) -> Option { + let mut it = ts.into_iter(); + match it.next() { + Some(proc_macro2::TokenTree::Ident(ident)) => { + if it.next().is_none() { + Some(ident) + } else { + None + } + } + Some(proc_macro2::TokenTree::Group(group)) => { + if group.delimiter() != proc_macro2::Delimiter::Brace { + return None; + } + if it.next().is_some() { + return None; + } + tokenstream_contains_unique_ident(group.stream()) + } + _ => None, + } + } + if let Some(unique_ident) = + tokenstream_contains_unique_ident(new_reduce_action.clone()) + { + if let Some(unique_idx) = tokens + .iter() + .enumerate() + .rev() + .find(move |(_, token)| token.mapto.as_ref() == Some(&unique_ident)) + .map(|(idx, _)| idx) + { + Some(ReduceAction::Identity(unique_idx)) + } else { + Some(ReduceAction::Custom(CustomReduceAction::new( + new_reduce_action, + ))) + } + } else { + Some(ReduceAction::Custom(CustomReduceAction::new( + new_reduce_action, + ))) + } } else { // reduce action is not defined, diff --git a/rusty_lr_parser/src/nonterminal_info.rs b/rusty_lr_parser/src/nonterminal_info.rs index a24936e6..29143126 100644 --- a/rusty_lr_parser/src/nonterminal_info.rs +++ b/rusty_lr_parser/src/nonterminal_info.rs @@ -5,14 +5,46 @@ use proc_macro2::Ident; use proc_macro2::Span; use proc_macro2::TokenStream; +pub struct CustomReduceAction { + pub body: TokenStream, + idents_used: BTreeSet, +} + +impl CustomReduceAction { + fn fetch_idents(set: &mut BTreeSet, ts: TokenStream) { + for token in ts { + match token { + proc_macro2::TokenTree::Group(g) => { + Self::fetch_idents(set, g.stream()); + } + proc_macro2::TokenTree::Ident(i) => { + set.insert(i); + } + _ => {} + } + } + } + pub fn new(body: TokenStream) -> Self { + let mut idents_used = BTreeSet::new(); + Self::fetch_idents(&mut idents_used, body.clone()); + Self { body, idents_used } + } + pub fn contains_ident(&self, ident: &Ident) -> bool { + self.idents_used.contains(ident) + } +} + pub enum ReduceAction { /// reduce action that is function-like TokenStream - Custom(TokenStream), + Custom(CustomReduceAction), /// reduce action that is auto-generated, and simply returns the i'th token itself Identity(usize), // index of the token in the rule } impl ReduceAction { + pub fn new_custom(body: TokenStream) -> Self { + ReduceAction::Custom(CustomReduceAction::new(body)) + } pub fn is_identity(&self) -> bool { matches!(self, ReduceAction::Identity(_)) } diff --git a/rusty_lr_parser/src/pattern.rs b/rusty_lr_parser/src/pattern.rs index c1d4b735..e0208a82 100644 --- a/rusty_lr_parser/src/pattern.rs +++ b/rusty_lr_parser/src/pattern.rs @@ -155,7 +155,7 @@ impl Pattern { begin_span: Span::call_site(), end_span: Span::call_site(), }], - reduce_action: Some(ReduceAction::Custom(quote! { + reduce_action: Some(ReduceAction::new_custom(quote! { { vec![A] } })), separator_span: Span::call_site(), @@ -179,7 +179,7 @@ impl Pattern { end_span: Span::call_site(), }, ], - reduce_action: Some(ReduceAction::Custom(quote! { + reduce_action: Some(ReduceAction::new_custom(quote! { { Ap.push(A); Ap } })), separator_span: Span::call_site(), @@ -309,7 +309,7 @@ impl Pattern { }; let line2 = Rule { tokens: vec![], - reduce_action: Some(ReduceAction::Custom(quote! { + reduce_action: Some(ReduceAction::new_custom(quote! { { vec![] } })), separator_span: Span::call_site(), @@ -409,7 +409,7 @@ impl Pattern { begin_span: Span::call_site(), end_span: Span::call_site(), }], - reduce_action: Some(ReduceAction::Custom(quote! { Some(A) })), + reduce_action: Some(ReduceAction::new_custom(quote! { Some(A) })), separator_span: Span::call_site(), lookaheads: None, prec: None, @@ -418,7 +418,7 @@ impl Pattern { }; let line2 = Rule { tokens: vec![], - reduce_action: Some(ReduceAction::Custom(quote! { + reduce_action: Some(ReduceAction::new_custom(quote! { { None } })), separator_span: Span::call_site(), @@ -734,7 +734,7 @@ impl Pattern { let initializer = quote! {(#initializer)}; let rule = Rule { tokens, - reduce_action: Some(ReduceAction::Custom(initializer)), + reduce_action: Some(ReduceAction::new_custom(initializer)), separator_span: Span::call_site(), lookaheads: None, prec: None, @@ -930,7 +930,7 @@ impl Pattern { begin_span: Span::call_site(), end_span: Span::call_site(), }], - reduce_action: Some(ReduceAction::Custom(quote! { + reduce_action: Some(ReduceAction::new_custom(quote! { { vec![__token0] } })), separator_span: Span::call_site(), @@ -960,7 +960,7 @@ impl Pattern { end_span: Span::call_site(), }, ], - reduce_action: Some(ReduceAction::Custom(quote! { + reduce_action: Some(ReduceAction::new_custom(quote! { { __token0.push(__token1); __token0 @@ -1097,7 +1097,7 @@ impl Pattern { }; let line2 = Rule { tokens: vec![], - reduce_action: Some(ReduceAction::Custom(quote! { + reduce_action: Some(ReduceAction::new_custom(quote! { { vec![] } })), separator_span: Span::call_site(), diff --git a/scripts/diff/calculator.rs b/scripts/diff/calculator.rs index c9d75d46..8c484aa7 100644 --- a/scripts/diff/calculator.rs +++ b/scripts/diff/calculator.rs @@ -256,38 +256,16 @@ impl EDataStack { Ok(true) } ///P -> lparen E rparen - #[inline] + #[inline(always)] fn reduce_P_1( __data_stack: &mut Self, __location_stack: &mut Vec<::rusty_lr::DefaultLocation>, - shift: &mut bool, - lookahead: &::rusty_lr::TerminalSymbol, - data: &mut i32, - __rustylr_location0: &mut ::rusty_lr::DefaultLocation, - ) -> Result { - #[cfg(debug_assertions)] - { - debug_assert!( - __data_stack.__tags.get(__data_stack.__tags.len() - 1 - 0usize) == Some(& - ETags::__terminals) - ); - debug_assert!( - __data_stack.__tags.get(__data_stack.__tags.len() - 1 - 1usize) == Some(& - ETags::__stack2) - ); - debug_assert!( - __data_stack.__tags.get(__data_stack.__tags.len() - 1 - 2usize) == Some(& - ETags::__terminals) - ); - } - __data_stack.__tags.truncate(__data_stack.__tags.len() - 3usize); - __data_stack.__tags.push(ETags::__stack2); - let mut E = __data_stack.__stack2.pop().unwrap(); + ) -> bool { __data_stack.__terminals.truncate(__data_stack.__terminals.len() - 2usize); __location_stack.truncate(__location_stack.len() - 3usize); - let __res = { E }; - __data_stack.__stack2.push(__res); - Ok(true) + __data_stack.__tags.truncate(__data_stack.__tags.len() - 3usize); + __data_stack.__tags.push(ETags::__stack2); + true } ///E -> A #[inline(always)] @@ -411,16 +389,7 @@ impl ::rusty_lr::parser::data_stack::DataStack for EDataStack { location0, ) } - 5usize => { - Self::reduce_P_1( - data_stack, - location_stack, - shift, - lookahead, - user_data, - location0, - ) - } + 5usize => Ok(Self::reduce_P_1(data_stack, location_stack)), 6usize => Ok(Self::reduce_E_0(data_stack, location_stack)), 7usize => { unreachable!("{rule_index}: this production rule was optimized out")