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<Ident> {
+                        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<Ident>,
+}
+
+impl CustomReduceAction {
+    fn fetch_idents(set: &mut BTreeSet<Ident>, 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<Token>,
-        data: &mut i32,
-        __rustylr_location0: &mut ::rusty_lr::DefaultLocation,
-    ) -> Result<bool, String> {
-        #[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")
