use collections::{Entry, Map, Set};
use grammar::parse_tree::WhereClause;
use grammar::repr::*;
use string_cache::DefaultAtom as Atom;
use lr1::core::*;
use lr1::lookahead::Token;
use rust::RustWrite;
use std::fmt;
use std::io::{self, Write};
use tls::Tls;
use util::Sep;
use super::base::CodeGenerator;
const DEBUG_PRINT: bool = false;
pub fn compile<'grammar, W: Write>(
grammar: &'grammar Grammar,
user_start_symbol: NonterminalString,
start_symbol: NonterminalString,
states: &[LR1State<'grammar>],
action_module: &str,
out: &mut RustWrite<W>,
) -> io::Result<()> {
let mut table_driven = CodeGenerator::new_table_driven(
grammar,
user_start_symbol,
start_symbol,
states,
action_module,
out,
);
table_driven.write()
}
enum Comment<'a, T> {
Goto(T, usize),
Error(T),
Reduce(T, &'a Production),
}
impl<'a, T: fmt::Display> fmt::Display for Comment<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Comment::Goto(ref token, new_state) => {
write!(f, " // on {}, goto {}", token, new_state)
}
Comment::Error(ref token) => write!(f, " // on {}, error", token),
Comment::Reduce(ref token, production) => {
write!(f, " // on {}, reduce `{:?}`", token, production)
}
}
}
}
struct TableDriven<'grammar> {
symbol_type_params: Vec<TypeParameter>,
symbol_where_clauses: Vec<WhereClause<TypeRepr>>,
all_nonterminals: Vec<NonterminalString>,
reduce_indices: Map<&'grammar Production, usize>,
state_type: &'static str,
variant_names: Map<Symbol, String>,
variants: Map<TypeRepr, String>,
reduce_functions: Set<usize>,
}
impl<'ascent, 'grammar, W: Write> CodeGenerator<'ascent, 'grammar, W, TableDriven<'grammar>> {
fn new_table_driven(
grammar: &'grammar Grammar,
user_start_symbol: NonterminalString,
start_symbol: NonterminalString,
states: &'ascent [LR1State<'grammar>],
action_module: &str,
out: &'ascent mut RustWrite<W>,
) -> Self {
let referenced_ty_params: Set<TypeParameter> = grammar
.types
.nonterminal_types()
.into_iter()
.chain(grammar.types.terminal_types())
.flat_map(|t| t.referenced())
.collect();
let symbol_type_params: Vec<_> = grammar
.type_parameters
.iter()
.filter(|t| referenced_ty_params.contains(t))
.cloned()
.collect();
let mut referenced_where_clauses = Set::new();
for wc in &grammar.where_clauses {
wc.map(|ty| {
if ty.referenced()
.iter()
.any(|p| symbol_type_params.contains(p))
{
referenced_where_clauses.insert(wc.clone());
}
});
}
let symbol_where_clauses: Vec<_> = grammar
.where_clauses
.iter()
.filter(|wc| referenced_where_clauses.contains(wc))
.cloned()
.collect();
let reduce_indices: Map<&'grammar Production, usize> = grammar
.nonterminals
.values()
.flat_map(|nt| &nt.productions)
.zip(0..)
.collect();
let state_type = {
let max_value = ::std::cmp::max(states.len(), reduce_indices.len() - 1);
if max_value <= ::std::i8::MAX as usize {
"i8"
} else if max_value <= ::std::i16::MAX as usize {
"i16"
} else {
"i32"
}
};
CodeGenerator::new(
grammar,
user_start_symbol,
start_symbol,
states,
out,
false,
action_module,
TableDriven {
symbol_type_params: symbol_type_params,
symbol_where_clauses: symbol_where_clauses,
all_nonterminals: grammar.nonterminals.keys().cloned().collect(),
reduce_indices: reduce_indices,
state_type: state_type,
variant_names: Map::new(),
variants: Map::new(),
reduce_functions: Set::new(),
},
)
}
fn write(&mut self) -> io::Result<()> {
self.write_parse_mod(|this| {
try!(this.write_value_type_defn());
try!(this.write_parse_table());
try!(this.write_parser_fn());
try!(this.write_error_recovery_fn());
try!(this.write_accepts_fn());
try!(this.emit_reduce_actions());
try!(this.emit_downcast_fns());
try!(this.emit_reduce_action_functions());
Ok(())
})
}
fn write_value_type_defn(&mut self) -> io::Result<()> {
rust!(self.out, "#[allow(dead_code)]");
rust!(
self.out,
"pub enum {}Symbol<{}>",
self.prefix,
Sep(", ", &self.custom.symbol_type_params)
);
if !self.custom.symbol_where_clauses.is_empty() {
rust!(
self.out,
" where {}",
Sep(", ", &self.custom.symbol_where_clauses)
);
}
rust!(self.out, " {{");
for term in &self.grammar.terminals.all {
let ty = self.types.terminal_type(term).clone();
let len = self.custom.variants.len();
let name = match self.custom.variants.entry(ty.clone()) {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => {
let name = format!("Variant{}", len);
rust!(self.out, "{}({}),", name, ty);
entry.insert(name)
}
};
self.custom
.variant_names
.insert(Symbol::Terminal(term.clone()), name.clone());
}
for nt in self.grammar.nonterminals.keys() {
let ty = self.types.nonterminal_type(nt).clone();
let len = self.custom.variants.len();
let name = match self.custom.variants.entry(ty.clone()) {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => {
let name = format!("Variant{}", len);
rust!(self.out, "{}({}),", name, ty);
entry.insert(name)
}
};
self.custom
.variant_names
.insert(Symbol::Nonterminal(nt.clone()), name.clone());
}
rust!(self.out, "}}");
Ok(())
}
fn write_parse_table(&mut self) -> io::Result<()> {
rust!(
self.out,
"const {}ACTION: &'static [{}] = &[",
self.prefix,
self.custom.state_type
);
for (index, state) in self.states.iter().enumerate() {
rust!(self.out, "// State {}", index);
if Tls::session().emit_comments {
for item in state.items.vec.iter() {
rust!(self.out, "// {:?}", item);
}
}
let custom = &self.custom;
let iterator = self.grammar.terminals.all.iter().map(|terminal| {
if let Some(new_state) = state.shifts.get(&terminal) {
(
new_state.0 as i32 + 1,
Comment::Goto(Token::Terminal(terminal.clone()), new_state.0),
)
} else {
Self::write_reduction(custom, state, &Token::Terminal(terminal.clone()))
}
});
try!(self.out.write_table_row(iterator))
}
rust!(self.out, "];");
rust!(
self.out,
"const {}EOF_ACTION: &'static [{}] = &[",
self.prefix,
self.custom.state_type
);
for (index, state) in self.states.iter().enumerate() {
rust!(self.out, "// State {}", index);
let reduction = Self::write_reduction(&self.custom, state, &Token::EOF);
try!(self.out.write_table_row(Some(reduction)));
}
rust!(self.out, "];");
rust!(
self.out,
"const {}GOTO: &'static [{}] = &[",
self.prefix,
self.custom.state_type
);
for (index, state) in self.states.iter().enumerate() {
rust!(self.out, "// State {}", index);
let iterator = self.grammar.nonterminals.keys().map(|nonterminal| {
if let Some(&new_state) = state.gotos.get(&nonterminal) {
(
new_state.0 as i32 + 1,
Comment::Goto(nonterminal, new_state.0),
)
} else {
(0, Comment::Error(nonterminal))
}
});
try!(self.out.write_table_row(iterator));
}
rust!(self.out, "];");
try!(self.emit_expected_tokens_fn());
Ok(())
}
fn write_reduction<'s>(
custom: &TableDriven<'grammar>,
state: &'s LR1State,
token: &Token,
) -> (i32, Comment<'s, Token>) {
let reduction = state
.reductions
.iter()
.filter(|&&(ref t, _)| t.contains(token))
.map(|&(_, p)| p)
.next();
if let Some(production) = reduction {
let action = custom.reduce_indices[production];
(
-(action as i32 + 1),
Comment::Reduce(token.clone(), production),
)
} else {
(0, Comment::Error(token.clone()))
}
}
fn write_parser_fn(&mut self) -> io::Result<()> {
let phantom_data_expr = self.phantom_data_expr();
try!(self.start_parser_fn());
try!(self.define_tokens());
rust!(
self.out,
"let mut {}states = vec![0_{}];",
self.prefix,
self.custom.state_type
);
rust!(self.out, "let mut {}symbols = vec![];", self.prefix);
rust!(self.out, "let mut {}integer;", self.prefix);
rust!(self.out, "let mut {}lookahead;", self.prefix);
rust!(
self.out,
"let {}last_location = &mut Default::default();",
self.prefix
);
rust!(self.out, "'{}shift: loop {{", self.prefix);
try!(self.next_token("lookahead", "tokens", "last_location", "shift"));
try!(self.token_to_integer("integer", "lookahead"));
if DEBUG_PRINT {
rust!(
self.out,
"println!(\"pulled next token from input: {{:?}}\", \
{p}lookahead);",
p = self.prefix
);
rust!(
self.out,
"println!(\" - integer: {{}}\", \
{p}integer);",
p = self.prefix
);
}
rust!(self.out, "'{}inner: loop {{", self.prefix);
rust!(
self.out,
"let {}state = *{}states.last().unwrap() as usize;",
self.prefix,
self.prefix
);
rust!(
self.out,
"let {}action = {}ACTION[{}state * {} + {}integer];",
self.prefix,
self.prefix,
self.prefix,
self.grammar.terminals.all.len(),
self.prefix
);
if DEBUG_PRINT {
rust!(
self.out,
"println!(\"state: {{}} lookahead: {{:?}}/{{}} action: {{}} stack-depth: {{}}\", \
{p}state, {p}lookahead, {p}integer, {p}action, {p}symbols.len());",
p = self.prefix
);
}
rust!(self.out, "if {}action > 0 {{", self.prefix);
if DEBUG_PRINT {
rust!(
self.out,
"println!(\"--> shift `{{:?}}`\", {p}lookahead);",
p = self.prefix
);
}
try!(self.token_to_symbol());
rust!(
self.out,
"{}states.push({}action - 1);",
self.prefix,
self.prefix
);
rust!(
self.out,
"{}symbols.push(({}lookahead.0, {}symbol, {}lookahead.2));",
self.prefix,
self.prefix,
self.prefix,
self.prefix
);
rust!(self.out, "continue '{}shift;", self.prefix);
rust!(self.out, "}} else if {}action < 0 {{", self.prefix);
if DEBUG_PRINT {
rust!(self.out, "println!(\"--> reduce\");");
}
rust!(
self.out,
"if let Some(r) = {p}reduce({}{p}action, Some(&{p}lookahead.0), &mut {p}states, &mut \
{p}symbols, {}) {{",
self.grammar.user_parameter_refs(),
phantom_data_expr,
p = self.prefix
);
rust!(self.out, "if r.is_err() {{");
rust!(self.out, "return r;");
rust!(self.out, "}}");
rust!(
self.out,
"return Err({}lalrpop_util::ParseError::ExtraToken {{ token: {}lookahead }});",
self.prefix,
self.prefix
);
rust!(self.out, "}}");
rust!(self.out, "}} else {{");
self.try_error_recovery(
"tokens",
"states",
"symbols",
"last_location",
Some(("lookahead", "integer", "inner", "shift")),
)?;
rust!(self.out, "}}");
rust!(self.out, "}}");
rust!(self.out, "}}");
rust!(self.out, "loop {{");
rust!(
self.out,
"let {}state = *{}states.last().unwrap() as usize;",
self.prefix,
self.prefix
);
if DEBUG_PRINT {
rust!(
self.out,
"println!(\"EOF loop state: {{}}\", {}state);",
self.prefix
);
}
rust!(
self.out,
"let {}action = {}EOF_ACTION[{}state];",
self.prefix,
self.prefix,
self.prefix
);
if DEBUG_PRINT {
rust!(
self.out,
"println!(\"EOF in state {{}} takes action {{}}\", {}state, {}action);",
self.prefix,
self.prefix
);
}
rust!(self.out, "if {}action < 0 {{", self.prefix);
rust!(
self.out,
"if let Some(r) = {}reduce({}{}action, None, &mut {}states, &mut {}symbols, {}) {{",
self.prefix,
self.grammar.user_parameter_refs(),
self.prefix,
self.prefix,
self.prefix,
phantom_data_expr
);
rust!(self.out, "return r;");
rust!(self.out, "}}");
rust!(self.out, "}} else {{");
self.try_error_recovery("tokens", "states", "symbols", "last_location", None)?;
rust!(self.out, "}}");
rust!(self.out, "}}");
self.end_parser_fn()
}
fn next_token(
&mut self,
lookahead: &str,
tokens: &str,
last_location: &str,
break_on_eof: &str,
) -> io::Result<()> {
rust!(
self.out,
"{p}{lookahead} = match {p}{tokens}.next() {{",
lookahead = lookahead,
tokens = tokens,
p = self.prefix
);
rust!(self.out, "Some(Ok(v)) => v,");
rust!(self.out, "None => break '{}{},", self.prefix, break_on_eof); if self.grammar.intern_token.is_some() {
rust!(self.out, "Some(Err(e)) => return Err(e),");
} else {
rust!(
self.out,
"Some(Err(e)) => return Err({p}lalrpop_util::ParseError::User {{ error: e }}),",
p = self.prefix
);
}
rust!(self.out, "}};");
rust!(
self.out,
"*{p}{last_location} = {p}{lookahead}.2.clone();",
last_location = last_location,
lookahead = lookahead,
p = self.prefix
);
Ok(())
}
fn token_to_integer(&mut self, integer: &str, lookahead: &str) -> io::Result<()> {
rust!(
self.out,
"{p}{integer} = match {p}{lookahead}.1 {{",
integer = integer,
lookahead = lookahead,
p = self.prefix
);
for (terminal, index) in self.grammar.terminals.all.iter().zip(0..) {
if *terminal == TerminalString::Error {
continue;
}
let pattern = self.grammar.pattern(terminal).map(&mut |_| "_");
rust!(
self.out,
"{pattern} if true => {index},",
pattern = pattern,
index = index
);
}
rust!(self.out, "_ => {{");
let prefix = self.prefix;
try!(self.let_unrecognized_token_error(
"error",
&format!("Some({p}{lookahead})", lookahead = lookahead, p = prefix)
));
rust!(self.out, "return Err({p}error);", p = self.prefix);
rust!(self.out, "}}");
rust!(self.out, "}};");
Ok(())
}
fn token_to_symbol(&mut self) -> io::Result<()> {
rust!(
self.out,
"let {}symbol = match {}integer {{",
self.prefix,
self.prefix
);
for (terminal, index) in self.grammar.terminals.all.iter().zip(0..) {
if *terminal == TerminalString::Error {
continue;
}
rust!(self.out, "{} => match {}lookahead.1 {{", index, self.prefix);
let mut pattern_names = vec![];
let pattern = self.grammar.pattern(terminal).map(&mut |_| {
let index = pattern_names.len();
pattern_names.push(format!("{}tok{}", self.prefix, index));
pattern_names.last().cloned().unwrap()
});
let mut pattern = format!("{}", pattern);
if pattern_names.is_empty() {
pattern_names.push(format!("{}tok", self.prefix));
pattern = format!("{}tok @ {}", self.prefix, pattern);
}
let variant_name = self.variant_name_for_symbol(&Symbol::Terminal(terminal.clone()));
rust!(
self.out,
"{} => {}Symbol::{}(({})),",
pattern,
self.prefix,
variant_name,
pattern_names.join(", ")
);
rust!(self.out, "_ => unreachable!(),");
rust!(self.out, "}},");
}
rust!(self.out, "_ => unreachable!(),");
rust!(self.out, "}};");
Ok(())
}
fn emit_reduce_actions(&mut self) -> io::Result<()> {
let success_type = self.types.nonterminal_type(&self.start_symbol);
let parse_error_type = self.types.parse_error_type();
let loc_type = self.types.terminal_loc_type();
let spanned_symbol_type = self.spanned_symbol_type();
let parameters = vec![
format!("{}action: {}", self.prefix, self.custom.state_type),
format!("{}lookahead_start: Option<&{}>", self.prefix, loc_type),
format!(
"{}states: &mut ::std::vec::Vec<{}>",
self.prefix, self.custom.state_type
),
format!(
"{}symbols: &mut ::std::vec::Vec<{}>",
self.prefix, spanned_symbol_type
),
format!("_: {}", self.phantom_data_type()),
];
try!(self.out.write_fn_header(
self.grammar,
&Visibility::Pub(Some(Path::from_id(Atom::from("crate")))),
format!("{}reduce", self.prefix),
vec![],
None,
parameters,
format!("Option<Result<{},{}>>", success_type, parse_error_type),
vec![]
));
rust!(self.out, "{{");
rust!(
self.out,
"let ({p}pop_states, {p}symbol, {p}nonterminal) = match -{}action {{",
p = self.prefix
);
for (production, index) in self.grammar
.nonterminals
.values()
.flat_map(|nt| &nt.productions)
.zip(1..)
{
rust!(self.out, "{} => {{", index);
let is_fallible = self.grammar.action_is_fallible(production.action);
let reduce_stack_space = !is_fallible && production.nonterminal != self.start_symbol;
if reduce_stack_space {
self.custom.reduce_functions.insert(index);
let phantom_data_expr = self.phantom_data_expr();
rust!(
self.out,
"{p}reduce{}({}{p}action, {p}lookahead_start, {p}states, {p}symbols, {})",
index,
self.grammar.user_parameter_refs(),
phantom_data_expr,
p = self.prefix
);
} else {
try!(self.emit_reduce_action(production));
}
rust!(self.out, "}}");
}
rust!(
self.out,
"_ => panic!(\"invalid action code {{}}\", {}action)",
self.prefix
);
rust!(self.out, "}};");
rust!(
self.out,
"let {p}states_len = {p}states.len();",
p = self.prefix
);
rust!(
self.out,
"{p}states.truncate({p}states_len - {p}pop_states);",
p = self.prefix
);
rust!(self.out, "{p}symbols.push({p}symbol);", p = self.prefix);
rust!(
self.out,
"let {}state = *{}states.last().unwrap() as usize;",
self.prefix,
self.prefix
);
rust!(
self.out,
"let {}next_state = {}GOTO[{}state * {} + {}nonterminal] - 1;",
self.prefix,
self.prefix,
self.prefix,
self.grammar.nonterminals.len(),
self.prefix
);
if DEBUG_PRINT {
rust!(
self.out,
"println!(\"goto state {{}} from {{}} due to nonterminal {{}}\", {}next_state, \
{}state, {}nonterminal);",
self.prefix,
self.prefix,
self.prefix
);
}
rust!(
self.out,
"{}states.push({}next_state);",
self.prefix,
self.prefix
);
rust!(self.out, "None");
rust!(self.out, "}}");
Ok(())
}
fn emit_reduce_action_functions(&mut self) -> io::Result<()> {
for (production, index) in self.grammar
.nonterminals
.values()
.flat_map(|nt| &nt.productions)
.zip(1..)
{
if self.custom.reduce_functions.contains(&index) {
self.emit_reduce_alternative_fn_header(index)?;
self.emit_reduce_action(production)?;
rust!(self.out, "}}");
}
}
Ok(())
}
fn emit_reduce_alternative_fn_header(&mut self, index: usize) -> io::Result<()> {
let loc_type = self.types.terminal_loc_type();
let spanned_symbol_type = self.spanned_symbol_type();
let parameters = vec![
format!("{}action: {}", self.prefix, self.custom.state_type),
format!("{}lookahead_start: Option<&{}>", self.prefix, loc_type),
format!(
"{}states: &mut ::std::vec::Vec<{}>",
self.prefix, self.custom.state_type
),
format!(
"{}symbols: &mut ::std::vec::Vec<{}>",
self.prefix, spanned_symbol_type
),
format!("_: {}", self.phantom_data_type()),
];
try!(self.out.write_fn_header(
self.grammar,
&Visibility::Pub(Some(Path::from_id(Atom::from("crate")))),
format!("{}reduce{}", self.prefix, index),
vec![],
None,
parameters,
format!("(usize, {}, usize)", spanned_symbol_type,),
vec![]
));
rust!(self.out, "{{");
Ok(())
}
fn emit_reduce_action(&mut self, production: &Production) -> io::Result<()> {
rust!(self.out, "// {:?}", production);
for (index, symbol) in production.symbols.iter().enumerate().rev() {
let name = self.variant_name_for_symbol(symbol);
rust!(
self.out,
"let {}sym{} = {}pop_{}({}symbols);",
self.prefix,
index,
self.prefix,
name,
self.prefix
);
}
let transfer_syms: Vec<_> = (0..production.symbols.len())
.map(|i| format!("{}sym{}", self.prefix, i))
.collect();
if let Some(first_sym) = transfer_syms.first() {
rust!(
self.out,
"let {}start = {}.0.clone();",
self.prefix,
first_sym
);
} else {
rust!(
self.out,
"let {}start = {}symbols.last().map(|s| s.2.clone()).unwrap_or_default();",
self.prefix,
self.prefix
);
}
if let Some(last_sym) = transfer_syms.last() {
rust!(self.out, "let {}end = {}.2.clone();", self.prefix, last_sym);
} else {
rust!(
self.out,
"let {}end = {}lookahead_start.cloned().unwrap_or_else(|| \
{}start.clone());",
self.prefix,
self.prefix,
self.prefix
);
}
let transfered_syms = transfer_syms.len();
let mut args = transfer_syms;
if transfered_syms == 0 {
args.push(format!("&{}start", self.prefix));
args.push(format!("&{}end", self.prefix));
}
let is_fallible = self.grammar.action_is_fallible(production.action);
if is_fallible {
rust!(
self.out,
"let {}nt = match {}::{}action{}::<{}>({}{}) {{",
self.prefix,
self.action_module,
self.prefix,
production.action.index(),
Sep(", ", &self.grammar.non_lifetime_type_parameters()),
self.grammar.user_parameter_refs(),
Sep(", ", &args)
);
rust!(self.out, "Ok(v) => v,");
rust!(self.out, "Err(e) => return Some(Err(e)),");
rust!(self.out, "}};");
} else {
rust!(
self.out,
"let {}nt = {}::{}action{}::<{}>({}{});",
self.prefix,
self.action_module,
self.prefix,
production.action.index(),
Sep(", ", &self.grammar.non_lifetime_type_parameters()),
self.grammar.user_parameter_refs(),
Sep(", ", &args)
);
}
if production.nonterminal == self.start_symbol {
rust!(self.out, "return Some(Ok({}nt));", self.prefix);
return Ok(());
}
let name =
self.variant_name_for_symbol(&Symbol::Nonterminal(production.nonterminal.clone()));
rust!(
self.out,
"let {}symbol = ({}start, {}Symbol::{}({}nt), {}end);",
self.prefix,
self.prefix,
self.prefix,
name,
self.prefix,
self.prefix
);
let index = self.custom
.all_nonterminals
.iter()
.position(|x| *x == production.nonterminal)
.unwrap();
rust!(
self.out,
"({len}, {p}symbol, {index})",
p = self.prefix,
index = index,
len = production.symbols.len()
);
Ok(())
}
fn variant_name_for_symbol(&mut self, s: &Symbol) -> String {
self.custom.variant_names[s].clone()
}
fn emit_downcast_fns(&mut self) -> io::Result<()> {
for (ty, name) in self.custom.variants.clone() {
try!(self.emit_downcast_fn(&name, ty));
}
Ok(())
}
fn emit_downcast_fn(&mut self, variant_name: &str, variant_ty: TypeRepr) -> io::Result<()> {
let spanned_symbol_type = self.spanned_symbol_type();
rust!(self.out, "fn {}pop_{}<", self.prefix, variant_name);
for type_parameter in &self.custom.symbol_type_params {
rust!(self.out, " {},", type_parameter);
}
rust!(self.out, ">(");
rust!(
self.out,
"{}symbols: &mut ::std::vec::Vec<{}>",
self.prefix,
spanned_symbol_type
);
rust!(self.out, ") -> {}", self.types.spanned_type(variant_ty));
if !self.custom.symbol_where_clauses.is_empty() {
rust!(
self.out,
" where {}",
Sep(", ", &self.custom.symbol_where_clauses)
);
}
rust!(self.out, " {{");
if DEBUG_PRINT {
rust!(self.out, "println!(\"pop_{}\");", variant_name);
}
rust!(self.out, "match {}symbols.pop().unwrap() {{", self.prefix);
rust!(
self.out,
"({}l, {}Symbol::{}({}v), {}r) => ({}l, {}v, {}r),",
self.prefix,
self.prefix,
variant_name,
self.prefix,
self.prefix,
self.prefix,
self.prefix,
self.prefix
);
rust!(self.out, "_ => panic!(\"symbol type mismatch\")");
rust!(self.out, "}}");
rust!(self.out, "}}");
Ok(())
}
fn try_error_recovery(
&mut self,
tokens: &str,
states: &str,
symbols: &str,
last_location: &str,
opt_lookahead: Option<(&str, &str, &str, &str)>,
) -> io::Result<()> {
if let Some((out_lookahead, out_integer, _, _)) = opt_lookahead {
rust!(
self.out,
"let mut {p}err_lookahead = Some({p}{});",
out_lookahead,
p = self.prefix,
);
rust!(
self.out,
"let mut {p}err_integer: Option<usize> = Some({p}{});",
out_integer,
p = self.prefix,
);
} else {
rust!(
self.out,
"let mut {p}err_lookahead = None;",
p = self.prefix,
);
rust!(
self.out,
"let mut {p}err_integer: Option<usize> = None;",
p = self.prefix,
);
}
if !self.grammar.uses_error_recovery {
let prefix = self.prefix;
self.let_unrecognized_token_error("error", &format!("{p}err_lookahead", p = prefix))?;
rust!(self.out, "return Err({p}error)", p = prefix);
return Ok(());
}
let phantom_data_expr = self.phantom_data_expr();
rust!(
self.out,
"match {p}error_recovery(\
{upr} \
&mut {p}{tokens}, \
&mut {p}{states}, \
&mut {p}{symbols}, \
{p}{last_location}, \
&mut {p}err_lookahead, \
&mut {p}err_integer, \
{phantom_data_expr}) {{",
upr = self.grammar.user_parameter_refs(),
tokens = tokens,
states = states,
symbols = symbols,
last_location = last_location,
phantom_data_expr = phantom_data_expr,
p = self.prefix
);
rust!(self.out, "Err({p}e) => return Err({p}e),", p = self.prefix);
rust!(
self.out,
"Ok(Some({p}v)) => return Ok({p}v),",
p = self.prefix
);
rust!(self.out, "Ok(None) => (),");
rust!(self.out, "}}");
if let Some((out_lookahead, out_integer, tok_target, eof_target)) = opt_lookahead {
rust!(
self.out,
"match ({p}err_lookahead, {p}err_integer) {{",
p = self.prefix
);
rust!(self.out, "(Some({p}l), Some({p}i)) => {{", p = self.prefix);
rust!(self.out, "{p}{} = {p}l;", out_lookahead, p = self.prefix);
rust!(self.out, "{p}{} = {p}i;", out_integer, p = self.prefix);
rust!(self.out, "continue '{p}{};", tok_target, p = self.prefix);
rust!(self.out, "}}"); rust!(self.out, "_ => break '{p}{},", eof_target, p = self.prefix);
rust!(self.out, "}}"); }
Ok(())
}
fn write_error_recovery_fn(&mut self) -> io::Result<()> {
if !self.grammar.uses_error_recovery {
return Ok(());
}
let parse_error_type = self.types.parse_error_type();
let error_type = self.types.error_type();
let spanned_symbol_type = self.spanned_symbol_type();
let triple_type = self.types.triple_type();
let loc_type = self.types.terminal_loc_type();
let prefix = self.prefix;
let actions_per_state = self.grammar.terminals.all.len();
let start_type = self.types.nonterminal_type(&self.start_symbol);
let tok_error_type = if self.grammar.intern_token.is_some() {
parse_error_type
} else {
&error_type
};
let parameters = vec![
format!("{p}tokens: &mut {p}I", p = self.prefix),
format!(
"{p}states: &mut ::std::vec::Vec<{typ}>",
p = self.prefix,
typ = self.custom.state_type
),
format!(
"{p}symbols: &mut ::std::vec::Vec<{spanned_symbol_type}>",
spanned_symbol_type = spanned_symbol_type,
p = self.prefix
),
format!(
"{p}last_location: &mut {loc_type}",
loc_type = loc_type,
p = self.prefix
),
format!(
"{p}opt_lookahead: &mut Option<{triple_type}>",
triple_type = triple_type,
p = self.prefix
),
format!("{p}opt_integer: &mut Option<usize>", p = self.prefix),
format!("_: {}", self.phantom_data_type()),
];
try!(self.out.write_fn_header(
self.grammar,
&Visibility::Priv,
format!("{p}error_recovery", p = self.prefix),
vec![format!("{p}I", p = self.prefix)],
None,
parameters,
format!(
"Result<Option<{start_type}>, {parse_error_type}>",
start_type = start_type,
parse_error_type = parse_error_type
),
vec![
format!(
"{p}I: Iterator<Item = \
Result<{triple_type}, {tok_error_type}>\
>",
triple_type = triple_type,
tok_error_type = tok_error_type,
p = self.prefix
),
]
));
rust!(self.out, "{{");
self.let_unrecognized_token_error(
"error",
&format!("{p}opt_lookahead.clone()", p = prefix),
)?;
rust!(self.out, "let mut {}dropped_tokens = vec![];", prefix);
let phantom_data_expr = self.phantom_data_expr();
if DEBUG_PRINT {
rust!(
self.out,
"println!(\"Initiating error recovery in state: {{}}\", \
{p}states.last().unwrap());",
p = self.prefix
);
rust!(
self.out,
"println!(\" - state stack size: {{}}\", \
{p}states.len());",
p = self.prefix
);
rust!(
self.out,
"println!(\" - symbol stack size: {{}}\", \
{p}symbols.len());",
p = self.prefix
);
rust!(
self.out,
"println!(\" - opt lookahead: {{:?}}\", \
{p}opt_lookahead);",
p = self.prefix
);
rust!(
self.out,
"println!(\" - opt integer: {{:?}}\", \
{p}opt_integer);",
p = self.prefix
);
}
rust!(self.out, "loop {{");
rust!(
self.out,
"let {p}state = *{p}states.last().unwrap() as usize;",
p = self.prefix
);
rust!(
self.out,
"let {p}action = {p}ACTION[{p}state * {} + {}];",
actions_per_state,
actions_per_state - 1,
p = self.prefix
);
rust!(self.out, "if {p}action >= 0 {{", p = self.prefix);
rust!(self.out, "break;");
rust!(self.out, "}}");
if DEBUG_PRINT {
rust!(
self.out,
r#"println!("Error recovery reduces on action: {{}}", {}action);"#,
self.prefix
);
}
rust!(
self.out,
"let {p}lookahead_start = {p}opt_lookahead.as_ref().map(|l| &l.0);",
p = self.prefix
);
rust!(
self.out,
"if let Some(r) = {p}reduce( \
{upr} \
{p}action, \
{p}lookahead_start, \
{p}states, \
{p}symbols, \
{phantoms} \
) {{",
upr = self.grammar.user_parameter_refs(),
phantoms = phantom_data_expr,
p = self.prefix
);
rust!(self.out, "return Ok(Some(r?));");
rust!(self.out, "}}");
rust!(self.out, "}}");
rust!(
self.out,
"let {p}states_len = {p}states.len();",
p = self.prefix
);
rust!(self.out, "let {p}top0;", p = self.prefix);
rust!(self.out, "'{p}find_state: loop {{", p = self.prefix);
rust!(
self.out,
"for {p}top in (0..{p}states_len).rev() {{",
p = self.prefix
);
rust!(
self.out,
"let {p}state = {p}states[{p}top] as usize;",
p = self.prefix
);
if DEBUG_PRINT {
rust!(
self.out,
"println!(\"Probing recovery from state {{}} (top = {{}}).\", {p}state, {p}top);",
p = self.prefix,
);
}
rust!(
self.out,
"let {p}action = {p}ACTION[{p}state * {} + {}];",
actions_per_state,
actions_per_state - 1,
p = self.prefix
);
rust!(
self.out,
"if {p}action <= 0 {{ continue; }}",
p = self.prefix
);
rust!(
self.out,
"let {p}error_state = {p}action - 1;",
p = self.prefix
);
rust!(
self.out,
"if {p}accepts(\
{upr} \
{p}error_state, \
&{p}states[..{p}top + 1], \
*{p}opt_integer, \
{phantoms},\
) {{",
upr = self.grammar.user_parameter_refs(),
phantoms = phantom_data_expr,
p = self.prefix
);
rust!(self.out, "{p}top0 = {p}top;", p = self.prefix);
rust!(self.out, "break '{p}find_state;", p = self.prefix);
rust!(self.out, "}}"); rust!(self.out, "}}");
rust!(self.out, "'{p}eof: loop {{", p = self.prefix);
rust!(
self.out,
"match {p}opt_lookahead.take() {{",
p = self.prefix
);
rust!(self.out, "None => {{");
if DEBUG_PRINT {
rust!(
self.out,
r#"println!("Error recovery: cannot drop EOF; aborting");"#
);
}
rust!(self.out, "return Err({}error)", prefix);
rust!(self.out, "}}");
rust!(self.out, "Some(mut {p}lookahead) => {{", p = self.prefix);
if DEBUG_PRINT {
rust!(
self.out,
r#"println!("Error recovery: dropping token `{{:?}}`", {p}lookahead);"#,
p = self.prefix,
);
}
rust!(
self.out,
"{p}dropped_tokens.push({p}lookahead);",
p = self.prefix
);
self.next_token("lookahead", "tokens", "last_location", "eof")?;
rust!(self.out, "let {p}integer;", p = self.prefix);
try!(self.token_to_integer("integer", "lookahead"));
rust!(
self.out,
"*{p}opt_lookahead = Some({p}lookahead);",
p = self.prefix
);
rust!(
self.out,
"*{p}opt_integer = Some({p}integer);",
p = self.prefix
);
rust!(self.out, "continue '{p}find_state;", p = self.prefix);
rust!(self.out, "}}"); rust!(self.out, "}}"); rust!(self.out, "}}");
if DEBUG_PRINT {
rust!(
self.out,
"println!(\"Encountered EOF during error recovery\");"
);
}
rust!(self.out, "*{p}opt_lookahead = None;", p = self.prefix);
rust!(self.out, "*{p}opt_integer = None;", p = self.prefix);
rust!(self.out, "}};");
rust!(self.out, "let {p}top = {p}top0;", p = self.prefix);
rust!(
self.out,
"let {p}start = if let Some({p}popped_sym) = {p}symbols.get({p}top) {{",
p = self.prefix
);
if DEBUG_PRINT {
rust!(
self.out,
"println!(\"Span starts from popped symbol {{:?}}\", \
(&{p}popped_sym.0 .. &{p}popped_sym.2));",
p = self.prefix,
);
}
rust!(self.out, "{p}popped_sym.0.clone()", p = self.prefix);
rust!(
self.out,
"}} else if let Some({p}dropped_token) = {p}dropped_tokens.first() {{",
p = self.prefix
);
if DEBUG_PRINT {
rust!(
self.out,
"println!(\"Span starts from dropped token {{:?}}\", \
(&{p}dropped_token.0 .. &{p}dropped_token.2));",
p = self.prefix,
);
}
rust!(self.out, "{p}dropped_token.0.clone()", p = self.prefix);
rust!(self.out, "}} else if {p}top > 0 {{", p = self.prefix);
if DEBUG_PRINT {
rust!(
self.out,
"println!(\"Span starts from end of last retained symbol\");"
);
}
rust!(
self.out,
"{p}symbols[{p}top - 1].2.clone()",
p = self.prefix
);
rust!(self.out, "}} else {{");
if DEBUG_PRINT {
rust!(self.out, "println!(\"Span starts from default\");");
}
rust!(self.out, "Default::default()");
rust!(self.out, "}};");
rust!(
self.out,
"let {p}end = if let Some({p}dropped_token) = {p}dropped_tokens.last() {{",
p = self.prefix
);
if DEBUG_PRINT {
rust!(
self.out,
"println!(\"Span ends at end of last dropped token {{:?}}\", \
(&{p}dropped_token.0 .. &{p}dropped_token.2));",
p = self.prefix,
);
}
rust!(self.out, "{p}dropped_token.2.clone()", p = self.prefix);
rust!(
self.out,
"}} else if {p}states_len - 1 > {p}top {{",
p = self.prefix
);
if DEBUG_PRINT {
rust!(
self.out,
"println!(\"Span ends at end of last popped symbol {{:?}}\", \
{p}symbols.last().unwrap().2);",
p = self.prefix,
);
}
rust!(
self.out,
"{p}symbols.last().unwrap().2.clone()",
p = self.prefix
);
rust!(
self.out,
"}} else if let Some({p}lookahead) = {p}opt_lookahead.as_ref() {{",
p = self.prefix
);
if DEBUG_PRINT {
rust!(self.out, "println!(\"Span ends at start of lookahead\");");
}
rust!(self.out, "{p}lookahead.0.clone()", p = self.prefix);
rust!(self.out, "}} else {{");
if DEBUG_PRINT {
rust!(self.out, "println!(\"Span ends at start\");");
}
rust!(self.out, "{p}start.clone()", p = self.prefix);
rust!(self.out, "}};");
rust!(self.out, "{p}states.truncate({p}top + 1);", p = self.prefix);
rust!(self.out, "{p}symbols.truncate({p}top);", p = self.prefix);
rust!(
self.out,
"let {p}recover_state = {p}states[{p}top] as usize;",
p = self.prefix
);
rust!(
self.out,
"let {p}error_action = {p}ACTION[{p}recover_state * {} + {}];",
actions_per_state,
actions_per_state - 1,
p = self.prefix
);
rust!(
self.out,
"let {p}error_state = {p}error_action - 1;",
p = self.prefix
);
if DEBUG_PRINT {
rust!(self.out, "println!(\"Recovering from error:\");");
rust!(
self.out,
"println!(\" - recovery base state: {{}}\", {p}top);",
p = self.prefix
);
rust!(
self.out,
"println!(\" - new top state {{}}\", {p}recover_state);",
p = self.prefix
);
rust!(
self.out,
"println!(\" - error state {{}}\", {p}error_state);",
p = self.prefix
);
rust!(
self.out,
"println!(\" - new stack length: {{}}\", {p}states.len());",
p = self.prefix
);
rust!(
self.out,
"println!(\" - new symbol length: {{}}\", {p}symbols.len());",
p = self.prefix
);
rust!(
self.out,
"println!(\" - span {{:?}}..{{:?}}\", {p}start, {p}end);",
p = self.prefix
);
}
rust!(self.out, "{p}states.push({p}error_state);", p = self.prefix);
rust!(
self.out,
"let {p}recovery = {}lalrpop_util::ErrorRecovery {{",
p = self.prefix
);
rust!(self.out, "error: {p}error,", p = self.prefix);
rust!(
self.out,
"dropped_tokens: {p}dropped_tokens,",
p = self.prefix
);
rust!(self.out, "}};");
let error_variant = self.variant_name_for_symbol(&Symbol::Terminal(TerminalString::Error));
rust!(
self.out,
"{p}symbols.push(({p}start, {p}Symbol::{e}({p}recovery), {p}end));",
p = self.prefix,
e = error_variant
);
rust!(self.out, "Ok(None)");
rust!(self.out, "}}"); Ok(())
}
fn write_accepts_fn(&mut self) -> io::Result<()> {
if !self.grammar.uses_error_recovery {
return Ok(());
}
let actions_per_state = self.grammar.terminals.all.len();
let parameters = vec![
format!(
"{p}error_state: {typ}",
p = self.prefix,
typ = self.custom.state_type
),
format!(
"{p}states: & [{typ}]",
p = self.prefix,
typ = self.custom.state_type
),
format!("{p}opt_integer: Option<usize>", p = self.prefix),
format!("_: {}", self.phantom_data_type()),
];
try!(self.out.write_fn_header(
self.grammar,
&Visibility::Priv,
format!("{}accepts", self.prefix),
vec![],
None,
parameters,
format!("bool"),
vec![]
));
rust!(self.out, "{{");
if DEBUG_PRINT {
rust!(
self.out,
"println!(\"Testing whether state {{}} accepts token {{:?}}\", \
{p}error_state, {p}opt_integer);",
p = self.prefix
);
}
rust!(
self.out,
"let mut {p}states = {p}states.to_vec();",
p = self.prefix
);
rust!(self.out, "{p}states.push({p}error_state);", p = self.prefix);
rust!(self.out, "loop {{",);
rust!(
self.out,
"let mut {}states_len = {}states.len();",
self.prefix,
self.prefix
);
rust!(
self.out,
"let {p}top = {p}states[{p}states_len - 1] as usize;",
p = self.prefix
);
if DEBUG_PRINT {
rust!(
self.out,
"println!(\"accepts: top-state={{}} num-states={{}}\", {p}top, {p}states_len);",
p = self.prefix
);
}
rust!(
self.out,
"let {p}action = match {p}opt_integer {{",
p = self.prefix
);
rust!(
self.out,
"None => {p}EOF_ACTION[{p}top as usize],",
p = self.prefix
);
rust!(
self.out,
"Some({p}integer) => {p}ACTION[{p}top * {actions_per_state} + {p}integer],",
p = self.prefix,
actions_per_state = actions_per_state,
);
rust!(self.out, "}};");
rust!(
self.out,
"if {p}action == 0 {{ return false; }}",
p = self.prefix
);
rust!(
self.out,
"if {p}action > 0 {{ return true; }}",
p = self.prefix
);
rust!(
self.out,
"let ({p}to_pop, {p}nt) = match -{p}action {{",
p = self.prefix
);
for (production, index) in self.grammar
.nonterminals
.values()
.flat_map(|nt| &nt.productions)
.zip(1..)
{
if Tls::session().emit_comments {
rust!(self.out, "// simulate {:?}", production);
}
if production.nonterminal == self.start_symbol {
rust!(self.out, "{} => return true,", index);
} else {
let num_symbols = production.symbols.len();
let nt = self.custom
.all_nonterminals
.iter()
.position(|x| *x == production.nonterminal)
.unwrap();
rust!(self.out, "{} => {{", index);
if DEBUG_PRINT {
rust!(
self.out,
"println!(r##\"accepts: simulating {:?}\"##);",
production
);
}
rust!(
self.out,
"({num_symbols}, {nt})",
num_symbols = num_symbols,
nt = nt
);
rust!(self.out, "}}");
}
}
rust!(
self.out,
"_ => panic!(\"invalid action code {{}}\", {}action)",
self.prefix
);
rust!(self.out, "}};");
rust!(self.out, "{p}states_len -= {p}to_pop;", p = self.prefix);
rust!(
self.out,
"{p}states.truncate({p}states_len);",
p = self.prefix
);
rust!(
self.out,
"let {p}top = {p}states[{p}states_len - 1] as usize;",
p = self.prefix
);
if DEBUG_PRINT {
rust!(
self.out,
"println!(\"accepts: popped {{}} symbols, new top is {{}}, nt is {{}}\", \
{p}to_pop, \
{p}top, \
{p}nt, \
);",
p = self.prefix
);
}
rust!(
self.out,
"let {p}next_state = {p}GOTO[{p}top * {num_non_terminals} + {p}nt] - 1;",
p = self.prefix,
num_non_terminals = self.grammar.nonterminals.len(),
);
rust!(self.out, "{p}states.push({p}next_state);", p = self.prefix);
rust!(self.out, "}}"); rust!(self.out, "}}");
Ok(())
}
fn symbol_type(&self) -> String {
format!(
"{}Symbol<{}>",
self.prefix,
Sep(", ", &self.custom.symbol_type_params)
)
}
fn spanned_symbol_type(&self) -> String {
let loc_type = self.types.terminal_loc_type();
format!("({},{},{})", loc_type, self.symbol_type(), loc_type)
}
fn let_unrecognized_token_error(&mut self, error_var: &str, token: &str) -> io::Result<()> {
rust!(
self.out,
"let {}state = *{}states.last().unwrap() as usize;",
self.prefix,
self.prefix
);
rust!(
self.out,
"let {}{} = {}lalrpop_util::ParseError::UnrecognizedToken {{",
self.prefix,
error_var,
self.prefix
);
rust!(self.out, "token: {},", token);
rust!(
self.out,
"expected: {}expected_tokens({}state),",
self.prefix,
self.prefix
);
rust!(self.out, "}};");
Ok(())
}
fn emit_expected_tokens_fn(&mut self) -> io::Result<()> {
rust!(
self.out,
"fn {}expected_tokens({}state: usize) -> Vec<::std::string::String> {{",
self.prefix,
self.prefix
);
rust!(
self.out,
"const {}TERMINAL: &'static [&'static str] = &[",
self.prefix
);
let all_terminals = if self.grammar.uses_error_recovery {
&self.grammar.terminals.all[..self.grammar.terminals.all.len() - 1]
} else {
&self.grammar.terminals.all
};
for terminal in all_terminals {
rust!(self.out, "r###\"{}\"###,", terminal);
}
rust!(self.out, "];");
rust!(
self.out,
"{}ACTION[({}state * {})..].iter().zip({}TERMINAL).filter_map(|(&state, terminal)| {{",
self.prefix,
self.prefix,
self.grammar.terminals.all.len(),
self.prefix
);
rust!(self.out, "if state == 0 {{");
rust!(self.out, "None");
rust!(self.out, "}} else {{");
rust!(self.out, "Some(terminal.to_string())");
rust!(self.out, "}}");
rust!(self.out, "}}).collect()");
rust!(self.out, "}}");
Ok(())
}
}