use crate::grammar::Grammar;
use crate::lalr::GotoMap;
use crate::mkpar::{ActionCode, YaccParser};
use crate::util::fill_copy;
use crate::{Rule, State, StateOrRule};
use log::debug;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use std::cmp;
use std::i16;
use std::iter::repeat;
use syn::{Block, Ident, Type};
pub(crate) fn output_parser_to_token_stream(
gram: &Grammar,
gotos: &GotoMap,
parser: &YaccParser,
blocks: &[Option<Block>],
rhs_bindings: &[Option<Ident>],
context_ty: Type, context_param_ident: Ident, symbol_value_ty: Type, ) -> TokenStream {
assert!(blocks.len() == gram.nrules);
let grammar_span = Span::call_site();
let mut items: TokenStream = TokenStream::new();
let default_reductions = crate::mkpar::default_reductions(&parser.actions);
let yydefred: Vec<i16> = default_reductions
.iter()
.map(|&s| if s != Rule(0) { s.0 - 2 } else { 0 })
.collect();
items.extend(make_table_i16(
Ident::new("YYDEFRED", grammar_span),
&yydefred,
));
items.extend(output_actions(
grammar_span,
gram,
gotos,
parser,
&default_reductions,
));
for t in 1..gram.ntokens {
let tokvalue = gram.value[t] as u16;
let tok_ident = &gram.name[t];
items.extend(quote!(pub const #tok_ident: u16 = #tokvalue;));
}
let yyfinal = parser.final_state.0 as u16;
items.extend(quote! {
const YYFINAL: u16 = #yyfinal;
});
let mut action_arms: TokenStream = TokenStream::new();
for (rule_i, block) in blocks.iter().enumerate() {
if rule_i < 3 {
continue;
}
let rule = Rule(rule_i as i16);
let stmts: TokenStream = match block {
Some(block) => {
let rhs_index = gram.rrhs(rule).index();
let num_rhs = gram.get_rhs_items(rule).len();
let mut t = TokenStream::new();
for rhs_binding in rhs_bindings[rhs_index..rhs_index + num_rhs].iter() {
t.extend(match rhs_binding {
Some(ref rbind) => {
quote! {
let #rbind = values.next().unwrap();
}
}
None => {
quote! {
values.next().unwrap();
}
}
})
}
t.extend(quote! {#block});
t
}
None => {
TokenStream::new()
}
};
let pat_value: u16 = rule_i as u16 - 2;
action_arms.extend(quote! {
#pat_value => {
#stmts
}
});
}
items.extend(quote! {
#[allow(unused_braces)]
fn reduce_actions<I: Iterator<Item = #symbol_value_ty>>(
mut values: I,
reduction: u16,
#context_param_ident: &mut #context_ty) -> Result<#symbol_value_ty, racc_runtime::Error> {
Ok(match reduction {
#action_arms
_ => unreachable!()
})
}
});
items.extend(output_rule_data(gram));
items.extend(make_table_i16(
Ident::new("YYLEN", grammar_span),
&gram
.iter_rules()
.skip(2)
.map(|r| gram.rule_rhs_syms(r).len() as i16)
.collect::<Vec<i16>>(),
));
items.extend(make_symbol_names_table(grammar_span, gram));
items.extend(make_rule_text_table(grammar_span, gram));
items.extend(make_states_text_table(grammar_span, gram, parser));
items.extend(output_gen_methods(symbol_value_ty, context_ty));
items
}
fn output_gen_methods(symbol_value_ty: Type, context_ty: Type) -> TokenStream {
quote! {
struct ParserState {
yystate: u16,
value_stack: Vec<#symbol_value_ty>,
state_stack: Vec<u16>,
}
const INITIAL_STATE: u16 = 0;
impl Default for ParserState {
fn default() -> ParserState {
ParserState {
yystate: INITIAL_STATE,
value_stack: Vec::new(),
state_stack: {
let mut v = Vec::with_capacity(20);
v.push(INITIAL_STATE);
v
},
}
}
}
impl ParserState {
pub fn new() -> Self {
Self::default()
}
pub fn reset(&mut self) {
self.yystate = INITIAL_STATE;
self.value_stack.clear();
self.state_stack.clear();
self.state_stack.push(INITIAL_STATE);
}
fn yyreduce(&mut self, reduction: u16, ctx: &mut #context_ty)
-> Result<(), racc_runtime::Error>
{
let len = YYLEN[reduction as usize] as usize;
log::debug!(
"state {} reducing by rule {}, len={}",
self.yystate, YYRULES[reduction as usize], len
);
assert!(self.value_stack.len() >= len);
assert!(self.state_stack.len() >= len);
let old_values_len = self.value_stack.len();
let reduce_value = reduce_actions(
self.value_stack.drain(self.value_stack.len() - len..), reduction, ctx)?;
assert!(self.value_stack.len() + len == old_values_len);
log::debug!(
" generated code popped {} values from value stack, new len = {}",
old_values_len,
self.value_stack.len()
);
log::debug!(" after pushing the result of the reduction, value_stack.len = {}, reduce_value={:?}",
self.value_stack.len() + 1, reduce_value);
self.value_stack.push(reduce_value);
self.state_stack.drain(self.state_stack.len() - len..);
let top_state = *self.state_stack.last().unwrap();
self.yystate = top_state;
let lhs = YYLHS[reduction as usize];
if top_state == 0 && lhs == 0 {
log::debug!(
" after reduction, shifting from state 0 to state {} (0/0 case!)",
YYFINAL
);
self.yystate = YYFINAL;
self.state_stack.push(YYFINAL);
} else {
let yyn_0 = YYGINDEX[lhs as usize];
let yyn_1 = yyn_0 + self.yystate;
let next_state: u16 =
if YYCHECK[yyn_1 as usize] == self.yystate {
YYTABLE[yyn_1 as usize]
} else {
YYDGOTO[lhs as usize]
};
log::debug!(
" after reduction, shifting from state {} to state {}",
self.yystate, next_state
);
self.yystate = next_state;
self.state_stack.push(next_state);
}
Ok(())
}
fn do_defreds(&mut self, ctx: &mut #context_ty) -> Result<bool, racc_runtime::Error> {
let mut any = false;
loop {
let defred = YYDEFRED[self.yystate as usize];
if defred != 0 {
self.yyreduce(defred, ctx)?;
any = true;
} else {
return Ok(any);
}
}
}
fn try_shift(&mut self, token: u16, lval: #symbol_value_ty) -> bool {
log::debug!("try_shift: yystate={} token={}", self.yystate, token);
let shift: i16 = YYSINDEX[self.yystate as usize];
if shift != 0 {
log::debug!("try_shift: shift={}", shift);
let yyn = shift as i32 + (token as i32);
assert!(yyn >= 0);
assert!(YYCHECK[yyn as usize] == token,
"yyn = {}, YYCHECK[yyn] = {}, token = {}", yyn, YYCHECK[yyn as usize], token);
let next_state = YYTABLE[yyn as usize];
log::debug!(
"state {}, shifting to state {}, pushing lval {:?}",
self.yystate, next_state, lval
);
self.yystate = next_state;
self.state_stack.push(self.yystate);
self.value_stack.push(lval); true
} else {
false
}
}
fn try_reduce(&mut self, ctx: &mut #context_ty, token: u16)
-> Result<bool, racc_runtime::Error>
{
let red = YYRINDEX[self.yystate as usize];
if red != 0 {
let yyn = red + (token as u16);
log::debug!(" yyn={}", yyn);
assert!(YYCHECK[yyn as usize] == token);
log::debug!(" reducing by {}", red);
let rr = YYTABLE[yyn as usize];
self.yyreduce(rr, ctx)?;
Ok(true)
} else {
Ok(false)
}
}
pub fn push_token(
&mut self,
ctx: &mut #context_ty,
token: u16,
lval: #symbol_value_ty,
) -> Result<(), racc_runtime::Error> {
assert!(!self.state_stack.is_empty());
log::debug!(
"state {}, reading {} ({}) lval {:?}, state_stack = {:?}",
self.yystate, token, YYNAME[token as usize], lval, self.state_stack
);
log::debug!("state: {}", YYSTATE_TEXT[self.yystate as usize]);
log::debug!("value_stack = {:?}", self.value_stack);
if self.try_shift(token, lval) {
self.do_defreds(ctx)?;
return Ok(());
}
if self.try_reduce(ctx, token)? {
self.do_defreds(ctx)?;
return Ok(());
}
log::debug!("syntax error! token is not recognized in this state.");
Err(racc_runtime::Error::SyntaxError)
}
pub fn finish(&mut self, ctx: &mut #context_ty)
-> Result<#symbol_value_ty, racc_runtime::Error>
{
assert!(!self.state_stack.is_empty());
log::debug!(
"finish: yystate={:?} state_stack = {:?}",
self.yystate, self.state_stack
);
self.try_reduce(ctx, 0)?;
self.do_defreds(ctx)?;
if self.value_stack.len() == 1 {
log::debug!("accept");
let final_lval = self.value_stack.pop().unwrap();
return Ok(final_lval);
}
log::debug!(
"done with all reductions. yystate={:?} state_stack={:?}",
self.yystate, self.state_stack
);
log::debug!("syntax error! token is not recognized in this state.");
Err(racc_runtime::Error::SyntaxError)
}
}
}
}
fn output_rule_data(gram: &Grammar) -> TokenStream {
let mut data: Vec<i16> = Vec::new();
data.push(gram.value[gram.start().index()]);
for i in 3..gram.nrules {
data.push(gram.value[gram.rlhs[i].0 as usize]);
}
make_table_i16(Ident::new("YYLHS", Span::call_site()), &data)
}
fn make_symbol_names_table(span: Span, gram: &Grammar) -> TokenStream {
let mut max_value: i16 = i16::MIN;
for i in 0..gram.ntokens {
max_value = cmp::max(max_value, gram.value[i]);
}
assert!(max_value >= 0);
assert!(max_value < i16::MAX);
let length = (max_value + 1) as usize;
let mut toknames: Vec<String> = vec![String::new(); length];
for (value, name) in gram.value[0..gram.ntokens]
.iter()
.zip(gram.name[0..gram.ntokens].iter())
{
toknames[*value as usize] = name.to_string();
}
make_table_string(Ident::new("YYNAME", span), &toknames)
}
fn make_table_string(name: Ident, strings: &[String]) -> TokenStream {
let strings_len = strings.len();
let strings: Vec<syn::LitStr> = strings
.iter()
.map(|s| syn::LitStr::new(s, name.span()))
.collect();
quote! {
static #name: [&str; #strings_len] = [
#( #strings ),*
];
}
}
fn make_rule_text_table(span: Span, gram: &Grammar) -> TokenStream {
let rules: Vec<String> = (2..gram.nrules)
.map(|rule| gram.rule_to_str(Rule(rule as i16)))
.collect();
make_table_string(Ident::new("YYRULES", span), &rules)
}
fn make_states_text_table(span: Span, gram: &Grammar, parser: &YaccParser) -> TokenStream {
let ss: Vec<String> = parser
.actions
.iter()
.enumerate()
.map(|(state, actions)| {
use std::fmt::Write;
let mut s = String::new();
for action in actions.iter() {
write!(
s,
"s{} : {} -> ",
state,
gram.name(action.symbol.to_symbol())
)
.unwrap();
match action.action_code {
ActionCode::Shift(to_state) => {
write!(s, "shift to s{}\n", to_state).unwrap();
}
ActionCode::Reduce(rule) => {
write!(s, "reduce {}\n", gram.rule_to_str(rule)).unwrap();
}
}
}
return s;
})
.collect::<Vec<String>>();
make_table_string(Ident::new("YYSTATE_TEXT", span), &ss)
}
fn make_table_i16(name: Ident, values: &[i16]) -> TokenStream {
make_table_i16_as_u16(name, values)
}
fn make_table_i16_signed(name: Ident, values: &[i16]) -> TokenStream {
let values_len = values.len();
quote! {
static #name: [i16; #values_len] = [
#(
#values
),*
];
}
}
fn make_table_i16_as_u16(name: Ident, values: &[i16]) -> TokenStream {
let u_values: Vec<u16> = values.iter().map(|&value| value as u16).collect();
let values_len = u_values.len();
quote! {
static #name: [u16; #values_len] = [
#(
#u_values
),*
];
}
}
fn output_actions(
span: Span,
gram: &Grammar,
gotos: &GotoMap,
parser: &YaccParser,
default_reductions: &[Rule],
) -> TokenStream {
let nstates = parser.nstates();
let mut act = ActionsTable::new(nstates, gram.nvars);
token_actions(
gram,
parser,
&default_reductions,
&mut act.froms,
&mut act.tos,
);
let default_goto_table = default_goto_table(nstates, gotos);
goto_actions(
gram,
nstates,
gotos,
&default_goto_table,
&mut act.froms,
&mut act.tos,
);
let order = sort_actions(&mut act);
let packed = packing::pack_table(parser.nstates(), &order, &act);
let mut items = TokenStream::new();
items.extend(make_table_i16(
Ident::new("YYDGOTO", span),
&default_goto_table.iter().map(|s| s.0).collect::<Vec<i16>>(),
));
items.extend(make_table_i16_signed(
Ident::new("YYSINDEX", span),
&packed.base[..nstates],
));
items.extend(make_table_i16(
Ident::new("YYRINDEX", span),
&packed.base[nstates..nstates * 2],
));
items.extend(make_table_i16(
Ident::new("YYGINDEX", span),
&packed.base[nstates * 2..act.nvectors],
));
items.extend(make_table_i16(Ident::new("YYTABLE", span), &packed.table));
items.extend(make_table_i16(Ident::new("YYCHECK", span), &packed.check));
items
}
pub struct ActionsTable {
nvectors: usize,
froms: Vec<Vec<StateOrRule>>,
tos: Vec<Vec<StateOrRule>>,
}
impl ActionsTable {
pub fn new(nstates: usize, nvars: usize) -> Self {
let nvectors = 2 * nstates + nvars;
Self {
nvectors,
froms: Vec::new(),
tos: Vec::new(),
}
}
pub fn tally(&self, i: usize) -> usize {
self.froms[i].len()
}
pub fn width(&self, i: usize) -> i16 {
let f = &self.froms[i];
if f.len() != 0 {
f[f.len() - 1] - f[0] + 1
} else {
0
}
}
}
fn token_actions(
gram: &Grammar,
parser: &YaccParser,
default_reductions: &[Rule],
froms: &mut Vec<Vec<i16>>,
tos: &mut Vec<Vec<StateOrRule>>,
) {
for actions in parser.actions.iter() {
let mut shift_r: Vec<i16> = Vec::new();
let mut shift_s: Vec<i16> = Vec::new();
for action in actions.iter() {
if action.suppressed == 0 {
match action.action_code {
ActionCode::Shift(shift_to_state) => {
let token = action.symbol.index();
shift_r.push(gram.value[token]);
shift_s.push(shift_to_state.0);
}
ActionCode::Reduce(_) => {}
}
}
}
froms.push(shift_r);
tos.push(shift_s);
}
for (state, actions) in parser.actions.iter().enumerate() {
let mut reduce_r: Vec<i16> = Vec::new();
let mut reduce_s: Vec<i16> = Vec::new();
for action in actions.iter() {
if action.suppressed == 0 {
match action.action_code {
ActionCode::Reduce(reduce_rule) => {
if reduce_rule != default_reductions[state] {
let token = action.symbol.index();
reduce_r.push(gram.value[token]);
reduce_s.push(reduce_rule.0 - 2);
}
}
ActionCode::Shift(_) => {}
}
}
}
froms.push(reduce_r);
tos.push(reduce_s);
}
}
fn goto_actions(
gram: &Grammar,
nstates: usize,
gotos: &GotoMap,
default_goto_table: &[State],
froms: &mut Vec<Vec<i16>>,
tos: &mut Vec<Vec<StateOrRule>>,
) {
debug!("goto_actions:");
let nvars = gotos.len();
assert_eq!(nvars, gram.nvars);
froms.extend(repeat(Vec::new()).take(nvars));
tos.extend(repeat(Vec::new()).take(nvars));
let goto_froms = &mut froms[nstates * 2..];
let goto_tos = &mut tos[nstates * 2..];
for var in gram.iter_vars() {
if var.index() == 0 {
continue;
}
let symbol = gram.var_to_symbol(var);
let default_state = default_goto_table[var.index()];
let mut spf = Vec::new();
let mut spt = Vec::new();
for &entry in &gotos[var.index()] {
if entry.to_state != default_state {
spf.push(entry.from_state.0);
spt.push(entry.to_state.0);
}
}
let col = gram.value[symbol.index()] as usize;
goto_froms[col] = spf;
goto_tos[col] = spt;
}
}
fn default_goto_table(nstates: usize, gotos: &GotoMap) -> Vec<State> {
let mut state_count: Vec<i16> = vec![0; nstates]; gotos
.iter()
.map(move |var_gotos| {
if var_gotos.is_empty() {
State(0)
} else {
fill_copy(&mut state_count, 0);
for &entry in var_gotos.iter() {
state_count[entry.to_state.0 as usize] += 1;
}
let mut max = 0;
let mut default_state = 0;
for (state, &count) in state_count.iter().enumerate() {
if count > max {
max = count;
default_state = state;
}
}
State(default_state as i16)
}
})
.collect()
}
fn sort_actions(act: &ActionsTable) -> Vec<usize> {
use std::cmp::Ordering;
let mut order: Vec<usize> = Vec::with_capacity(act.nvectors);
for col in 0..act.nvectors {
let t = act.tally(col);
if t > 0 {
order.push(col);
}
}
order.sort_by(|&a, &b| match act.width(b).cmp(&act.width(a)) {
Ordering::Equal => act.tally(b).cmp(&act.tally(a)),
c => c,
});
order
}
mod packing {
use super::ActionsTable;
use log::debug;
fn matching_vector(pack: &PackState<'_>, vector: usize) -> Option<usize> {
let i = pack.order[vector];
if i >= 2 * pack.nstates {
return None;
}
let act = pack.act;
let t = act.tally(i);
let w = act.width(i);
for &j in pack.order[0..vector].iter().rev() {
if act.width(j) != w || act.tally(j) != t {
return None;
}
if act.tos[i] == act.tos[j] && act.froms[i] == act.froms[j] {
return Some(j);
}
}
None
}
fn pack_vector(pack: &mut PackState<'_>, vector: usize) -> i16 {
let act = pack.act;
let i = pack.order[vector];
let from = &act.froms[i];
let to = &act.tos[i];
let t = from.len();
assert!(t != 0);
let mut j: i16 = pack.lowzero - from[0];
for &f in from[1..t].iter() {
if pack.lowzero - f > j {
j = pack.lowzero - f;
}
}
loop {
if j == 0 {
j = 1;
continue;
}
let mut ok = true;
for &f in &from[0..t] {
let loc = (j + f) as usize;
if loc >= pack.table.len() {
assert!(pack.table.len() == pack.check.len());
pack.table.resize(loc + 1, 0);
pack.check.resize(loc + 1, -1);
}
if pack.check[loc] != -1 {
ok = false;
break;
}
}
if !ok {
j += 1;
continue;
}
if pack.pos[0..vector].iter().any(|&p| p == j) {
j += 1;
continue;
}
for (&f, &t) in from[0..t].iter().zip(to[0..t].iter()) {
let loc = (j + f) as usize;
pack.table[loc] = t;
pack.check[loc] = f;
if loc > pack.high {
pack.high = loc;
}
}
while pack.check[pack.lowzero as usize] != -1 {
pack.lowzero += 1;
}
return j as i16;
}
}
struct PackState<'a> {
base: Vec<i16>,
pos: Vec<i16>,
table: Vec<i16>, check: Vec<i16>, lowzero: i16,
high: usize,
order: &'a [usize],
nstates: usize,
act: &'a ActionsTable,
}
pub struct PackedTables {
pub base: Vec<i16>,
pub table: Vec<i16>,
pub check: Vec<i16>,
pub high: usize,
}
pub fn pack_table(nstates: usize, order: &[usize], act: &ActionsTable) -> PackedTables {
debug!("pack_table: nentries={}", order.len());
let initial_maxtable = 1000;
let mut pack = PackState {
base: vec![0; act.nvectors],
pos: vec![0; order.len()],
table: vec![0; initial_maxtable],
check: vec![-1; initial_maxtable],
lowzero: 0,
high: 0,
order: order,
nstates: nstates,
act: act,
};
for i in 0..order.len() {
let place = match matching_vector(&pack, i) {
Some(state) => pack.base[state],
None => pack_vector(&mut pack, i),
};
pack.pos[i] = place;
pack.base[order[i]] = place;
}
pack.check.truncate(pack.high + 1);
pack.table.truncate(pack.high + 1);
PackedTables {
base: pack.base,
table: pack.table,
check: pack.check,
high: pack.high,
}
}
}