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
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ let mut context = ExprContext::new();

context.expected_token(); // Returns the expected symbols for the current state
context.can_feed(&token); // Checks if a terminal can be fed next
context.trace(); // Retrieves active `%trace` non-terminals
println!("{}", context.backtrace()); // Prints the stack trace of parser states
println!("{}", context); // Formats the state tree (requires 'tree' feature)
```
Expand Down
13 changes: 0 additions & 13 deletions SYNTAX.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ This document provides a comprehensive guide to the grammar definition syntax us
- [Reduce Actions](#reduceaction-optional)
- [Accessing Data in Reduce Actions](#accessing-token-data-in-reduceaction)
- [Exclamation Mark (`!`) Value Discard](#exclamation-mark-)
- [Tracing Non-Terminals (`%trace`)](#tracing-non-terminals)
- [Start Symbol (`%start`)](#start-symbol-must-defined)
- [Userdata Type (`%userdata`)](#userdata-type-optional)
- [Conflict Resolution](#resolving-conflicts)
Expand Down Expand Up @@ -351,18 +350,6 @@ Expr(i32) : '('! Expr ')'! ;

---

## Tracing Non-Terminals

```
%trace NonTerm1 NonTerm2 ... ;
```

Registers non-terminals for tracing. When registered, you can query the active parsing goals at runtime using `context.trace(&parser)`, which returns a `HashSet<NonTerminals>`.

Additionally, tracing prevents the optimization engine from merging or optimizing away these non-terminal states.

---

## Start Symbol (Must Defined)

```
Expand Down
106 changes: 0 additions & 106 deletions rusty_lr_core/src/parser/deterministic/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -754,112 +754,6 @@ impl<
)
}

/// Get set of `%trace` non-terminal symbols that current context is trying to parse.
///
/// The order of the returned set does not mean anything.
/// If the current context is attempting to recognize following grammar:
///
/// Chunk -> Statement -> IfStatement -> ReturnStatement -> ...
///
/// Then the returned set will be:
/// [`Chunk`, `Statement`, `IfStatement`, `ReturnStatement`]
/// Get set of `%trace` non-terminal symbols that current context is trying to parse.
///
/// The order of the returned set does not mean anything.
/// If the current context is attempting to recognize following grammar:
///
/// Chunk -> Statement -> IfStatement -> ReturnStatement -> ...
///
/// Then the returned set will be:
/// [`Chunk`, `Statement`, `IfStatement`, `ReturnStatement`]
pub fn trace(&self) -> crate::hash::HashSet<Data::NonTerm>
where
P::NonTerm: Hash + Eq,
{
use crate::hash::HashSet;
use crate::token::Token;

let rules = P::get_rules();
let states = P::get_states();

let mut zero_shifted_rules = BTreeSet::new();
let mut non_zero_shifted_rules = BTreeSet::new();
{
let last_state = &states[self.state_stack.last().unwrap().into_usize()];
for rule in last_state.get_rules().iter() {
if rule.shifted == 0 {
zero_shifted_rules.insert(rule.rule);
} else {
non_zero_shifted_rules.insert((rule.rule, rule.shifted));
}
}
}

let mut ret: HashSet<Data::NonTerm> = Default::default();

for &state_idx in self.state_stack.iter().rev() {
let state = &states[state_idx.into_usize()];
let ruleset = state.get_rules();

// insert new shifted rule that brings zero_shifted rules in this state
let mut new_zero_shifted_rules = Vec::new();
loop {
let zero_len0 = zero_shifted_rules.len();
let nonzero_len0 = non_zero_shifted_rules.len();

new_zero_shifted_rules.clear();

for &zero_rule in zero_shifted_rules.iter() {
let nonterm0 = rules[zero_rule].name;
for rule in ruleset.iter() {
let prod_rule = &rules[rule.rule];
if let Some(Token::NonTerm(nonterm)) = prod_rule.rule.get(rule.shifted) {
if &nonterm0 == nonterm {
if rule.shifted == 0 {
new_zero_shifted_rules.push(rule.rule);
} else {
// insert new shifted rule
non_zero_shifted_rules.insert((rule.rule, rule.shifted));
}
}
}
}
}
zero_shifted_rules.extend(new_zero_shifted_rules.iter().copied());

if zero_len0 == zero_shifted_rules.len()
&& nonzero_len0 == non_zero_shifted_rules.len()
{
break;
}
}

// push nonterminal of zero-shifted-rules into backtrace vector
for &zero_rule in zero_shifted_rules.iter() {
let nonterm0 = rules[zero_rule].name;
// do not insert auto-generated nonterminals
// since user don't need to know about them
if nonterm0.is_trace() {
ret.insert(nonterm0);
}
}

// shift to next state
zero_shifted_rules.clear();
let mut new_non_zero_shifted_rules = BTreeSet::new();
for (rule, shifted) in non_zero_shifted_rules.into_iter() {
if shifted == 1 {
zero_shifted_rules.insert(rule);
} else {
new_non_zero_shifted_rules.insert((rule, shifted - 1));
}
}
non_zero_shifted_rules = new_non_zero_shifted_rules;
}

ret
}

/// Get backtrace information for current state.
/// What current state is trying to parse, and where it comes from.
pub fn backtrace(&self) -> crate::Backtrace<P::TermClass, P::NonTerm>
Expand Down
110 changes: 0 additions & 110 deletions rusty_lr_core/src/parser/nondeterministic/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -562,116 +562,6 @@ impl<
})
}

fn trace_impl(&self, node: usize) -> crate::hash::HashSet<Data::NonTerm>
where
Data::NonTerm: Eq + Hash,
{
use crate::hash::HashSet;
use crate::token::Token;
use std::collections::BTreeSet;

let rules = P::get_rules();
let states = P::get_states();

let mut zero_shifted_rules = BTreeSet::new();
let mut non_zero_shifted_rules = BTreeSet::new();
{
let last_state = &states[self.state(node)];
for rule in last_state.get_rules().iter() {
if rule.shifted == 0 {
zero_shifted_rules.insert(rule.rule);
} else {
non_zero_shifted_rules.insert((rule.rule, rule.shifted));
}
}
}

let mut ret: HashSet<Data::NonTerm> = Default::default();

for state in self.state_iter(node).chain(std::iter::once(0)) {
let state = &states[state];
let ruleset = &state.get_rules();

// insert new shifted rule that brings zero_shifted rules in this state
let mut new_zero_shifted_rules = Vec::new();
loop {
let zero_len0 = zero_shifted_rules.len();
let nonzero_len0 = non_zero_shifted_rules.len();

new_zero_shifted_rules.clear();

for &zero_rule in zero_shifted_rules.iter() {
let nonterm0 = rules[zero_rule].name;
for rule in ruleset.iter() {
let prod_rule = &rules[rule.rule];
if let Some(Token::NonTerm(nonterm)) = prod_rule.rule.get(rule.shifted) {
if &nonterm0 == nonterm {
if rule.shifted == 0 {
new_zero_shifted_rules.push(rule.rule);
} else {
// insert new shifted rule
non_zero_shifted_rules.insert((rule.rule, rule.shifted));
}
}
}
}
}
zero_shifted_rules.extend(new_zero_shifted_rules.iter().copied());

if zero_len0 == zero_shifted_rules.len()
&& nonzero_len0 == non_zero_shifted_rules.len()
{
break;
}
}

// push nonterminal of zero-shifted-rules into backtrace vector
for &zero_rule in zero_shifted_rules.iter() {
let nonterm0 = rules[zero_rule].name;
// do not insert auto-generated nonterminals
// since user don't need to know about them
if nonterm0.is_trace() {
ret.insert(nonterm0);
}
}

// shift to next state
zero_shifted_rules.clear();
let mut new_non_zero_shifted_rules = BTreeSet::new();
for (rule, shifted) in non_zero_shifted_rules.into_iter() {
if shifted == 1 {
zero_shifted_rules.insert(rule);
} else {
new_non_zero_shifted_rules.insert((rule, shifted - 1));
}
}
non_zero_shifted_rules = new_non_zero_shifted_rules;
}

ret
}

/// Get set of `%trace` non-terminal symbols that current context is trying to parse.
///
/// The order of the returned set does not mean anything.
/// If the current context is attempting to recognize following grammar:
///
/// Chunk -> Statement -> IfStatement -> ReturnStatement -> ...
///
/// Then the returned set will be:
/// [`Chunk`, `Statement`, `IfStatement`, `ReturnStatement`]
pub fn trace(&self) -> crate::hash::HashSet<Data::NonTerm>
where
Data::NonTerm: Copy + Eq + std::hash::Hash + NonTerminal,
{
let mut ret: crate::hash::HashSet<Data::NonTerm> = Default::default();
for &node in self.current_nodes.iter() {
let set = self.trace_impl(node);
ret.extend(set.into_iter());
}
ret
}

fn backtrace_impl(&self, node: usize) -> crate::Backtrace<P::TermClass, P::NonTerm>
where
P::Term: Clone,
Expand Down
2 changes: 0 additions & 2 deletions rusty_lr_core/src/parser/nonterminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ pub trait NonTerminal: Copy {
fn is_augmented(&self) -> bool {
self.nonterm_type() == Some(NonTerminalType::Augmented)
}
/// whether this non-terminal is set as %trace
fn is_trace(&self) -> bool;

/// for internal use only;
/// If this non-terminal is auto-generated, gets the pattern where this non-terminal was generated from.
Expand Down
16 changes: 0 additions & 16 deletions rusty_lr_parser/src/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,6 @@ impl Grammar {

let mut comma_separated_variants = TokenStream::new();
let mut case_as_str = TokenStream::new();
let mut nonterm_trait_is_trace_case = TokenStream::new();
let mut nonterm_type_case = TokenStream::new();
for nonterm in self.nonterminals.iter() {
let name = utils::ident_from_located(
Expand All @@ -459,16 +458,6 @@ impl Grammar {
#enum_typename::#name => #display_str,
});

let is_trace = if nonterm.name.value().as_str() == utils::AUGMENTED_NAME {
false
} else {
// non-term is auto-generated if nonterm.regex_span.is_some()
nonterm.trace
};

nonterm_trait_is_trace_case.extend(quote! {
#enum_typename::#name => #is_trace,
});
if let Some(enum_name) = &nonterm.nonterm_type {
let enum_name = format!("{:?}", enum_name);
let enum_name = Ident::new(&enum_name, Span::call_site());
Expand Down Expand Up @@ -523,11 +512,6 @@ impl Grammar {
#case_as_str
}
}
fn is_trace(&self) -> bool {
match self {
#nonterm_trait_is_trace_case
}
}
fn nonterm_type(&self) -> Option<#module_prefix::parser::nonterminal::NonTerminalType> {
match self {
#nonterm_type_case
Expand Down
13 changes: 0 additions & 13 deletions rusty_lr_parser/src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1093,7 +1093,6 @@ impl Grammar {
ruletype_boxed: is_boxed,
rules: Vec::new(), // production rules will be added later
root_location: None,
trace: false,
protected: false,
nonterm_type: None,
};
Expand Down Expand Up @@ -1835,7 +1834,6 @@ impl Grammar {
ruletype_boxed: false,
root_location: None,
rules: vec![augmented_rule],
trace: false,
protected: true,
nonterm_type: Some(rusty_lr_core::parser::nonterminal::NonTerminalType::Augmented),
};
Expand All @@ -1849,17 +1847,6 @@ impl Grammar {
.insert(augmented_name.into_value(), augmented_idx);
}

// set `%trace`
for trace in grammar_args.traces.into_iter() {
if let Some(&nonterm_idx) = grammar.nonterminals_index.get(trace.value().as_str()) {
grammar.nonterminals[nonterm_idx].trace = true;
grammar.nonterminals[nonterm_idx].protected = true;
} else {
return Err(ParseError::NonTerminalNotDefined(trace.location()));
// no such rule
}
}

// initialize terminal classes with one-terminal-one-class
grammar.terminal_class_id.reserve(grammar.terminals.len());
grammar.terminal_classes.reserve(grammar.terminals.len());
Expand Down
3 changes: 1 addition & 2 deletions rusty_lr_parser/src/nonterminal_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,7 @@ pub struct NonTerminalInfo {
/// the location of that pattern is stored here for error reporting.
pub(crate) root_location: Option<Location>,

pub(crate) trace: bool,
/// protected from optimization removal; trace rules are always protected
/// protected from optimization removal
pub(crate) protected: bool,

/// if this non-terminal is auto-generated, the pattern that generated this rule.
Expand Down
2 changes: 0 additions & 2 deletions rusty_lr_parser/src/parser/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,6 @@ pub struct GrammarArgs {
pub glr: bool,
pub no_optim: bool,
pub dense: bool,
pub traces: Vec<Located<String>>,
pub filter: Vec<(Location, TokenStream)>,
pub location_typename: Vec<(Location, TokenStream)>,

Expand All @@ -688,7 +687,6 @@ impl Default for GrammarArgs {
glr: false,
no_optim: false,
dense: false,
traces: Vec::new(),
filter: Vec::new(),
location_typename: Vec::new(),
error_recovered: Vec::new(),
Expand Down
Loading