use proc_macro2::Ident;
use proc_macro2::Span;
use proc_macro2::TokenStream;
use quote::format_ident;
use quote::quote;
use crate::grammar::Grammar;
use crate::terminal_info::TerminalName;
use crate::utils;
fn list_to_case_stream(list: impl Iterator<Item = usize>) -> TokenStream {
let mut stream = TokenStream::new();
let mut prev = None;
for val in list {
if let Some((prev_start, prev_last)) = prev {
if prev_last + 1 == val {
prev = Some((prev_start, val));
} else {
if !stream.is_empty() {
stream.extend(quote! { | });
}
if prev_start == prev_last {
let prev_start = proc_macro2::Literal::usize_unsuffixed(prev_start);
stream.extend(quote! { #prev_start });
} else {
let prev_start = proc_macro2::Literal::usize_unsuffixed(prev_start);
let prev_last = proc_macro2::Literal::usize_unsuffixed(prev_last);
stream.extend(quote! { #prev_start..=#prev_last });
}
prev = Some((val, val));
}
} else {
prev = Some((val, val));
}
}
if let Some((prev_start, prev_last)) = prev {
if !stream.is_empty() {
stream.extend(quote! { | });
}
if prev_start == prev_last {
let prev_start = proc_macro2::Literal::usize_unsuffixed(prev_start);
stream.extend(quote! { #prev_start });
} else {
let prev_start = proc_macro2::Literal::usize_unsuffixed(prev_start);
let prev_last = proc_macro2::Literal::usize_unsuffixed(prev_last);
stream.extend(quote! { #prev_start..=#prev_last });
}
}
stream
}
impl Grammar {
fn emit_type_alises(&self, stream: &mut TokenStream) {
let module_prefix = &self.module_prefix;
let start_rule_name = &self.start_rule_name;
let rule_typename = format_ident!("{}Rule", start_rule_name);
let state_typename = format_ident!("{}State", start_rule_name);
let nonterm_typename = format_ident!("{}NonTerminals", start_rule_name);
let parse_error_typename = format_ident!("{}ParseError", start_rule_name);
let context_struct_name = format_ident!("{}Context", start_rule_name);
let data_stack_typename = format_ident!("{}DataStack", start_rule_name);
let termclass_typename = format_ident!("{}TerminalClasses", start_rule_name);
let location_typename = self
.location_typename
.as_ref()
.cloned()
.unwrap_or_else(|| quote! { #module_prefix::DefaultLocation });
let reduce_error_typename = &self.error_typename;
let state_structname = if self.emit_dense {
format_ident!("DenseState")
} else {
format_ident!("SparseState")
};
let token_typename = &self.token_typename;
let state_index_typename = if self.states.len() <= u8::MAX as usize {
quote! { u8 }
} else if self.states.len() <= u16::MAX as usize {
quote! { u16 }
} else if self.states.len() <= u32::MAX as usize {
quote! { u32 }
} else {
quote! { usize }
};
let rule_index_type = if self.builder.rules.len() <= u8::MAX as usize {
quote! { u8 }
} else if self.builder.rules.len() <= u16::MAX as usize {
quote! { u16 }
} else if self.builder.rules.len() <= u32::MAX as usize {
quote! { u32 }
} else {
quote! { usize }
};
if self.glr {
let max_reduce_rules = self
.states
.iter()
.flat_map(|s| s.reduce_map.iter().map(|(_, rules)| rules.len()))
.max()
.unwrap_or(1);
let rule_container_type = if max_reduce_rules == 1 {
rule_index_type.clone()
} else {
quote! { #module_prefix::parser::state::ArrayVec<#rule_index_type, #max_reduce_rules> }
};
stream.extend(
quote! {
#[allow(non_camel_case_types,dead_code)]
pub type #context_struct_name = #module_prefix::parser::nondeterministic::Context<#data_stack_typename, #state_index_typename, #max_reduce_rules>;
#[allow(non_camel_case_types,dead_code)]
pub type #rule_typename = #module_prefix::rule::ProductionRule<#termclass_typename, #nonterm_typename>;
#[allow(non_camel_case_types,dead_code)]
pub type #state_typename = #module_prefix::parser::state::#state_structname<#termclass_typename, #nonterm_typename, #rule_container_type, #state_index_typename>;
#[allow(non_camel_case_types,dead_code)]
pub type #parse_error_typename = #module_prefix::parser::nondeterministic::ParseError<#token_typename, #location_typename, #reduce_error_typename>;
}
);
} else {
stream.extend(
quote! {
#[allow(non_camel_case_types,dead_code)]
pub type #context_struct_name = #module_prefix::parser::deterministic::Context<#data_stack_typename, #state_index_typename>;
#[allow(non_camel_case_types,dead_code)]
pub type #rule_typename = #module_prefix::rule::ProductionRule<#termclass_typename, #nonterm_typename>;
#[allow(non_camel_case_types,dead_code)]
pub type #state_typename = #module_prefix::parser::state::#state_structname<#termclass_typename, #nonterm_typename, #rule_index_type, #state_index_typename>;
#[allow(non_camel_case_types,dead_code)]
pub type #parse_error_typename = #module_prefix::parser::deterministic::ParseError<#token_typename, #location_typename, #reduce_error_typename>;
}
);
}
}
fn emit_termclass_enum(&self, stream: &mut TokenStream) {
let start_rule_name = &self.start_rule_name;
let termclass_typename = format_ident!("{}TerminalClasses", start_rule_name);
let module_prefix = &self.module_prefix;
let error_name = format_ident!("{}", utils::ERROR_NAME);
let eof_name = format_ident!("{}", utils::EOF_NAME);
let token_typename = &self.token_typename;
let mut class_variants = Vec::with_capacity(self.terminal_classes.len());
let mut as_str_match_stream = TokenStream::new();
for (class_id, class_def) in self.terminal_classes.iter().enumerate() {
let (variant_name, pretty_name) = if self.is_u8 || self.is_char {
let name = format_ident!("TermClass{}", class_id);
let pretty_name =
self.class_pretty_name_list(rusty_lr_core::TerminalSymbol::Term(class_id), 4);
(name, pretty_name)
} else {
if class_def.terminals.len() == 1 {
let term_idx = class_def.terminals[0];
let term = self.terminals[term_idx].name.ident().unwrap();
(term.clone(), term.to_string())
} else {
let name = format_ident!("TermClass{}", class_id);
let pretty_name = self
.class_pretty_name_list(rusty_lr_core::TerminalSymbol::Term(class_id), 4);
(name, pretty_name)
}
};
as_str_match_stream.extend(quote! {
#termclass_typename::#variant_name => #pretty_name,
});
class_variants.push(variant_name);
}
let mut precedence_match_stream = TokenStream::new();
{
let mut level_classes = Vec::new();
level_classes.resize(
self.builder.precedence_types.len(),
std::collections::BTreeSet::new(),
);
for (&class, &level) in self.builder.precedence_levels.iter() {
level_classes[level].insert(class.into_term().unwrap());
}
for (level, classes) in level_classes.into_iter().enumerate() {
if classes.is_empty() {
continue;
} else {
debug_assert!(level < u8::MAX as usize);
let iter = classes.iter().map(|&c| {
let var = &class_variants[c];
quote! { #termclass_typename::#var }
});
let level = proc_macro2::Literal::usize_unsuffixed(level);
let case_stream = quote! { #(#iter)|* };
precedence_match_stream.extend(quote! {
#case_stream => #module_prefix::parser::Precedence::new(#level),
});
}
}
if let Some(error_prec) = self.error_precedence {
debug_assert!(error_prec < u8::MAX as usize);
let error_prec = proc_macro2::Literal::usize_unsuffixed(error_prec);
precedence_match_stream.extend(quote! {
#termclass_typename::#error_name => #module_prefix::parser::Precedence::new(#error_prec),
});
}
precedence_match_stream.extend(quote! {
#termclass_typename::#eof_name => {
unreachable!("eof token cannot be used in precedence levels")
},
_ => #module_prefix::parser::Precedence::none(),
});
}
let other_class_id = self.other_terminal_class_id;
let match_terminal_filter_expression = if let Some(filter) = &self.filter {
quote! { #filter(terminal) }
} else {
quote! {terminal}
};
let mut from_term_match_stream = TokenStream::new();
if self.is_char || self.is_u8 {
for (class_id, class_def) in self.terminal_classes.iter().enumerate() {
if class_id == self.other_terminal_class_id {
continue;
}
let mut match_case_stream = TokenStream::new();
for (i, &(s, e)) in class_def.ranges.iter().enumerate() {
let stream = if self.is_char {
let s = unsafe { char::from_u32_unchecked(s) };
let e = unsafe { char::from_u32_unchecked(e) };
if s == e {
quote! { #s }
} else {
quote! { #s..=#e }
}
} else if self.is_u8 {
let s = s as u8;
let e = e as u8;
if s == e {
quote! { #s }
} else {
quote! { #s..=#e }
}
} else {
unreachable!("unexpected char type")
};
if i > 0 {
match_case_stream.extend(quote! {|});
}
match_case_stream.extend(quote! { #stream });
}
let class_variant = &class_variants[class_id];
from_term_match_stream.extend(quote! {
#match_case_stream => #termclass_typename::#class_variant,
});
}
let other_variant = &class_variants[other_class_id];
from_term_match_stream.extend(quote! {
_ => #termclass_typename::#other_variant,
});
} else {
for (class_id, class_def) in self.terminal_classes.iter().enumerate() {
if class_id == self.other_terminal_class_id {
continue;
}
let mut match_case_stream = TokenStream::new();
for (i, &term) in class_def.terminals.iter().enumerate() {
let case_stream = match &self.terminals[term].name {
TerminalName::CharRange(s, l) => {
if self.is_char {
if s == l {
quote! {#s}
} else {
quote! {#s..=#l}
}
} else if self.is_u8 {
let s = *s as u8;
let l = *l as u8;
if s == l {
quote! {#s}
} else {
quote! {#s..=#l}
}
} else {
unreachable!("unexpected char type")
}
}
TerminalName::Ident(_) => {
let term_stream = &self.terminals[term].body;
quote! {#term_stream}
}
};
if i > 0 {
match_case_stream.extend(quote! { | });
}
match_case_stream.extend(case_stream);
}
let class_variant = &class_variants[class_id];
from_term_match_stream.extend(quote! {
#match_case_stream => #termclass_typename::#class_variant,
});
}
let other_class_variant = &class_variants[other_class_id];
from_term_match_stream.extend(quote! {
_ => #termclass_typename::#other_class_variant,
});
}
stream.extend(quote! {
#[allow(non_camel_case_types, dead_code)]
#[derive(Clone, Copy, std::hash::Hash, std::cmp::PartialEq, std::cmp::Eq, std::cmp::PartialOrd, std::cmp::Ord)]
pub enum #termclass_typename {
#(#class_variants),*,
#error_name,
#eof_name,
}
impl #module_prefix::parser::terminalclass::TerminalClass for #termclass_typename {
type Term = #token_typename;
const ERROR:Self = Self::#error_name;
const EOF:Self = Self::#eof_name;
fn as_str(&self) -> &'static str {
match self {
#as_str_match_stream
#termclass_typename::#error_name => "error",
#termclass_typename::#eof_name => "eof",
}
}
fn to_usize(&self) -> usize {
*self as usize
}
fn precedence(&self) -> #module_prefix::parser::Precedence {
match self {
#precedence_match_stream
}
}
fn from_term(terminal: &Self::Term) -> Self {
#[allow(unreachable_patterns)]
match #match_terminal_filter_expression {
#from_term_match_stream
}
}
}
impl std::fmt::Display for #termclass_typename {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use #module_prefix::parser::terminalclass::TerminalClass;
write!(f, "{}", self.as_str())
}
}
impl std::fmt::Debug for #termclass_typename {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use #module_prefix::parser::terminalclass::TerminalClass;
write!(f, "{}", self.as_str())
}
}
});
}
fn emit_nonterm_enum(&self, stream: &mut TokenStream) {
let start_rule_name = &self.start_rule_name;
let enum_typename = format_ident!("{}NonTerminals", start_rule_name);
let module_prefix = &self.module_prefix;
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 = &nonterm.name;
comma_separated_variants.extend(quote! {
#name,
});
let display_str = nonterm.pretty_name.as_str();
case_as_str.extend(quote! {
#enum_typename::#name => #display_str,
});
let is_trace = if name == utils::AUGMENTED_NAME {
false
} else {
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());
nonterm_type_case.extend(quote! {
#enum_typename::#name => Some(#module_prefix::parser::nonterminal::NonTerminalType::#enum_name),
});
} else {
nonterm_type_case.extend(quote! {
#enum_typename::#name => None,
});
}
}
stream.extend(
quote! {
#[allow(non_camel_case_types, dead_code)]
#[derive(Clone, Copy, std::hash::Hash, std::cmp::PartialEq, std::cmp::Eq, std::cmp::PartialOrd, std::cmp::Ord)]
pub enum #enum_typename {
#comma_separated_variants
}
impl std::fmt::Display for #enum_typename {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use #module_prefix::parser::nonterminal::NonTerminal;
write!(f, "{}", self.as_str())
}
}
impl std::fmt::Debug for #enum_typename {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use #module_prefix::parser::nonterminal::NonTerminal;
write!(f, "{}", self.as_str())
}
}
impl #module_prefix::parser::nonterminal::NonTerminal for #enum_typename{
fn as_str(&self) -> &'static str {
match self {
#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
}
}
fn to_usize(&self) -> usize {
*self as usize
}
}
}
);
}
fn emit_parser(&self, stream: &mut TokenStream) {
let module_prefix = &self.module_prefix;
let nonterminals_enum_name = format_ident!("{}NonTerminals", &self.start_rule_name);
let rule_typename = format_ident!("{}Rule", self.start_rule_name);
let state_typename = format_ident!("{}State", self.start_rule_name);
let parser_struct_name = format_ident!("{}Parser", self.start_rule_name);
let token_typename = &self.token_typename;
let termclass_typename = format_ident!("{}TerminalClasses", &self.start_rule_name);
let mut class_variants = Vec::with_capacity(self.terminal_classes.len());
for (class_id, class_def) in self.terminal_classes.iter().enumerate() {
let variant_name = if self.is_u8 || self.is_char {
let name = format_ident!("TermClass{}", class_id);
name
} else {
if class_def.terminals.len() == 1 {
let term_idx = class_def.terminals[0];
let term = self.terminals[term_idx].name.ident().unwrap();
term.clone()
} else {
let name = format_ident!("TermClass{}", class_id);
name
}
};
class_variants.push(variant_name);
}
use rusty_lr_core::rule::ReduceType;
use rusty_lr_core::TerminalSymbol;
use rusty_lr_core::Token;
let precedence_to_stream = |op: rusty_lr_core::rule::Precedence| -> TokenStream {
match op {
rusty_lr_core::rule::Precedence::Fixed(level) => {
quote! { #module_prefix::rule::Precedence::Fixed(#level) }
}
rusty_lr_core::rule::Precedence::Dynamic(idx) => {
quote! { #module_prefix::rule::Precedence::Dynamic(#idx) }
}
}
};
let nonterminals_token: Vec<_> = self
.nonterminals
.iter()
.map(|nonterm| {
let name = &nonterm.name;
quote! {
#nonterminals_enum_name::#name
}
})
.collect();
let token_to_stream = |token: Token<TerminalSymbol<usize>, usize>| -> TokenStream {
match token {
Token::Term(term) => {
let term = match term {
TerminalSymbol::Term(term) => {
let var = &class_variants[term];
quote! { #termclass_typename::#var }
}
TerminalSymbol::Error => {
let error_name = format_ident!("{}", utils::ERROR_NAME);
quote! { #termclass_typename::#error_name }
}
TerminalSymbol::Eof => {
let eof_name = format_ident!("{}", utils::EOF_NAME);
quote! { #termclass_typename::#eof_name }
}
};
quote! { #module_prefix::Token::Term(#term) }
}
Token::NonTerm(nonterm) => {
let nonterm = &nonterminals_token[nonterm];
quote! { #module_prefix::Token::NonTerm(#nonterm) }
}
}
};
let precedence_types_match_body_stream = {
let lefts = list_to_case_stream(
self.builder
.precedence_types
.iter()
.copied()
.enumerate()
.filter_map(|(level, reduce_type)| {
debug_assert!(level < u8::MAX as usize);
if reduce_type == Some(ReduceType::Left) {
Some(level)
} else {
None
}
}),
);
let rights = list_to_case_stream(
self.builder
.precedence_types
.iter()
.copied()
.enumerate()
.filter_map(|(level, reduce_type)| {
debug_assert!(level < u8::MAX as usize);
if reduce_type == Some(ReduceType::Right) {
Some(level)
} else {
None
}
}),
);
let mut stream = TokenStream::new();
if !lefts.is_empty() {
stream.extend(quote! {
#lefts => Some(#module_prefix::rule::ReduceType::Left),
});
}
if !rights.is_empty() {
stream.extend(quote! {
#rights => Some(#module_prefix::rule::ReduceType::Right),
});
}
stream.extend(quote! {
_ => None,
});
stream
};
let grammar_build_stream = {
let state_index_typename = if self.states.len() <= u8::MAX as usize {
quote! { u8 }
} else if self.states.len() <= u16::MAX as usize {
quote! { u16 }
} else if self.states.len() <= u32::MAX as usize {
quote! { u32 }
} else {
quote! { usize }
};
let rule_index_typename = if self.builder.rules.len() <= u8::MAX as usize {
quote! { u8 }
} else if self.builder.rules.len() <= u16::MAX as usize {
quote! { u16 }
} else if self.builder.rules.len() <= u32::MAX as usize {
quote! { u32 }
} else {
quote! { usize }
};
let mut production_rules_body_stream = TokenStream::new();
for rule in &self.builder.rules {
let mut tokens_vec_body_stream = TokenStream::new();
for &token in &rule.rule.rule {
let token_stream = token_to_stream(token);
tokens_vec_body_stream.extend(quote! {
#token_stream,
});
}
let name = &nonterminals_token[rule.rule.name];
let precedence_stream = if let Some(precedence) = rule.rule.precedence {
let s = precedence_to_stream(precedence);
quote! { Some(#s) }
} else {
quote! { None }
};
production_rules_body_stream.extend(quote! {
#module_prefix::rule::ProductionRule{
name: #name,
rule: vec![ #tokens_vec_body_stream ],
precedence: #precedence_stream,
},
});
}
let mut states_body_stream = TokenStream::new();
let mut terminal_sets_name_map = std::collections::BTreeMap::new();
let mut get_or_insert_terminal_set =
|set: std::collections::BTreeSet<TerminalSymbol<usize>>| -> Ident {
let new_index = terminal_sets_name_map.len();
terminal_sets_name_map
.entry(set)
.or_insert_with(|| format_ident!("__RUSTYLR_TSET{new_index}"))
.clone()
};
for state in &self.states {
let mut shift_term_body_stream = TokenStream::new();
for &(term, next_state) in &state.shift_goto_map_term {
let push = next_state.push;
let next_state = proc_macro2::Literal::usize_unsuffixed(next_state.state);
let term = match term {
TerminalSymbol::Term(term) => {
let var = &class_variants[term];
quote! { #termclass_typename::#var }
}
TerminalSymbol::Error => {
let error_name = format_ident!("{}", utils::ERROR_NAME);
quote! { #termclass_typename::#error_name }
}
TerminalSymbol::Eof => {
let eof_name = format_ident!("{}", utils::EOF_NAME);
quote! { #termclass_typename::#eof_name }
}
};
shift_term_body_stream.extend(quote! {
(#term, #module_prefix::parser::state::ShiftTarget::new(#next_state,#push)),
});
}
let mut shift_nonterm_body_stream = TokenStream::new();
for &(nonterm, next_state) in &state.shift_goto_map_nonterm {
let nonterm_stream = &nonterminals_token[nonterm];
let push = next_state.push;
let next_state = proc_macro2::Literal::usize_unsuffixed(next_state.state);
shift_nonterm_body_stream.extend(quote! {
(#nonterm_stream, #module_prefix::parser::state::ShiftTarget::new(#next_state,#push)),
});
}
let mut reduce_body_stream = TokenStream::new();
let mut reduce_rules_terms_map = std::collections::BTreeMap::new();
for (term, rules) in &state.reduce_map {
reduce_rules_terms_map
.entry(rules)
.or_insert_with(std::collections::BTreeSet::new)
.insert(*term);
}
for (rules, terms) in reduce_rules_terms_map {
let terms_set_name = get_or_insert_terminal_set(terms);
let rules_len = rules.len();
let rules_it = rules
.iter()
.map(|&rule| proc_macro2::Literal::usize_unsuffixed(rule));
reduce_body_stream.extend(quote! {
{
static __REDUCE_RULES:[#rule_index_typename; #rules_len] = [#(#rules_it),*];
__reduce_map.extend(
#terms_set_name.iter().map(
|term| (*term, __REDUCE_RULES.to_vec())
)
);
}
});
}
let mut ruleset_rules_body_stream = TokenStream::new();
let mut ruleset_shifted_body_stream = TokenStream::new();
let ruleset_len = state.ruleset.len();
let mut max_shifted = 0;
for &rule in &state.ruleset {
max_shifted = max_shifted.max(rule.shifted);
let shifted = proc_macro2::Literal::usize_unsuffixed(rule.shifted);
let rule = proc_macro2::Literal::usize_unsuffixed(rule.rule);
ruleset_rules_body_stream.extend(quote! {
#rule,
});
ruleset_shifted_body_stream.extend(quote! {
#shifted,
});
}
let shifted_typename = if max_shifted <= u8::MAX as usize {
quote! { u8 }
} else if max_shifted <= u16::MAX as usize {
quote! { u16 }
} else if max_shifted <= u32::MAX as usize {
quote! { u32 }
} else {
quote! { usize }
};
let reduce_map_construct_stream = if reduce_body_stream.is_empty() {
quote! { Default::default() }
} else {
quote! {
{
let mut __reduce_map = std::collections::BTreeMap::new();
#reduce_body_stream
__reduce_map.into_iter().collect()
}
}
};
let can_accept_error = match state.can_accept_error {
rusty_lr_core::TriState::False => quote! { #module_prefix::TriState::False },
rusty_lr_core::TriState::True => quote! { #module_prefix::TriState::True },
rusty_lr_core::TriState::Maybe => quote! { #module_prefix::TriState::Maybe },
};
states_body_stream.extend(quote! {
#module_prefix::parser::state::IntermediateState {
shift_goto_map_term: vec![#shift_term_body_stream],
shift_goto_map_nonterm: vec![#shift_nonterm_body_stream],
reduce_map: #reduce_map_construct_stream,
ruleset: {
static __RULES: [#rule_index_typename; #ruleset_len] = [
#ruleset_rules_body_stream
];
static __SHIFTED: [#shifted_typename; #ruleset_len] = [
#ruleset_shifted_body_stream
];
__RULES.iter().zip(__SHIFTED.iter()).map(
|(&rule, &shifted)| {
#module_prefix::rule::ShiftedRuleRef {
rule: rule as usize,
shifted: shifted as usize,
}
}
).collect()
},
can_accept_error: #can_accept_error,
},
});
}
let mut terminal_set_initialize_stream = TokenStream::new();
for (set, name) in terminal_sets_name_map {
let len = set.len();
let set_it = set.into_iter().map(|val| match val {
TerminalSymbol::Term(term) => {
let var = &class_variants[term];
quote! { #termclass_typename::#var }
}
TerminalSymbol::Error => {
let error_name = format_ident!("{}", utils::ERROR_NAME);
quote! { #termclass_typename::#error_name }
}
TerminalSymbol::Eof => {
let eof_name = format_ident!("{}", utils::EOF_NAME);
quote! { #termclass_typename::#eof_name }
}
});
terminal_set_initialize_stream.extend(quote! {
static #name: [#termclass_typename; #len] = [#(#set_it),*];
});
}
quote! {
let rules: Vec<#module_prefix::rule::ProductionRule<
#termclass_typename, #nonterminals_enum_name
>> = vec![
#production_rules_body_stream
];
#terminal_set_initialize_stream
let states: Vec<#module_prefix::parser::state::IntermediateState<
#termclass_typename, #nonterminals_enum_name, #state_index_typename, #rule_index_typename
>> = vec![
#states_body_stream
];
let states:Vec<#state_typename> = states.into_iter().map(
|state| state.into(),
).collect();
}
};
let error_used = self.error_used;
stream.extend(quote! {
#[allow(unused_braces, unused_parens, unused_variables, non_snake_case, unused_mut)]
pub struct #parser_struct_name {
pub rules: Vec<#rule_typename>,
pub states: Vec<#state_typename>,
}
#[rustfmt::skip]
impl #module_prefix::parser::Parser for #parser_struct_name {
type Term = #token_typename;
type TermClass = #termclass_typename;
type NonTerm = #nonterminals_enum_name;
type State = #state_typename;
const ERROR_USED:bool = #error_used;
fn precedence_types(&self, level: u8) -> Option<#module_prefix::rule::ReduceType> {
#[allow(unreachable_patterns)]
match level {
#precedence_types_match_body_stream
}
}
fn get_rules(&self) -> &[#rule_typename] {
&self.rules
}
fn get_states(&self) -> &[#state_typename] {
&self.states
}
}
#[rustfmt::skip]
#[allow(unused_braces, unused_parens, unused_variables, non_snake_case, unused_mut)]
impl #parser_struct_name {
#[allow(clippy::clone_on_copy)]
pub fn new() -> Self {
#grammar_build_stream
Self {
rules,
states,
}
}
}
});
}
fn emit_data_stack(&self, stream: &mut TokenStream) {
use rusty_lr_core::TerminalSymbol;
use rusty_lr_core::Token;
let module_prefix = &self.module_prefix;
let nonterminals_enum_name = format_ident!("{}NonTerminals", &self.start_rule_name);
let reduce_error_typename = &self.error_typename;
let data_stack_typename = format_ident!("{}DataStack", self.start_rule_name);
let token_typename = &self.token_typename;
let user_data_parameter_name =
Ident::new(utils::USER_DATA_PARAMETER_NAME, Span::call_site());
let user_data_typename = &self.userdata_typename;
let location_typename = self
.location_typename
.as_ref()
.cloned()
.unwrap_or_else(|| quote! { #module_prefix::DefaultLocation });
let tag_stack_name = format_ident!("__tags");
let tag_enum_name = format_ident!("{}Tags", &self.start_rule_name);
let empty_tag_name = format_ident!("Empty");
let terminal_stack_name = format_ident!("__terminals");
let mut stack_names_for_nonterm = Vec::with_capacity(self.nonterminals.len());
let mut ruletype_stack_map: rusty_lr_core::hash::HashMap<String, Option<Ident>> =
Default::default();
let mut stack_names_in_order = Vec::new();
let mut empty_tag_used = false;
let mut terminal_data_used = false;
if self.terminal_classes.iter().any(|c| c.data_used) {
terminal_data_used = true;
ruletype_stack_map.insert(
self.token_typename.to_string(),
Some(terminal_stack_name.clone()),
);
stack_names_in_order.push((terminal_stack_name.clone(), self.token_typename.clone()));
}
if self.terminal_classes.iter().any(|c| !c.data_used) {
empty_tag_used = true;
}
fn remove_whitespaces(s: String) -> String {
s.chars().filter(|c| !c.is_whitespace()).collect()
}
for nonterm in self.nonterminals.iter() {
if let Some(ruletype_stream) = nonterm.ruletype.as_ref().cloned() {
let cur_len = ruletype_stack_map.len();
let stack_name = ruletype_stack_map
.entry(remove_whitespaces(ruletype_stream.to_string()))
.or_insert_with(|| {
let new_stack_name = format_ident!("__stack{}", cur_len);
stack_names_in_order
.push((new_stack_name.clone(), ruletype_stream.clone()));
Some(new_stack_name)
})
.clone();
stack_names_for_nonterm.push(stack_name);
} else {
empty_tag_used = true;
stack_names_for_nonterm.push(None);
}
}
let unique_tag = (empty_tag_used && stack_names_in_order.is_empty())
|| (!empty_tag_used && stack_names_in_order.len() == 1);
let token_to_stack_name = |token: Token<TerminalSymbol<usize>, usize>| match token {
Token::Term(term) => match term {
TerminalSymbol::Term(term) => {
if self.terminal_classes[term].data_used {
Some(&terminal_stack_name)
} else {
None
}
}
TerminalSymbol::Error | TerminalSymbol::Eof => None,
},
Token::NonTerm(nonterm_idx) => stack_names_for_nonterm[nonterm_idx].as_ref(),
};
let mut reduce_action_case_streams = quote! {};
let mut fn_reduce_for_each_rule_stream = TokenStream::new();
for (i, action) in self.custom_reduce_actions.iter().enumerate() {
let fn_name = format_ident!("custom_reduce_action_{}", i);
let data_arg = action
.input_type
.as_ref()
.map(|(name, ty)| {
quote! { mut #name: #ty, }
})
.unwrap_or_default();
let location_arg = action
.input_location
.as_ref()
.map(|name| {
quote! { mut #name: #location_typename, }
})
.unwrap_or_default();
let body = &action.body.body;
let output_type = if let Some(ty) = action.output_type.as_ref() {
ty.clone()
} else {
quote! { () }
};
fn_reduce_for_each_rule_stream.extend(quote! {
fn #fn_name(
#data_arg
#location_arg
#user_data_parameter_name: &mut #user_data_typename,
__rustylr_location0: &mut #location_typename,
) -> Result<#output_type, #reduce_error_typename> {
Ok(#body)
}
});
}
let mut rule_index: usize = 0;
for (nonterm_idx, nonterm) in self.nonterminals.iter().enumerate() {
for (rule_local_id, rule) in nonterm.rules.iter().enumerate() {
let reduce_fn_ident = format_ident!("reduce_{}_{}", nonterm.name, rule_local_id);
let rule_debug_str = format!(
"{} -> {}",
self.nonterm_pretty_name(nonterm_idx),
rule.tokens
.iter()
.map(|t| {
match t.token {
Token::Term(term) => self.class_pretty_name_list(term, 5),
Token::NonTerm(nonterm) => self.nonterm_pretty_name(nonterm),
}
})
.collect::<Vec<_>>()
.join(" ")
);
use super::nonterminal_info::ReduceAction;
#[derive(PartialEq, Eq, PartialOrd, Ord)]
enum StackName {
DataStack(Ident),
LocationStack,
}
impl StackName {
pub fn to_token_stream(&self) -> TokenStream {
match self {
StackName::DataStack(name) => {
quote! {__data_stack.#name}
}
StackName::LocationStack => quote! {__location_stack},
}
}
}
if rule.is_used {
let mut debug_tag_check_stream = TokenStream::new();
let mut stack_mapto_map = std::collections::BTreeMap::new();
for (token_idx, token) in rule.tokens.iter().enumerate().rev() {
let token_index_from_end = rule.tokens.len() - 1 - token_idx;
let stack_name = token_to_stack_name(token.token);
let tag_name = stack_name.unwrap_or(&empty_tag_name);
if let Some(stack_name) = stack_name {
let mapto =
if let Some(first_chain) = token.reduce_action_chains.first() {
let first_chain = &self.custom_reduce_actions[*first_chain];
if first_chain.input_type.is_some() {
Some(format_ident!("__rustylr_data_{}", token_idx))
} else {
None
}
} else {
if let Some(mapto) = &token.mapto {
if rule.reduce_action_contains_ident(mapto) {
Some(mapto.clone())
} else {
None
}
} else {
None
}
};
stack_mapto_map
.entry(StackName::DataStack(stack_name.clone()))
.or_insert_with(Vec::new)
.push(mapto);
}
let location_mapto = if token.reduce_action_chains.is_empty() {
if let Some(mapto) = &token.mapto {
let location_varname =
format_ident!("__rustylr_location_{}", mapto);
if rule.reduce_action_contains_ident(&location_varname) {
Some(location_varname)
} else {
None
}
} else {
None
}
} else {
if token.reduce_action_chains.iter().any(|&idx| {
let action = &self.custom_reduce_actions[idx];
action.input_location.is_some()
}) {
Some(format_ident!("__rustylr_location_{}", token_idx))
} else {
None
}
};
stack_mapto_map
.entry(StackName::LocationStack)
.or_insert_with(Vec::new)
.push(location_mapto);
if !unique_tag {
debug_tag_check_stream.extend(quote! {
debug_assert!(
__data_stack.#tag_stack_name.get(
__data_stack.#tag_stack_name.len()-1-#token_index_from_end
) == Some( &#tag_enum_name::#tag_name )
);
});
}
}
let new_tag_name = stack_names_for_nonterm[nonterm_idx]
.as_ref()
.unwrap_or(&empty_tag_name);
let modify_tag_stream = if unique_tag {
TokenStream::new()
} else {
if rule.tokens.len() > 0 {
if new_tag_name == &empty_tag_name {
let first_tag_name = token_to_stack_name(rule.tokens[0].token)
.unwrap_or(&empty_tag_name);
if first_tag_name == new_tag_name {
let len = rule.tokens.len() - 1;
let truncate_stream = if len > 0 {
quote! {__data_stack.#tag_stack_name.truncate(__data_stack.#tag_stack_name.len() - #len);}
} else {
TokenStream::new()
};
truncate_stream
} else {
let len = rule.tokens.len();
quote! {
__data_stack.#tag_stack_name.truncate(__data_stack.#tag_stack_name.len() - #len);
__data_stack.#tag_stack_name.push(#tag_enum_name::#new_tag_name);
}
}
} else {
let first_tag_name = token_to_stack_name(rule.tokens[0].token)
.unwrap_or(&empty_tag_name);
if first_tag_name == new_tag_name {
let lenm1 = rule.tokens.len() - 1;
let truncate_stream = if lenm1 > 0 {
quote! {__data_stack.#tag_stack_name.truncate(__data_stack.#tag_stack_name.len() - #lenm1);}
} else {
TokenStream::new()
};
let len = rule.tokens.len();
quote! {
if __push_data {
#truncate_stream
} else {
__data_stack.#tag_stack_name.truncate(__data_stack.#tag_stack_name.len() - #len);
__data_stack.#tag_stack_name.push(#tag_enum_name::#empty_tag_name);
}
}
} else if first_tag_name == &empty_tag_name {
let lenm1 = rule.tokens.len() - 1;
let truncate_stream = if lenm1 > 0 {
quote! {__data_stack.#tag_stack_name.truncate(__data_stack.#tag_stack_name.len() - #lenm1);}
} else {
TokenStream::new()
};
let len = rule.tokens.len();
quote! {
if __push_data {
__data_stack.#tag_stack_name.truncate(__data_stack.#tag_stack_name.len() - #len);
__data_stack.#tag_stack_name.push(#tag_enum_name::#new_tag_name);
} else {
#truncate_stream
}
}
} else {
let len = rule.tokens.len();
quote! {
__data_stack.#tag_stack_name.truncate(__data_stack.#tag_stack_name.len() - #len);
if __push_data {
__data_stack.#tag_stack_name.push(#tag_enum_name::#new_tag_name);
} else {
__data_stack.#tag_stack_name.push(#tag_enum_name::#empty_tag_name);
}
}
}
}
} else {
if new_tag_name == &empty_tag_name {
quote! {
__data_stack.#tag_stack_name.push(#tag_enum_name::#new_tag_name);
}
} else {
quote! {
if __push_data {
__data_stack.#tag_stack_name.push(#tag_enum_name::#new_tag_name);
} else {
__data_stack.#tag_stack_name.push(#tag_enum_name::#empty_tag_name);
}
}
}
}
};
let mut extract_data_stream = TokenStream::new();
for (stack_name, maptos) in stack_mapto_map.iter() {
let stack_stream = stack_name.to_token_stream();
let mut last_none_count: usize = 0;
for mapto in maptos {
match mapto {
Some(mapto) => {
if last_none_count > 0 {
extract_data_stream.extend(quote! {
#stack_stream.truncate(#stack_stream.len() - #last_none_count);
});
last_none_count = 0;
}
extract_data_stream.extend(quote! {
let mut #mapto = #stack_stream.pop().unwrap();
});
}
None => {
last_none_count += 1;
}
}
}
if last_none_count > 0 {
extract_data_stream.extend(quote! {
#stack_stream.truncate(#stack_stream.len() - #last_none_count);
});
}
}
let mut custom_reduce_action_stream = TokenStream::new();
for (token_idx, token) in rule.tokens.iter().enumerate() {
if token.reduce_action_chains.is_empty() {
continue;
}
let data_varname = format_ident!("__rustylr_data_{}", token_idx);
let location_varname = format_ident!("__rustylr_location_{}", token_idx);
let location_used_in_this_action = if let Some(mapto) = &token.mapto {
let location_mapto_varname =
format_ident!("__rustylr_location_{}", mapto);
rule.reduce_action_contains_ident(&location_mapto_varname)
} else {
false
};
for (idx, &chain_idx) in token.reduce_action_chains.iter().enumerate() {
let action = &self.custom_reduce_actions[chain_idx];
let fn_name = format_ident!("custom_reduce_action_{}", chain_idx);
let data_arg = if action.input_type.is_some() {
quote! { #data_varname, }
} else {
quote! {}
};
let mut location_used_later = location_used_in_this_action;
if !location_used_later {
for &chain_idx in token.reduce_action_chains[idx + 1..].iter() {
let action = &self.custom_reduce_actions[chain_idx];
if action.input_location.is_some() {
location_used_later = true;
break;
}
}
}
let location_arg = if action.input_location.is_some() {
if location_used_later {
quote! { #location_varname.clone(), }
} else {
quote! { #location_varname, }
}
} else {
quote! {}
};
custom_reduce_action_stream.extend(quote! {
let #data_varname = Self::#fn_name(
#data_arg
#location_arg
#user_data_parameter_name,
__rustylr_location0,
)?;
});
}
if let Some(mapto) = &token.mapto {
if rule.reduce_action_contains_ident(mapto) {
custom_reduce_action_stream.extend(quote! {
let mut #mapto = #data_varname;
});
}
let location_mapto_varname =
format_ident!("__rustylr_location_{}", mapto);
if rule.reduce_action_contains_ident(&location_mapto_varname) {
custom_reduce_action_stream.extend(quote! {
let mut #location_mapto_varname = #location_varname;
});
}
}
}
reduce_action_case_streams.extend(quote! {
#rule_index => Self::#reduce_fn_ident( data_stack, location_stack, push_data, shift, lookahead, user_data, location0 ),
});
let reduce_action_body = match &rule.reduce_action {
Some(ReduceAction::Custom(custom)) => {
let body = &custom.body;
quote! { #body; }
}
Some(ReduceAction::Identity(identity_idx)) => {
let ith_ident = rule.tokens[*identity_idx].mapto.as_ref().unwrap();
quote! { #ith_ident; }
}
None => TokenStream::new(),
};
if let Some(stack_name) = &stack_names_for_nonterm[nonterm_idx] {
fn_reduce_for_each_rule_stream.extend(quote! {
#[doc = #rule_debug_str]
#[inline]
fn #reduce_fn_ident(
__data_stack: &mut Self,
__location_stack: &mut Vec<#location_typename>,
__push_data: bool,
shift: &mut bool,
lookahead: &#module_prefix::TerminalSymbol<#token_typename>,
#user_data_parameter_name: &mut #user_data_typename,
__rustylr_location0: &mut #location_typename,
) -> Result<(), #reduce_error_typename> {
#[cfg(debug_assertions)]
{
#debug_tag_check_stream
}
#modify_tag_stream
#extract_data_stream
#custom_reduce_action_stream
let __res = #reduce_action_body
if __push_data {
__data_stack.#stack_name.push(__res);
}
Ok(())
}
});
} else {
fn_reduce_for_each_rule_stream.extend(quote! {
#[doc = #rule_debug_str]
#[inline]
fn #reduce_fn_ident(
__data_stack: &mut Self,
__location_stack: &mut Vec<#location_typename>,
__push_data: bool,
shift: &mut bool,
lookahead: &#module_prefix::TerminalSymbol<#token_typename>,
#user_data_parameter_name: &mut #user_data_typename,
__rustylr_location0: &mut #location_typename,
) -> Result<(), #reduce_error_typename> {
#[cfg(debug_assertions)]
{
#debug_tag_check_stream
}
#modify_tag_stream
#extract_data_stream
#custom_reduce_action_stream
#reduce_action_body
Ok(())
}
});
}
}
rule_index += 1;
}
}
let start_idx = *self.nonterminals_index.get(&self.start_rule_name).unwrap();
let start_stack_name = &stack_names_for_nonterm[start_idx];
let tag_name = start_stack_name.as_ref().unwrap_or(&empty_tag_name);
let tag_check_stream = if unique_tag {
TokenStream::new()
} else {
quote! {
let tag = self.#tag_stack_name.pop();
debug_assert!(tag == Some(#tag_enum_name::#tag_name));
}
};
let (start_typename, pop_start) = match start_stack_name {
Some(stack_name) => {
let ruletype = self.nonterminals[start_idx]
.ruletype
.as_ref()
.unwrap()
.clone();
(
ruletype,
quote! {
#tag_check_stream
self.#stack_name.pop()
},
)
}
None => (
quote! {()},
quote! {
#tag_check_stream
Some(())
},
),
};
let max_shift = self
.nonterminals
.iter()
.map(|nonterm| {
nonterm
.rules
.iter()
.map(|rule| rule.tokens.len())
.max()
.unwrap_or(0)
})
.max()
.unwrap_or(0);
let shift_typename = if max_shift <= u8::MAX as usize {
quote! { u8 }
} else if max_shift <= u16::MAX as usize {
quote! { u16 }
} else if max_shift <= u32::MAX as usize {
quote! { u32 }
} else {
quote! { usize }
};
let mut stack_definition_stream = TokenStream::new();
let mut stack_default_stream = TokenStream::new();
let mut pop_body_stream = TokenStream::new();
let mut stack_clear_stream = TokenStream::new();
let mut stack_append_stream = TokenStream::new();
let stack_len = stack_names_in_order.len();
let mut split_off_split_stream = TokenStream::new();
let mut split_off_ctor_stream = TokenStream::new();
let mut truncate_stream = TokenStream::new();
for (stack_idx, (stack_name, typename)) in stack_names_in_order.iter().enumerate() {
stack_definition_stream.extend(quote! {
#stack_name: Vec<#typename>,
});
stack_default_stream.extend(quote! {
#stack_name: Vec::new(),
});
if unique_tag {
pop_body_stream.extend(quote! {
self.#stack_name.pop();
});
} else {
pop_body_stream.extend(quote! {
#tag_enum_name::#stack_name => { self.#stack_name.pop(); }
});
};
stack_clear_stream.extend(quote! {
self.#stack_name.clear();
});
stack_append_stream.extend(quote! {
self.#stack_name.append(&mut other.#stack_name);
});
let other_stack_name = format_ident!("__other_{}", stack_name);
if unique_tag {
split_off_split_stream.extend(quote! {
let #other_stack_name = self.#stack_name.split_off( at );
});
} else {
split_off_split_stream.extend(quote! {
let #other_stack_name = self.#stack_name.split_off( self.#stack_name.len() - (__counts[#stack_idx] as usize) );
});
};
split_off_ctor_stream.extend(quote! {
#stack_name: #other_stack_name,
});
if unique_tag {
truncate_stream.extend(quote! {
self.#stack_name.truncate( at );
});
} else {
truncate_stream.extend(quote! {
self.#stack_name.truncate( self.#stack_name.len() - (__counts[#stack_idx] as usize) );
});
};
}
let pop_stream = if unique_tag {
pop_body_stream
} else {
quote! {
match self.#tag_stack_name.pop().unwrap() {
#pop_body_stream
_ => {}
}
}
};
let split_off_count_stream = if unique_tag {
quote! {}
} else {
quote! {
let mut __counts: [#shift_typename; #stack_len+1] = [0; #stack_len+1];
let __other_tag_stack = self.#tag_stack_name.split_off(at);
for &tag in &__other_tag_stack {
__counts[ tag as usize ] += 1;
}
}
};
if !unique_tag {
split_off_ctor_stream.extend(quote! {
#tag_stack_name: __other_tag_stack,
});
}
let truncate_count_stream = if unique_tag {
quote! {}
} else {
quote! {
let mut __counts: [#shift_typename; #stack_len+1] = [0; #stack_len+1];
for &tag in &self.#tag_stack_name[at..] {
__counts[ tag as usize ] += 1;
}
self.#tag_stack_name.truncate( self.#tag_stack_name.len() - at );
}
};
let derive_clone_for_glr = if self.glr {
quote! {#[derive(Clone)]}
} else {
quote! {}
};
let push_terminal_body_stream = if terminal_data_used {
if unique_tag {
quote! {
self.#terminal_stack_name.push( term );
}
} else {
quote! {
self.#tag_stack_name.push(#tag_enum_name::#terminal_stack_name);
self.#terminal_stack_name.push( term );
}
}
} else {
quote! {
unreachable!();
}
};
let push_empty_body_stream = if empty_tag_used {
if unique_tag {
TokenStream::new()
} else {
quote! {
self.#tag_stack_name.push(#tag_enum_name::#empty_tag_name);
}
}
} else {
quote! { unreachable!(); }
};
let tag_definition_stream = if unique_tag {
TokenStream::new()
} else {
let mut tag_definition_body_stream = TokenStream::new();
for (stack_name, _) in &stack_names_in_order {
tag_definition_body_stream.extend(quote! {
#stack_name,
});
}
if empty_tag_used {
tag_definition_body_stream.extend(quote! {
#empty_tag_name,
});
}
quote! {
#[rustfmt::skip]
#[allow(unused_braces, unused_parens, non_snake_case, non_camel_case_types)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum #tag_enum_name {
#tag_definition_body_stream
}
}
};
let tag_stack_definition_stream = if unique_tag {
TokenStream::new()
} else {
quote! { pub #tag_stack_name: Vec<#tag_enum_name>, }
};
let tag_stack_init_stream = if unique_tag {
TokenStream::new()
} else {
quote! {
#tag_stack_name: Vec::new(),
}
};
let tag_stack_clear_stream = if unique_tag {
TokenStream::new()
} else {
quote! {
self.#tag_stack_name.clear();
}
};
let tag_stack_reserve_stream = if unique_tag {
TokenStream::new()
} else {
quote! {
self.#tag_stack_name.reserve(additional);
}
};
let tag_stack_append_stream = if unique_tag {
TokenStream::new()
} else {
quote! {
self.#tag_stack_name.append(&mut other.#tag_stack_name);
}
};
stream.extend(quote! {
#tag_definition_stream
#[rustfmt::skip]
#[allow(unused_braces, unused_parens, non_snake_case, non_camel_case_types)]
#derive_clone_for_glr
pub struct #data_stack_typename {
#tag_stack_definition_stream
#stack_definition_stream
}
impl Default for #data_stack_typename {
fn default() -> Self {
Self {
#tag_stack_init_stream
#stack_default_stream
}
}
}
#[rustfmt::skip]
#[allow(unused_braces, unused_parens, unused_variables, non_snake_case, unused_mut, dead_code)]
impl #data_stack_typename {
#fn_reduce_for_each_rule_stream
}
#[rustfmt::skip]
#[allow(unused_braces, unused_parens, non_snake_case, non_camel_case_types, unused_variables)]
impl #module_prefix::parser::data_stack::DataStack for #data_stack_typename {
type Term = #token_typename;
type NonTerm = #nonterminals_enum_name;
type ReduceActionError = #reduce_error_typename;
type UserData = #user_data_typename;
type StartType = #start_typename;
type Location = #location_typename;
fn pop_start(&mut self) -> Option<Self::StartType> {
#pop_start
}
fn pop(&mut self) {
#pop_stream
}
fn push_terminal(&mut self, term: Self::Term) {
#push_terminal_body_stream
}
fn push_empty(&mut self) {
#push_empty_body_stream
}
fn clear(&mut self) {
#tag_stack_clear_stream
#stack_clear_stream
}
fn reserve(&mut self, additional: usize) {
#tag_stack_reserve_stream
}
fn split_off(&mut self, at: usize) -> Self {
#split_off_count_stream
#split_off_split_stream
Self {
#split_off_ctor_stream
}
}
fn truncate(&mut self, at: usize) {
#truncate_count_stream
#truncate_stream
}
fn append(&mut self, other: &mut Self) {
#tag_stack_append_stream
#stack_append_stream
}
fn reduce_action(
data_stack: &mut Self,
location_stack: &mut Vec<#location_typename>,
push_data: bool,
rule_index: usize,
shift: &mut bool,
lookahead: &#module_prefix::TerminalSymbol<Self::Term>,
user_data: &mut Self::UserData,
location0: &mut Self::Location,
) -> Result<(), Self::ReduceActionError> {
match rule_index {
#reduce_action_case_streams
_ => {
unreachable!( "Invalid Rule: {}", rule_index );
}
}
}
}
});
}
pub fn emit_compiletime(&self) -> TokenStream {
let mut stream = TokenStream::new();
self.emit_type_alises(&mut stream);
self.emit_termclass_enum(&mut stream);
self.emit_nonterm_enum(&mut stream);
self.emit_data_stack(&mut stream);
self.emit_parser(&mut stream);
stream
}
}