Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 6 additions & 27 deletions rusty_lr_parser/src/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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]
Expand All @@ -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]
Expand All @@ -1354,7 +1333,7 @@ impl Grammar {

#extract_data_stream

#reduce_action
#body

Ok(#returns_non_empty)
}
Expand Down
47 changes: 46 additions & 1 deletion rusty_lr_parser/src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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> {

Copilot AI Sep 12, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] This nested function should be extracted as a module-level function or method to improve readability and testability of this complex logic.

Copilot uses AI. Check for mistakes.
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,

Expand Down
34 changes: 33 additions & 1 deletion rusty_lr_parser/src/nonterminal_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
}
Comment on lines +8 to +11

Copilot AI Sep 12, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The idents_used field should be public or provide a getter method since it's used for optimization decisions. Currently it's private but accessed through contains_ident method, which is good design.

Copilot uses AI. Check for mistakes.

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(_))
}
Expand Down
18 changes: 9 additions & 9 deletions rusty_lr_parser/src/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -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(),
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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,
Expand All @@ -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(),
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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(),
Expand Down
43 changes: 6 additions & 37 deletions scripts/diff/calculator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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")
Expand Down