mod lempar;
use lempar::LemparReader;
use std::mem;
use std::cmp;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::cell::RefCell;
use std::io::{self, BufRead, BufReader};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::collections::hash_map::DefaultHasher;
use std::error::Error;
use std::iter::Take;
use std::slice::Iter;
use std::convert::TryFrom;
use std::rc::Rc;
use std::sync::Arc;
use std::fs::File;
use std::borrow::Cow;
fn typename_to_string(value: String) -> String
{ let value_trimmed = value.trim();
if value_trimmed.is_empty() || value.starts_with('(') && value.ends_with(')') && value[1 .. value.len()-1].trim().is_empty()
{ String::new()
}
else
{ if value.len() == value_trimmed.len() {value} else {value_trimmed.to_string()}
}
}
fn is_terminal_name(s: &str) -> bool
{ s.chars().position(|c| c.is_ascii_lowercase()).is_none()
}
#[derive(Debug)]
pub struct LemonMintError
{ pub filename: Arc<String>,
pub n_line: usize,
pub message: String,
}
impl LemonMintError
{ pub fn new(filename: &Arc<String>, n_line: usize, message: String) -> Self
{ Self {filename: Arc::clone(filename), n_line, message}
}
}
impl fmt::Display for LemonMintError
{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{ let message: &str = if self.message.is_empty() {"Unspecified error"} else {&self.message};
if self.filename.is_empty()
{ write!(f, "{}", message)
}
else
{ write!(f, "{}({}): {}", self.filename, self.n_line, message)
}
}
}
impl Error for LemonMintError {}
type ParserResult<T> = Result<T, LemonMintError>;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum Directive
{ Rule,
TokenType,
Type,
DefaultType,
StartSymbol,
Trace,
ExtraArgument,
Left,
Right,
Nonassoc,
Fallback,
Code,
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum SymbolType
{ TERMINAL,
NONTERMINAL
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum Associativity
{ LEFT,
RIGHT,
NONASSOC,
UNKNOWN
}
#[derive(Debug)]
struct Set
{ data: Vec<bool>,
}
impl Set
{ pub fn new(size: usize) -> Self
{ let mut this = Self {data: Vec::new()};
if size > 0
{ this.set_size(size);
}
this
}
pub fn len(&self) -> usize
{ self.data.len()
}
pub fn set_size(&mut self, size: usize)
{ self.data.clear();
self.data.resize(size, false);
}
pub fn contains(&self, element: usize) -> bool
{ *self.data.get(element).unwrap_or(&false)
}
pub fn add(&mut self, element: usize) -> bool
{ let rv = self.contains(element);
if let Some(v) = self.data.get_mut(element)
{ *v = true;
}
!rv
}
pub fn intersect(&mut self, other: &Set) -> bool
{ let mut changed = false;
for (i, v) in &mut self.data.iter_mut().enumerate()
{ if !*v && other.contains(i)
{ changed = true;
*v = true;
}
}
return changed;
}
}
#[derive(Debug)]
struct Symbol
{ name: Rc<String>,
index: usize,
typ: SymbolType,
sym_rules_arr: Vec<usize>,
fallback: Option<Rc<RefCell<Symbol>>>,
prec: i32,
assoc: Associativity,
firstset: Set,
lambda: bool,
data_type: String,
dtnum: usize,
}
impl Symbol
{ fn new(name: &str, index: usize) -> Self
{ let typ = if is_terminal_name(name) || name=="$" {SymbolType::TERMINAL} else {SymbolType::NONTERMINAL};
Self
{ name: Rc::new(String::new()),
index,
typ,
sym_rules_arr: Vec::new(),
fallback: None,
prec: -1,
assoc: Associativity::UNKNOWN,
firstset: Set::new(0),
lambda: false,
data_type: String::new(),
dtnum: 0,
}
}
}
impl PartialEq for Symbol
{ fn eq(&self, other: &Self) -> bool
{ self.index == other.index
}
}
#[derive(Debug)]
struct Rule
{ rule_filename: Arc<String>,
rule_n_line: usize,
lhs: Rc<RefCell<Symbol>>,
lhs_start: bool,
rhs: Vec<Rc<RefCell<Symbol>>>,
rhs_aliases: Vec<String>,
line: usize,
code: String,
precsym: Option<Rc<RefCell<Symbol>>>,
index: usize,
can_reduce: bool,
does_reduce: bool,
}
impl Rule
{ fn new(rule_filename: &Arc<String>, rule_n_line: usize, lhs: &Rc<RefCell<Symbol>>, index: usize, code: String) -> Self
{ Self
{ rule_filename: Arc::clone(rule_filename),
rule_n_line,
lhs: Rc::clone(lhs),
lhs_start: false,
rhs: Vec::new(),
rhs_aliases: Vec::new(),
line: 0,
code,
precsym: None,
index,
can_reduce: false,
does_reduce: false,
}
}
}
impl fmt::Display for Rule
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{ write!(f, "{} ::=", self.lhs.borrow().name)?;
for sp in self.rhs.iter()
{ write!(f, " {}", sp.borrow().name)?;
}
Ok(())
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
struct ConfigKey
{ n_rule: usize,
dot: usize,
}
impl ConfigKey
{ fn new(config: &Config) -> Self
{ Self
{ n_rule: config.n_rule,
dot: config.dot,
}
}
}
impl cmp::Ord for ConfigKey
{ fn cmp(&self, other: &Self) -> cmp::Ordering
{ let mut res = self.n_rule.cmp(&other.n_rule);
if res == Ordering::Equal
{ res = self.dot.cmp(&other.dot);
}
res
}
}
impl cmp::PartialOrd for ConfigKey
{ fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering>
{ Some(self.cmp(other))
}
}
#[derive(Debug)]
struct Config
{ n_rule: usize,
dot: usize,
fws: Set,
fplp: Vec<Rc<RefCell<Config>>>,
bplp: Vec<Rc<RefCell<Config>>>,
n_state: usize,
status_is_complete: bool,
}
impl Config
{ pub fn new(n_rule: usize, dot: usize, fws_size: usize) -> Self
{ Self
{ n_rule,
dot,
fws: Set::new(fws_size),
fplp: Vec::new(),
bplp: Vec::new(),
n_state: std::usize::MAX,
status_is_complete: false,
}
}
}
impl cmp::Ord for Config
{ fn cmp(&self, other: &Self) -> cmp::Ordering
{ ConfigKey::new(self).cmp(&ConfigKey::new(other))
}
}
impl cmp::PartialOrd for Config
{ fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering>
{ Some(self.cmp(other))
}
}
impl cmp::PartialEq for Config
{ fn eq(&self, other: &Self) -> bool
{ ConfigKey::new(self).eq(&ConfigKey::new(other))
}
}
impl Eq for Config {}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
enum ActionType
{ Shift,
Accept,
Reduce,
Error,
SsConflict,
SrConflict,
RrConflict,
ShResolved,
RdResolved,
NotUsed,
ShiftReduce
}
#[derive(Debug, PartialEq, Copy, Clone)]
enum StateOrRule
{ State(usize),
Rule(usize),
EmptyRule
}
#[derive(Debug)]
struct Action
{ id: i32,
sp: Rc<RefCell<Symbol>>,
typ: ActionType,
x: StateOrRule,
sp_opt: Option<Rc<RefCell<Symbol>>>,
}
impl Action
{ fn new_state(id: i32, sp: &Rc<RefCell<Symbol>>, n_state: usize) -> Action
{ Action
{ id,
sp: Rc::clone(sp),
typ: ActionType::Shift,
x: StateOrRule::State(n_state),
sp_opt: None,
}
}
fn new_rule(id: i32, symbol: &Rc<RefCell<Symbol>>, typ: ActionType, n_rule: usize) -> Action
{ Action
{ id,
sp: Rc::clone(symbol),
typ,
x: StateOrRule::Rule(n_rule),
sp_opt: None,
}
}
fn new_empty_rule(id: i32, symbol: &Rc<RefCell<Symbol>>, typ: ActionType) -> Action
{ Action
{ id,
sp: Rc::clone(symbol),
typ,
x: StateOrRule::EmptyRule,
sp_opt: None,
}
}
}
impl cmp::Ord for Action
{ fn cmp(&self, other: &Self) -> cmp::Ordering
{ let index = self.sp.borrow().index;
let other_index = other.sp.borrow().index;
let mut res = index.cmp(&other_index);
if res == Ordering::Equal
{ res = self.typ.cmp(&other.typ);
if res == Ordering::Equal
{ if self.typ==ActionType::Reduce || self.typ==ActionType::ShiftReduce
{ if let StateOrRule::Rule(n_rule) = self.x
{ if let StateOrRule::Rule(other_n_rule) = other.x
{ if n_rule < other_n_rule
{ return Ordering::Less;
}
if n_rule > other_n_rule
{ return Ordering::Greater;
}
}
}
}
res = self.id.cmp(&other.id);
}
}
res
}
}
impl cmp::PartialOrd for Action
{ fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering>
{ Some(self.cmp(other))
}
}
impl cmp::PartialEq for Action
{ fn eq(&self, other: &Self) -> bool
{ self.cmp(other) == Ordering::Equal
}
}
impl Eq for Action {}
#[derive(Debug)]
struct State
{ basis: Vec<Rc<RefCell<Config>>>,
configurations: Vec<Rc<RefCell<Config>>>,
n_state: usize,
actions: Vec<Rc<RefCell<Action>>>,
n_token_act: usize,
n_nt_act: usize,
token_offset: isize,
nt_offset: isize,
default_reduce_action: usize,
default_reduce_rule: usize,
auto_reduce: bool,
}
impl State
{ pub fn new(n_state: usize, basis: Vec<Rc<RefCell<Config>>>, configurations: Vec<Rc<RefCell<Config>>>) -> Self
{ Self
{ basis,
configurations,
n_state,
actions: Vec::new(),
n_token_act: 0,
n_nt_act: 0,
token_offset: 0,
nt_offset: 0,
default_reduce_action: 0,
default_reduce_rule: std::usize::MAX,
auto_reduce: false,
}
}
}
#[derive(Debug)]
struct AxSet
{ n_state: usize,
is_tkn: bool,
n_action: usize,
i_order: usize,
}
impl AxSet
{ fn new(n_state: usize, is_tkn: bool, n_action: usize, i_order: usize) -> Self
{ Self
{ n_state,
is_tkn,
n_action,
i_order,
}
}
}
impl cmp::Ord for AxSet
{ fn cmp(&self, other: &Self) -> cmp::Ordering
{ let mut res = other.n_action.cmp(&self.n_action);
if res == Ordering::Equal
{ res = self.i_order.cmp(&other.i_order);
}
res
}
}
impl cmp::PartialOrd for AxSet
{ fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering>
{ Some(self.cmp(other))
}
}
impl cmp::PartialEq for AxSet
{ fn eq(&self, other: &Self) -> bool
{ self.cmp(other) == Ordering::Equal
}
}
impl Eq for AxSet {}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
struct LookaheadAction
{ lookahead: usize,
action: usize,
}
impl LookaheadAction
{ fn new(lookahead: usize, action: usize) -> Self
{ Self
{ lookahead,
action,
}
}
}
#[derive(Debug)]
struct ActTab
{ n_action: usize,
a_action: Vec<LookaheadAction>,
a_lookahead: Vec<LookaheadAction>,
min_lookahead: usize,
max_lookahead: usize,
min_action: usize,
n_terminals: usize,
n_symbols: usize,
min_token_offset: isize,
max_token_offset: isize,
min_nt_offset: isize,
max_nt_offset: isize,
n_fallbacks: usize,
n_shift_offset: usize,
reduce_count: usize,
}
impl ActTab
{ fn new(n_terminals: usize, n_symbols: usize) -> Self
{ Self
{ n_action: 0,
a_action: Vec::new(),
a_lookahead: Vec::new(),
min_lookahead: 0,
max_lookahead: 0,
min_action: 0,
n_terminals,
n_symbols,
min_token_offset: 0,
max_token_offset: 0,
min_nt_offset: 0,
max_nt_offset: 0,
n_fallbacks: 0,
n_shift_offset: 0,
reduce_count: 0,
}
}
fn get_n_lookahead(&self) -> usize
{ self.n_action
}
fn get_n_actions(&self) -> usize
{ let mut n_action = self.n_action;
for rec in self.a_action.iter().take(n_action).rev()
{ if rec.lookahead == std::usize::MAX
{ n_action -= 1;
}
else
{ break;
}
}
n_action
}
fn iter(&self) -> Take<Iter<LookaheadAction>>
{ self.a_action.iter().take(self.n_action)
}
fn acttab_action(&mut self, lookahead: usize, action: usize)
{ if self.a_lookahead.len() == 0
{ self.max_lookahead = lookahead;
self.min_lookahead = lookahead;
self.min_action = action;
}
else
{ if self.max_lookahead < lookahead
{ self.max_lookahead = lookahead;
}
if self.min_lookahead > lookahead
{ self.min_lookahead = lookahead;
self.min_action = action;
}
}
self.a_lookahead.push(LookaheadAction::new(lookahead, action));
}
fn acttab_insert(&mut self, make_it_safe: bool) -> isize
{ assert!(self.a_lookahead.len() > 0);
if self.n_action + self.n_symbols + 1 >= self.a_action.len()
{ self.a_action.resize(self.n_action + self.n_symbols + self.a_action.len() + 21, LookaheadAction::new(std::usize::MAX, std::usize::MAX));
}
let mut found_offset = std::usize::MAX;
let end = if make_it_safe {self.min_lookahead} else {0};
'l: for i in (end .. self.n_action).rev()
{ if self.a_action[i].lookahead == self.min_lookahead
{
if self.a_action[i].action == self.min_action
{ for j in 0..self.a_lookahead.len()
{ let k = self.a_lookahead[j].lookahead + i - self.min_lookahead;
if k>=self.n_action || self.a_lookahead[j].lookahead != self.a_action[k].lookahead || self.a_lookahead[j].action != self.a_action[k].action
{ continue 'l;
}
}
let mut n = 0;
for j in 0..self.n_action
{ if self.a_action[j].lookahead != std::usize::MAX
{ if self.a_action[j].lookahead == (j + self.min_lookahead).wrapping_sub(i)
{ n += 1;
}
}
}
if n == self.a_lookahead.len()
{ found_offset = i;
break;
}
}
}
}
if found_offset == std::usize::MAX
{
found_offset = if make_it_safe {self.min_lookahead} else {0};
'm: for i in found_offset .. self.a_action.len() - self.max_lookahead
{ if self.a_action[i].lookahead == std::usize::MAX
{ for j in 0 .. self.a_lookahead.len()
{ let k = self.a_lookahead[j].lookahead + i - self.min_lookahead;
if k >= self.a_action.len() || self.a_action[k].lookahead != std::usize::MAX
{ continue 'm;
}
}
for j in 0 .. self.n_action
{ if self.a_action[j].lookahead == (j + self.min_lookahead).wrapping_sub(i)
{ continue 'm;
}
}
found_offset = i;
break;
}
}
}
for j in 0 .. self.a_lookahead.len()
{ let k = self.a_lookahead[j].lookahead - self.min_lookahead + found_offset;
self.a_action[k] = self.a_lookahead[j];
if k >= self.n_action
{ self.n_action = k + 1;
}
}
if make_it_safe && found_offset+self.n_terminals >= self.n_action
{ self.n_action = found_offset+self.n_terminals+1;
}
self.a_lookahead.clear();
return found_offset as isize - self.min_lookahead as isize;
}
}
#[derive(Debug)]
struct SymbolsBuilder
{ symbols_map: HashMap<Rc<String>, Rc<RefCell<Symbol>>>,
n_terminals: usize,
n_nonterminals: usize,
}
impl SymbolsBuilder
{ pub fn new(empty: bool) -> Self
{ let mut this = Self
{ symbols_map: HashMap::new(),
n_terminals: 0,
n_nonterminals: 0,
};
if !empty
{ this.add(Rc::new("$".to_string()));
}
this
}
pub fn add(&mut self, symbol_name: Rc<String>) -> Rc<RefCell<Symbol>>
{ if let Some(symbol) = self.symbols_map.get(&symbol_name)
{ let symbol = Rc::clone(symbol);
return symbol;
}
let index = if is_terminal_name(symbol_name.as_ref()) || symbol_name.as_ref()=="$"
{ self.n_terminals += 1;
self.n_terminals - 1
}
else
{ self.n_nonterminals += 1;
self.n_nonterminals - 1
};
let symbol = Rc::new(RefCell::new(Symbol::new(&symbol_name, index)));
self.symbols_map.insert(symbol_name, Rc::clone(&symbol));
symbol
}
pub fn into_symbols(mut self, start_name: &String, nonterminal_types: HashMap<String, StringInFile>, precedence: HashMap<String, PrecedenceInFile>) -> ParserResult<Symbols>
{
for (symbol_name, symbol_type) in nonterminal_types
{ if let Some(symbol) = self.symbols_map.get(&symbol_name)
{ symbol.borrow_mut().data_type = symbol_type.string;
}
else
{ return Err(LemonMintError::new(&symbol_type.filename, symbol_type.n_line, format!("No such nonterminal symbol when defining symbol type: \"{}\"", symbol_name)));
}
}
for (symbol_name, precedence) in precedence
{ if let Some(symbol) = self.symbols_map.get(&symbol_name)
{ let mut symbol = symbol.borrow_mut();
symbol.prec = precedence.precedence;
symbol.assoc = precedence.assoc;
}
else
{ return Err(LemonMintError::new(&precedence.filename, precedence.n_line, format!("No such terminal symbol \"{}\" when defining precedence", symbol_name)));
}
}
let n_symbols = self.symbols_map.len();
let default_symbol = self.add(Rc::new("{default}".to_string()));
let default_symbol_index = default_symbol.borrow().index + self.n_terminals;
let mut start_symbol_index = std::usize::MAX;
let mut error_symbol_index = std::usize::MAX;
let mut array = Vec::new();
let empty = Rc::new(RefCell::new(Symbol::new("", 0)));
array.resize_with(self.symbols_map.len(), || empty.clone());
for (symbol_name, symbol) in self.symbols_map
{ let index =
{ let mut symbol_mut = symbol.borrow_mut();
if symbol_mut.typ == SymbolType::NONTERMINAL
{ symbol_mut.index += self.n_terminals;
}
if *symbol_name == *start_name
{ start_symbol_index = symbol_mut.index;
}
else if symbol_name.as_ref() == "error"
{ error_symbol_index = symbol_mut.index;
}
symbol_mut.name = symbol_name;
symbol_mut.index
};
array[index] = symbol;
}
array[0].borrow_mut().typ = SymbolType::NONTERMINAL;
Ok(Symbols {array, n_symbols, n_terminals: self.n_terminals, default_symbol_index, start_symbol_index, error_symbol_index})
}
}
#[derive(Debug)]
struct Symbols
{ pub array: Vec<Rc<RefCell<Symbol>>>,
pub n_symbols: usize,
pub n_terminals: usize,
start_symbol_index: usize,
default_symbol_index: usize,
error_symbol_index: usize,
}
impl Symbols
{ pub fn get_start_symbol(&self) -> &Rc<RefCell<Symbol>>
{ &self.array[self.start_symbol_index]
}
pub fn get_default_symbol(&self) -> &Rc<RefCell<Symbol>>
{ &self.array[self.default_symbol_index]
}
pub fn get_error_symbol(&self) -> Option<&Rc<RefCell<Symbol>>>
{ self.array.get(self.error_symbol_index)
}
}
#[derive(Debug)]
struct States
{ pub array: Vec<State>,
pub n_no_tail: usize,
}
#[derive(Debug)]
struct StatesBuilder;
impl StatesBuilder
{
pub fn build(rules: &mut Vec<Rule>, start: &Rc<RefCell<Symbol>>, n_terminals: usize, actions_enum: &mut i32) -> ParserResult<States>
{ let mut configs_basis_keys_arr = Vec::new();
let mut configs_basis_arr = Vec::new();
let mut configs_arr = Vec::new();
let mut configs_map = HashMap::new();
let mut states_map = HashMap::new();
for n_rule in start.borrow().sym_rules_arr.iter()
{ rules[*n_rule].lhs_start = true;
Self::configlist_add(rules, &mut configs_basis_keys_arr, &mut configs_basis_arr, &mut configs_arr, &mut configs_map, n_terminals, *n_rule, 0, true).borrow_mut().fws.add(0);
}
Self::getstate(rules, &mut configs_basis_keys_arr, &mut configs_basis_arr, &mut configs_arr, &mut configs_map, &mut states_map, n_terminals, actions_enum)?;
let mut states = States {array: Vec::with_capacity(states_map.len()), n_no_tail: 0};
for (_, elem) in states_map
{ states.array.push(Rc::try_unwrap(elem).unwrap().into_inner());
}
states.array.sort_by(|a, b| a.n_state.cmp(&b.n_state));
states.n_no_tail = states.array.len();
Ok(states)
}
fn getstate
( rules: &Vec<Rule>,
configs_basis_keys_arr: &mut Vec<ConfigKey>,
configs_basis_arr: &mut Vec<Rc<RefCell<Config>>>,
configs_arr: &mut Vec<Rc<RefCell<Config>>>,
configs_map: &mut HashMap<ConfigKey, Rc<RefCell<Config>>>,
states_map: &mut HashMap<Vec<ConfigKey>, Rc<RefCell<State>>>,
n_terminals: usize,
actions_enum: &mut i32,
) -> ParserResult<usize>
{
let mut basis_keys = mem::replace(configs_basis_keys_arr, Vec::new());
let mut basis = mem::replace(configs_basis_arr, Vec::new());
basis_keys.sort();
basis.sort();
if let Some(found) = states_map.get(&basis_keys)
{ let found = found.borrow();
let n_state = found.n_state;
for (i, x) in basis.iter().enumerate()
{ let mut y = found.basis[i].borrow_mut();
if let Ok(mut x) = x.try_borrow_mut()
{ mem::swap(&mut x.bplp, &mut y.bplp);
y.bplp.reserve(x.bplp.len());
for config in x.bplp.iter()
{ y.bplp.push(Rc::clone(config));
}
}
else
{
let len = y.bplp.len();
y.bplp.reserve(len);
for i in 0 .. len
{ let o = Rc::clone(&y.bplp[i]);
y.bplp.push(o);
}
}
}
configs_arr.clear();
Ok(n_state)
}
else
{
Self::configlist_closure(rules, configs_basis_keys_arr, configs_basis_arr, configs_arr, configs_map, n_terminals)?;
let n_state = states_map.len();
let mut configurations = mem::replace(configs_arr, Vec::new());
configurations.sort();
let stp = State::new
( n_state,
basis,
configurations
);
let stp = Rc::new(RefCell::new(stp));
states_map.insert(basis_keys, Rc::clone(&stp));
let actions = Self::buildshifts(rules, configs_basis_keys_arr, configs_basis_arr, configs_arr, configs_map, states_map, n_terminals, actions_enum, &stp)?;
stp.borrow_mut().actions = actions;
Ok(n_state)
}
}
fn buildshifts
( rules: &Vec<Rule>,
configs_basis_keys_arr: &mut Vec<ConfigKey>,
configs_basis_arr: &mut Vec<Rc<RefCell<Config>>>,
configs_arr: &mut Vec<Rc<RefCell<Config>>>,
configs_map: &mut HashMap<ConfigKey, Rc<RefCell<Config>>>,
states_map: &mut HashMap<Vec<ConfigKey>, Rc<RefCell<State>>>,
n_terminals: usize,
actions_enum: &mut i32,
stp: &Rc<RefCell<State>>
) -> ParserResult<Vec<Rc<RefCell<Action>>>>
{ let mut actions = Vec::new();
for cfp in stp.borrow().configurations.iter()
{ cfp.borrow_mut().status_is_complete = false;
}
for (i, cfp) in stp.borrow().configurations.iter().enumerate()
{ let (status_is_complete, dot, n_rule) =
{ let cfp = cfp.borrow();
(cfp.status_is_complete, cfp.dot, cfp.n_rule)
};
if !status_is_complete && dot<rules[n_rule].rhs.len()
{ configs_basis_keys_arr.clear();
configs_basis_arr.clear();
configs_arr.clear();
configs_map.clear();
let sp = Rc::clone(&rules[n_rule].rhs[dot]);
for bcfp in stp.borrow().configurations.iter().skip(i)
{ if !bcfp.borrow().status_is_complete && bcfp.borrow().dot<rules[bcfp.borrow().n_rule].rhs.len()
{ let bsp = Rc::clone(&rules[bcfp.borrow().n_rule].rhs[bcfp.borrow().dot]);
if *bsp.borrow() == *sp.borrow()
{ bcfp.borrow_mut().status_is_complete = true;
let newcfg = Self::configlist_add(rules, configs_basis_keys_arr, configs_basis_arr, configs_arr, configs_map, n_terminals, bcfp.borrow().n_rule, bcfp.borrow().dot+1, true);
newcfg.borrow_mut().bplp.push(Rc::clone(bcfp));
}
}
}
let n_state = Self::getstate(rules, configs_basis_keys_arr, configs_basis_arr, configs_arr, configs_map, states_map, n_terminals, actions_enum)?;
actions.insert(0, Rc::new(RefCell::new(Action::new_state(*actions_enum, &sp, n_state))));
*actions_enum += 1;
}
}
Ok(actions)
}
fn configlist_add
( rules: &Vec<Rule>,
configs_basis_keys_arr: &mut Vec<ConfigKey>,
configs_basis_arr: &mut Vec<Rc<RefCell<Config>>>,
configs_arr: &mut Vec<Rc<RefCell<Config>>>,
configs_map: &mut HashMap<ConfigKey, Rc<RefCell<Config>>>,
n_terminals: usize,
n_rule: usize,
dot: usize,
is_basis: bool
) -> Rc<RefCell<Config>>
{ let key = ConfigKey {n_rule: rules[n_rule].index, dot};
if let Some(found) = configs_map.get(&key)
{ Rc::clone(found)
}
else
{ let cfp = Rc::new(RefCell::new(Config::new(n_rule, dot, n_terminals+1)));
configs_arr.push(Rc::clone(&cfp));
let key = ConfigKey::new(&cfp.borrow());
if is_basis
{ configs_basis_keys_arr.push(key);
configs_basis_arr.push(Rc::clone(&cfp));
}
configs_map.insert(key, Rc::clone(&cfp));
cfp
}
}
fn configlist_closure
( rules: &Vec<Rule>,
configs_basis_keys_arr: &mut Vec<ConfigKey>,
configs_basis_arr: &mut Vec<Rc<RefCell<Config>>>,
configs_arr: &mut Vec<Rc<RefCell<Config>>>,
configs_map: &mut HashMap<ConfigKey, Rc<RefCell<Config>>>,
n_terminals: usize
) -> ParserResult<()>
{ let mut c = 0;
while c < configs_arr.len()
{ let (rule, dot) =
{ let config = configs_arr[c].borrow();
(&rules[config.n_rule], config.dot)
};
if dot < rule.rhs.len()
{ let sp = &rule.rhs[dot];
if sp.borrow().typ == SymbolType::NONTERMINAL
{ if sp.borrow().sym_rules_arr.is_empty() && sp.borrow().name.as_ref()!="error"
{ return Err(LemonMintError::new(&rule.rule_filename, rule.line, format!("Nonterminal \"{}\" has no rules.", &sp.borrow().name)));
}
for new_n_rule in sp.borrow().sym_rules_arr.iter()
{ let newcfp = Self::configlist_add(rules, configs_basis_keys_arr, configs_basis_arr, configs_arr, configs_map, n_terminals, *new_n_rule, 0, false);
let mut found = false;
for i in dot+1 .. rule.rhs.len()
{ let xsp = &rule.rhs[i];
if xsp.borrow().typ == SymbolType::TERMINAL
{ newcfp.borrow_mut().fws.add(xsp.borrow().index);
found = true;
break;
}
newcfp.borrow_mut().fws.intersect(&xsp.borrow().firstset);
if !xsp.borrow().lambda
{ found = true;
break;
}
}
if !found
{ configs_arr[c].borrow_mut().fplp.insert(0, newcfp);
}
}
}
}
c += 1;
}
Ok(())
}
}
#[derive(Debug)]
struct StringInFile
{ filename: Arc<String>,
n_line: usize,
string: String,
}
#[derive(Debug)]
struct PrecedenceInFile
{ filename: Arc<String>,
n_line: usize,
assoc: Associativity,
precedence: i32,
}
struct ArrayFormatter
{ i: usize,
values_on_line: usize,
endl: &'static str,
}
impl ArrayFormatter
{ pub fn new(values_on_line: usize) -> Self
{ Self
{ i: 0,
values_on_line,
endl: "\n[\t",
}
}
pub fn separ<W>(&mut self, out: &mut W) -> io::Result<()> where W: io::Write
{ if self.i % self.values_on_line == 0
{ write!(out, "{}/* {:5} */ ", self.endl, self.i)?;
self.endl = ",\n\t";
}
else
{ write!(out, ", ")?;
}
self.i += 1;
Ok(())
}
pub fn end<W>(self, out: &mut W) -> io::Result<()> where W: io::Write
{ writeln!(out, "{}];", if self.i==0 {" ["} else {"\n"})
}
}
#[derive(Debug)]
pub struct LemonMintBuilder
{ rules: Vec<Rule>,
token_type: String,
vartype: String,
start_name: String,
with_trace: bool,
trace_prompt: String,
extracode: String,
n_conflicts: usize,
n_action_table_entries: usize,
tables_size: usize,
has_fallback: bool,
symbols_builder: SymbolsBuilder,
prec_counter: i32,
actions_enum: i32,
nonterminal_types: HashMap<String, StringInFile>,
precedence: HashMap<String, PrecedenceInFile>,
extra_argument_type: String,
min_shift_reduce: usize,
error_action: usize,
accept_action: usize,
no_action: usize,
min_reduce: usize,
max_action: usize,
no_compress: bool,
no_resort: bool,
}
impl LemonMintBuilder
{
pub fn new() -> Self
{ Self
{ rules: Vec::with_capacity(64),
token_type: String::new(),
vartype: String::new(),
start_name: String::new(),
with_trace: false,
trace_prompt: String::new(),
extracode: String::new(),
n_conflicts: 0,
n_action_table_entries: 0,
tables_size: 0,
has_fallback: false,
symbols_builder: SymbolsBuilder::new(false),
prec_counter: 1,
actions_enum: 0,
nonterminal_types: HashMap::new(),
precedence: HashMap::new(),
extra_argument_type: String::new(),
min_shift_reduce: 0,
error_action: 0,
accept_action: 0,
no_action: 0,
min_reduce: 0,
max_action: 0,
no_compress: false,
no_resort: false,
}
}
fn find_rule_precedences(rules: &mut Vec<Rule>)
{ for rule in rules.iter_mut()
{ if rule.precsym.is_none()
{ for sp in rule.rhs.iter()
{ if sp.borrow().prec >= 0
{ rule.precsym = Some(Rc::clone(sp));
break;
}
}
}
}
}
fn find_first_sets(&self, symbols: &Symbols, rules: &Vec<Rule>)
{ for (i, symbol) in symbols.array[0 .. symbols.n_symbols].iter().enumerate()
{ symbol.borrow_mut().lambda = false;
if i >= symbols.n_terminals
{ symbol.borrow_mut().firstset.set_size(symbols.n_terminals + 1);
}
}
let mut progress = true;
while progress
{ progress = false;
'l: for rule in rules.iter()
{ let rule = rule;
if !rule.lhs.borrow().lambda
{ for sp in rule.rhs.iter()
{ assert!(sp.borrow().typ==SymbolType::NONTERMINAL || !sp.borrow().lambda);
if !sp.borrow().lambda
{ continue 'l;
}
}
rule.lhs.borrow_mut().lambda = true;
progress = true;
}
}
}
progress = true;
while progress
{ progress = false;
for rule in rules.iter()
{ let s1 = &rule.lhs;
for s2 in rule.rhs.iter()
{ if s2.borrow().typ == SymbolType::TERMINAL
{ if s1.borrow_mut().firstset.add(s2.borrow().index)
{ progress = true;
}
break;
}
else if *s1.borrow() == *s2.borrow()
{ if !s1.borrow().lambda
{ break;
}
}
else
{ if s1.borrow_mut().firstset.intersect(&s2.borrow().firstset)
{ progress = true;
}
if !s2.borrow().lambda
{ break;
}
}
}
}
}
}
fn find_links(states: &States)
{
for stp in states.array.iter()
{ for cfp in stp.configurations.iter()
{ cfp.borrow_mut().n_state = stp.n_state;
}
}
for stp in states.array.iter()
{ for cfp in stp.configurations.iter()
{ for plp in cfp.borrow().bplp.iter()
{ plp.borrow_mut().fplp.insert(0, Rc::clone(&cfp));
}
}
}
}
fn find_follow_sets(states: &States)
{ for stp in states.array.iter()
{ for cfp in stp.configurations.iter()
{ cfp.borrow_mut().status_is_complete = false;
}
}
let mut progress = true;
while progress
{ progress = false;
for stp in states.array.iter()
{ for cfp in stp.configurations.iter()
{ if !cfp.borrow().status_is_complete
{ for plp in cfp.borrow().fplp.iter()
{ let mut plp = plp.borrow_mut();
if plp.fws.intersect(&cfp.borrow().fws)
{ plp.status_is_complete = false;
progress = true;
}
}
cfp.borrow_mut().status_is_complete = true;
}
}
}
}
}
fn resolve_conflict(rules: &mut Vec<Rule>, apx: &Rc<RefCell<Action>>, apy: &Rc<RefCell<Action>>) -> usize
{ let mut errcnt = 0;
let mut apx = apx.borrow_mut();
let mut apy = apy.borrow_mut();
assert_eq!(apx.sp, apy.sp);
let mut apx_typ = apx.typ;
let mut apy_typ = apy.typ;
if apx_typ==ActionType::Shift && apy_typ==ActionType::Shift
{ apy_typ = ActionType::SsConflict;
errcnt += 1;
}
if apx_typ==ActionType::Shift && apy_typ==ActionType::Reduce
{ let spx = &apx.sp;
let spx = spx.borrow();
if let StateOrRule::Rule(n_rule) = apy.x
{ let spy = &rules[n_rule].precsym;
if let Some(spy) = spy
{ let spy = spy.borrow();
if spx.prec<0 || spy.prec<0
{
apy_typ = ActionType::SrConflict;
errcnt += 1;
}
else if spx.prec > spy.prec
{ apy_typ = ActionType::RdResolved;
}
else if spx.prec < spy.prec
{ apx_typ = ActionType::ShResolved;
}
else if spx.prec==spy.prec && spx.assoc== Associativity::RIGHT
{ apy_typ = ActionType::RdResolved;
}
else if spx.prec==spy.prec && spx.assoc== Associativity::LEFT
{ apx_typ = ActionType::ShResolved;
}
else
{ assert!(spx.prec==spy.prec && spx.assoc== Associativity::NONASSOC);
apy_typ = ActionType::Error;
}
}
else
{
apy_typ = ActionType::SrConflict;
errcnt += 1;
}
}
}
else if apx_typ==ActionType::Reduce && apy_typ==ActionType::Reduce
{ if let StateOrRule::Rule(n_rule_x) = apx.x
{ if let StateOrRule::Rule(n_rule_y) = apy.x
{ let spx = &rules[n_rule_x].precsym;
let spy = &rules[n_rule_y].precsym;
if let Some(spx) = spx
{ if let Some(spy) = spy
{ let spx = spx.borrow();
let spy = spy.borrow();
if spx.prec<0 || spy.prec<0 || spx.prec==spy.prec
{ apy_typ = ActionType::RrConflict;
errcnt += 1;
}
else if spx.prec > spy.prec
{ apy_typ = ActionType::RdResolved;
}
else if spx.prec < spy.prec
{ apx_typ = ActionType::RdResolved;
}
}
else
{ apy_typ = ActionType::RrConflict;
errcnt += 1;
}
}
else
{ apy_typ = ActionType::RrConflict;
errcnt += 1;
}
}
}
}
else
{ assert!
( apx_typ == ActionType::ShResolved ||
apx_typ == ActionType::RdResolved ||
apx_typ == ActionType::SsConflict ||
apx_typ == ActionType::SrConflict ||
apx_typ == ActionType::RrConflict ||
apy_typ == ActionType::ShResolved ||
apy_typ == ActionType::RdResolved ||
apy_typ == ActionType::SsConflict ||
apy_typ == ActionType::SrConflict ||
apy_typ == ActionType::RrConflict
);
}
apx.typ = apx_typ;
apy.typ = apy_typ;
errcnt
}
fn find_actions(&mut self, symbols: &Symbols, rules: &mut Vec<Rule>, states: &mut States) -> ParserResult<()>
{
for stp in states.array.iter_mut()
{ for cfp in stp.configurations.iter()
{ if cfp.borrow().dot == rules[cfp.borrow().n_rule].rhs.len()
{ for (j, symbol) in symbols.array.iter().take(symbols.n_terminals).enumerate()
{ if cfp.borrow().fws.contains(j)
{
stp.actions.push(Rc::new(RefCell::new(Action::new_rule(self.actions_enum, symbol, ActionType::Reduce, cfp.borrow().n_rule))));
self.actions_enum += 1;
}
}
}
}
}
states.array[0].actions.push(Rc::new(RefCell::new(Action::new_empty_rule(self.actions_enum, symbols.get_start_symbol(), ActionType::Accept))));
self.actions_enum += 1;
for stp in states.array.iter_mut()
{ if stp.actions.len() != 0
{ stp.actions.sort();
for action in stp.actions.windows(2)
{ if action[0].borrow().sp == action[1].borrow().sp
{
self.n_conflicts += Self::resolve_conflict(rules, &action[0], &action[1]);
}
}
}
}
for stp in states.array.iter_mut()
{ for action in stp.actions.iter()
{ if action.borrow().typ == ActionType::Reduce
{ if let StateOrRule::Rule(n_rule) = action.borrow().x
{ rules[n_rule].can_reduce = true;
}
}
}
}
for rule in rules.iter()
{ if !rule.can_reduce
{ return Err(LemonMintError::new(&rule.rule_filename, rule.rule_n_line, "This rule can not be reduced.\n".to_string()));
}
}
Ok(())
}
fn get_filename_of_first_rule(rules: &Vec<Rule>) -> Arc<String>
{ if let Some(rule) = rules.iter().next()
{ return Arc::clone(&rule.rule_filename);
}
Arc::new("".to_string())
}
fn compress_tables(&self, symbols: &Symbols, rules: &Vec<Rule>, states: &mut States, default_symbol: &Rc<RefCell<Symbol>>)
{ for state in states.array.iter_mut()
{ let mut n_best = 0;
let mut r_best = std::usize::MAX;
let mut it = state.actions.iter();
while let Some(action) = it.next()
{ if action.borrow().typ == ActionType::Reduce
{ if let StateOrRule::Rule(n_rule) = action.borrow().x
{ let rule = &rules[n_rule];
if !rule.lhs_start && n_rule != r_best
{ let mut n = 1;
let mut it2 = it.clone();
while let Some(ap2) = it2.next()
{ if ap2.borrow().typ == ActionType::Reduce
{ if let StateOrRule::Rule(n_rule_2) = ap2.borrow().x
{ if n_rule_2 != r_best && n_rule_2 == n_rule
{ n += 1;
}
}
}
}
if n > n_best
{ n_best = n;
r_best = n_rule;
}
}
}
}
}
if n_best < 1
{ continue;
}
let mut found = false;
for action in state.actions.iter()
{ let mut action = action.borrow_mut();
if action.typ == ActionType::Reduce
{ if let StateOrRule::Rule(n_rule) = action.x
{ if rules[n_rule].index == r_best
{ if !found
{ action.sp = Rc::clone(default_symbol);
found = true;
}
else
{ action.typ = ActionType::NotUsed;
}
}
}
}
}
assert!(found);
state.actions.sort();
found = false;
for action in state.actions.iter()
{ let action = action.borrow();
if action.typ==ActionType::Shift || action.typ==ActionType::Reduce && match action.x {StateOrRule::Rule(n_rule) => rules[n_rule].index != r_best, _ => false}
{ found = true;
break;
}
}
if !found
{ state.auto_reduce = true;
state.default_reduce_rule = r_best;
}
}
for stp in states.array.iter()
{ for action in stp.actions.iter()
{ let mut action = action.borrow_mut();
if action.typ == ActionType::Shift
{ if let StateOrRule::State(next_n_state) = action.x
{ let next_state = &states.array[next_n_state];
if next_state.auto_reduce && next_state.default_reduce_rule!=std::usize::MAX
{ action.typ = ActionType::ShiftReduce;
action.x = StateOrRule::Rule(next_state.default_reduce_rule);
}
}
}
}
}
for stp in states.array.iter()
{ for action in stp.actions.iter()
{ let mut done = false;
while !done
{ done = true;
let mut ap2 = None;
{ let action = action.borrow();
if action.typ == ActionType::ShiftReduce
{ if let StateOrRule::Rule(n_rule) = action.x
{ let rp = &rules[n_rule];
if rp.code.is_empty() && rp.rhs.len()==1
{
if action.sp.borrow().index >= symbols.n_terminals
{
ap2 = stp.actions.iter().find
( |a|
{ let a = a.borrow();
a.id!= action.id && a.sp.borrow().index==rp.lhs.borrow().index
}
);
assert!(ap2.is_some());
done = false;
}
}
}
}
}
if let Some(ap2) = ap2
{ let mut action = action.borrow_mut();
let ap2 = ap2.borrow();
action.sp_opt = Some(Rc::clone(&ap2.sp));
action.typ = ap2.typ;
action.x = ap2.x;
}
}
}
}
}
fn compute_action(&self, symbols: &Symbols, action: &Rc<RefCell<Action>>) -> usize
{ let action = action.borrow();
match action.typ
{ ActionType::Shift =>
{ match action.x
{ StateOrRule::State(n_state) => n_state,
_ => std::usize::MAX
}
}
ActionType::ShiftReduce =>
{
match action.x
{ StateOrRule::Rule(n_rule) =>
{ if action.sp.borrow().index >= symbols.n_terminals
{ self.min_reduce + n_rule
}
else
{ self.min_shift_reduce + n_rule
}
},
_ => std::usize::MAX
}
}
ActionType::Reduce =>
{ match action.x
{ StateOrRule::Rule(n_rule) => self.min_reduce + n_rule,
_ => std::usize::MAX
}
}
ActionType::Error =>
{ self.error_action
}
ActionType::Accept =>
{ self.accept_action
}
_ => std::usize::MAX
}
}
fn resort_states(&mut self, symbols: &Symbols, states: &mut States)
{ let sorted_len = states.array.len();
for stp in states.array.iter_mut()
{ stp.n_token_act = 0;
stp.n_nt_act = 0;
stp.default_reduce_action = std::usize::MAX;
stp.token_offset = std::isize::MIN;
stp.nt_offset = std::isize::MIN;
for action in stp.actions.iter()
{ let n_action = self.compute_action(symbols, action);
if n_action != std::usize::MAX
{ let action = action.borrow();
if action.sp.borrow().index < symbols.n_terminals
{ stp.n_token_act += 1;
}
else if action.sp.borrow().index < symbols.n_symbols
{ stp.n_nt_act += 1;
}
else
{ assert!(!stp.auto_reduce || action.x == StateOrRule::Rule(stp.default_reduce_rule));
stp.default_reduce_action = n_action;
}
}
}
}
if sorted_len > 2
{ states.array[1 ..].sort_by
( |a, b|
{ let mut res = b.n_nt_act.cmp(&a.n_nt_act);
if res == Ordering::Equal
{ res = b.n_token_act.cmp(&a.n_token_act);
if res == Ordering::Equal
{ res = b.n_state.cmp(&a.n_state);
}
}
res
}
);
}
let mut map = Vec::new();
map.resize(states.array.len(), 0);
for (i, stp) in states.array.iter().enumerate()
{ map[stp.n_state] = i;
}
for stp in states.array.iter()
{ for action in stp.actions.iter()
{ if let StateOrRule::State(ref mut state) = action.borrow_mut().x
{ *state = map[*state];
}
}
for configuration in stp.configurations.iter()
{ let mut configuration = configuration.borrow_mut();
configuration.n_state = map[configuration.n_state];
}
}
for (new_n_state, stp) in states.array.iter_mut().enumerate()
{ stp.n_state = new_n_state;
}
while states.n_no_tail>1 && states.array[states.n_no_tail-1].auto_reduce
{ states.n_no_tail -= 1;
}
}
fn minimum_size_type(lower: isize, upper: isize) -> isize
{ if lower >= 0
{ if upper <= 255
{ 1
}
else if upper < 65535
{ 2
}
else
{ 4
}
}
else
{ if lower>=-127 && upper<=127
{ -1
}
else if lower>=-32767 && upper<32767
{ -2
}
else
{ -4
}
}
}
fn translate_code(&self, rule: &mut Rule) -> ParserResult<()>
{ let mut used = Vec::new();
used.resize(rule.rhs.len(), false);
if rule.code.is_empty()
{ rule.line = rule.rule_n_line;
}
let mut it = rule.code.chars();
let mut it_prev = it.clone();
while let Some(cp) = it.next()
{ if cp.is_ascii_alphabetic()
{ let ident: String = it_prev.take_while(|c| c.is_ascii_alphanumeric() || *c=='_').collect();
if ident.len() > 1
{ it.nth(ident.chars().count() - 2).unwrap();
}
for i in 0 .. rule.rhs.len()
{ if rule.rhs_aliases.get(i) == Some(&ident)
{ used[i] = true;
break;
}
}
}
it_prev = it.clone();
}
for i in 0 .. rule.rhs.len()
{ if i < rule.rhs_aliases.len()
{ let alias: &str = &rule.rhs_aliases[i];
if !alias.is_empty() && !used[i]
{ return Err
( LemonMintError::new
( &rule.rule_filename,
rule.rule_n_line,
format!("Label {} for \"{}({})\" is never used.", alias, rule.rhs[i].borrow().name, alias)
)
);
}
}
}
Ok(())
}
fn get_minor_type(&self, symbols: &Symbols) -> Vec<(String, String)>
{
let arraysize = symbols.n_symbols * 2;
let mut types = Vec::new();
types.resize(arraysize, String::new());
'l: for (i, sp) in symbols.array[0 .. symbols.n_symbols].iter().enumerate()
{ let mut sp = &mut *sp.borrow_mut();
if i == symbols.error_symbol_index
{ sp.dtnum = arraysize+1;
continue;
}
if sp.typ==SymbolType::TERMINAL || (sp.data_type.is_empty() && self.vartype.is_empty())
{ sp.dtnum = 0;
continue;
}
let stddt = if !sp.data_type.is_empty()
{ &sp.data_type
}
else
{ &self.vartype
};
if !self.token_type.is_empty() && &self.token_type == stddt
{ sp.dtnum = 0;
continue;
}
let mut hash = DefaultHasher::new();
stddt.hash(&mut hash);
let mut hash = hash.finish() as usize % arraysize;
while !types[hash].is_empty()
{ if &types[hash] == stddt
{ sp.dtnum = hash + 1;
continue 'l;
}
hash += 1;
if hash >= arraysize
{ hash = 0;
}
}
sp.dtnum = hash + 1;
types[hash] = stddt.to_string();
}
let mut minor_type = Vec::with_capacity(arraysize+3);
minor_type.push(("None".to_string(), "".to_string()));
minor_type.push(("Symbol0".to_string(), "TokenValue".to_string()));
let mut i = 1;
for name in types
{ if !name.is_empty()
{ minor_type.push((format!("Symbol{}", i), name));
}
i += 1;
}
if let Some(symbol) = symbols.get_error_symbol()
{ minor_type.push((format!("Symbol{}", symbol.borrow().dtnum), "i32".to_string()));
}
minor_type
}
fn init_acttab(&mut self, symbols: &Symbols, rules: &mut Vec<Rule>, states: &mut States) -> ActTab
{ let mut acttab = ActTab::new(symbols.n_terminals, symbols.n_symbols);
let mut ax = Vec::with_capacity(states.n_no_tail*2);
for stp in states.array[0 .. states.n_no_tail].iter()
{ ax.push(AxSet::new(stp.n_state, true, stp.n_token_act, ax.len()));
ax.push(AxSet::new(stp.n_state, false, stp.n_nt_act, ax.len()));
}
ax.sort();
let mut min_token_offset = 0;
let mut max_token_offset = 0;
let mut min_nt_offset = 0;
let mut max_nt_offset = 0;
for ax_i in ax.iter()
{ if ax_i.n_action == 0
{ break;
}
let stp = &mut states.array[ax_i.n_state];
if ax_i.is_tkn
{ for action in stp.actions.iter()
{ if action.borrow().sp.borrow().index < symbols.n_terminals
{ let n_action = self.compute_action(symbols, action);
if n_action != std::usize::MAX
{ acttab.acttab_action(action.borrow().sp.borrow().index, n_action);
}
}
}
stp.token_offset = acttab.acttab_insert(true);
if stp.token_offset < min_token_offset
{ min_token_offset = stp.token_offset;
}
if stp.token_offset > max_token_offset
{ max_token_offset = stp.token_offset;
}
}
else
{ for action in stp.actions.iter()
{ if action.borrow().sp.borrow().index >= symbols.n_terminals && action.borrow().sp.borrow().index != symbols.n_symbols
{ let n_action = self.compute_action(symbols, action);
if n_action != std::usize::MAX
{ acttab.acttab_action(action.borrow().sp.borrow().index, n_action);
}
}
}
stp.nt_offset = acttab.acttab_insert(false);
if stp.nt_offset < min_nt_offset
{ min_nt_offset = stp.nt_offset;
}
if stp.nt_offset > max_nt_offset
{ max_nt_offset = stp.nt_offset;
}
}
}
for rule in rules.iter_mut()
{ rule.does_reduce = false;
}
for state in states.array[0 .. states.n_no_tail].iter()
{ for action in state.actions.iter()
{ let action = action.borrow_mut();
if action.typ==ActionType::Reduce || action.typ==ActionType::ShiftReduce
{ if let StateOrRule::Rule(n_rule) = action.x
{ rules[n_rule].does_reduce = true;
}
}
}
}
let mut n_fallbacks = 0;
if self.has_fallback
{ n_fallbacks = symbols.n_terminals - 1;
while n_fallbacks>0 && symbols.array[n_fallbacks].borrow().fallback.is_none()
{ n_fallbacks -= 1;
}
n_fallbacks += 1;
}
self.has_fallback = n_fallbacks != 0;
let mut n_shift_offset = states.n_no_tail;
while n_shift_offset >0 && states.array[n_shift_offset -1].token_offset == std::isize::MIN
{ n_shift_offset -= 1;
}
let mut reduce_count = states.n_no_tail;
while reduce_count>0 && states.array[reduce_count-1].nt_offset == std::isize::MIN
{ reduce_count -= 1;
}
acttab.min_token_offset = min_token_offset;
acttab.max_token_offset = max_token_offset;
acttab.min_nt_offset = min_nt_offset;
acttab.max_nt_offset = max_nt_offset;
acttab.n_fallbacks = n_fallbacks;
acttab.n_shift_offset = n_shift_offset;
acttab.reduce_count = reduce_count;
acttab
}
fn get_lemon(mut self, symbols: Symbols, mut rules: Vec<Rule>, states: States, acttab: ActTab) -> ParserResult<LemonMint>
{
let sz_code_type = Self::minimum_size_type(0, symbols.n_symbols as isize+1);
let sz_action_type = Self::minimum_size_type(0, (states.array.len()+rules.len()+5) as isize);
let sz_shift_offset_type = Self::minimum_size_type(acttab.min_token_offset-1, acttab.max_token_offset);
let sz_reduce_offset_type = Self::minimum_size_type(acttab.min_nt_offset-1, acttab.max_nt_offset);
let mut token = Vec::with_capacity(symbols.n_terminals);
for symbol in symbols.array[1 .. symbols.n_terminals].iter()
{ token.push(Rc::clone(&symbol.borrow().name));
}
self.tables_size += rules.len() * sz_code_type.abs() as usize;
self.n_action_table_entries = acttab.get_n_actions();
let mut n = acttab.get_n_lookahead();
let n_lookahead = std::cmp::max(n, symbols.n_terminals + self.n_action_table_entries);
let mut lookahead_and_action = Vec::with_capacity(n_lookahead);
for (i, LookaheadAction {lookahead, action}) in acttab.iter().enumerate()
{ let lookahead = if *lookahead == std::usize::MAX {symbols.n_symbols} else {*lookahead};
let action = if i >= self.n_action_table_entries {0} else if *action == std::usize::MAX {self.no_action} else {*action};
lookahead_and_action.push((lookahead, action));
}
while n < n_lookahead
{ lookahead_and_action.push((symbols.n_terminals, 0));
n += 1;
}
self.tables_size += lookahead_and_action.len() * (sz_code_type + sz_action_type).abs() as usize;
let mut shift_offset = Vec::with_capacity(acttab.n_shift_offset);
for stp in states.array.iter().take(acttab.n_shift_offset)
{ shift_offset.push(if stp.token_offset==std::isize::MIN {acttab.min_token_offset - 1} else {stp.token_offset});
}
self.tables_size += shift_offset.len() * sz_shift_offset_type.abs() as usize;
let mut reduce_offset = Vec::with_capacity(acttab.reduce_count);
for stp in states.array.iter().take(acttab.reduce_count)
{ reduce_offset.push(if stp.nt_offset==std::isize::MIN {acttab.min_nt_offset - 1} else {stp.nt_offset});
}
self.tables_size += reduce_offset.len() * sz_reduce_offset_type.abs() as usize;
let mut default = Vec::with_capacity(states.n_no_tail);
for stp in states.array.iter().take(states.n_no_tail)
{ default.push(if stp.default_reduce_action!=std::usize::MAX {stp.default_reduce_action + self.min_reduce} else {self.error_action});
}
self.tables_size += default.len() * sz_action_type.abs() as usize;
let mut fallback = Vec::new();
if self.has_fallback
{ fallback.reserve(acttab.n_fallbacks);
for symbol in symbols.array.iter().take(acttab.n_fallbacks)
{ if let Some(ref fb) = symbol.borrow().fallback
{ fallback.push((fb.borrow().index, Rc::clone(&symbol.borrow().name), Rc::clone(&fb.borrow().name)));
}
else
{ fallback.push((0, Rc::clone(&symbol.borrow().name), Rc::new(String::new())));
}
}
}
self.tables_size += fallback.len() * sz_code_type.abs() as usize;
let mut token_names = Vec::new();
if self.with_trace && symbols.n_symbols!=0
{ token_names.reserve(symbols.n_symbols);
for symbol in symbols.array.iter().take(symbols.n_symbols)
{ token_names.push(Rc::clone(&symbol.borrow().name));
}
}
for sym in symbols.array.iter()
{ for n_rule in sym.borrow().sym_rules_arr.iter()
{ self.translate_code(&mut rules[*n_rule])?;
}
}
let start_type = symbols.get_start_symbol().borrow().data_type.clone();
let minor_type = self.get_minor_type(&symbols);
let n_symbols = symbols.n_symbols;
let n_states = states.array.len();
let n_no_tail = states.n_no_tail;
let error_symbol = if let Some(symbol) = symbols.get_error_symbol() {symbol.borrow().index} else {0};
let n_terminals = symbols.n_terminals;
let n_nonterminals = symbols.n_symbols - symbols.n_terminals;
Ok
( LemonMint
{ symbols,
rules,
states,
token_type: self.token_type,
vartype: self.vartype,
start_name: self.start_name,
start_type,
extra_argument_type: self.extra_argument_type,
extracode: self.extracode,
minor_type,
token,
sz_code_type,
sz_action_type,
sz_shift_offset_type,
sz_reduce_offset_type,
n_symbols,
n_states,
n_no_tail,
n_fallbacks: acttab.n_fallbacks,
error_symbol,
with_fallback: self.has_fallback,
shift_count: acttab.n_shift_offset-1,
reduce_count: acttab.reduce_count-1,
with_trace: self.with_trace,
trace_prompt: self.trace_prompt,
shift_min: acttab.min_token_offset,
shift_max: acttab.max_token_offset,
reduce_min: acttab.min_nt_offset,
reduce_max: acttab.max_nt_offset,
n_terminals,
n_nonterminals,
tables_size: self.tables_size,
n_action_table_entries: self.n_action_table_entries,
lookahead_and_action,
shift_offset,
reduce_offset,
default,
fallback,
token_names,
}
)
}
pub fn load_y_file(self, filename: &Arc<String>) -> ParserResult<Self>
{ self.load_y(filename, File::open(filename.as_ref()).map_err(|e| LemonMintError::new(filename, 1, e.to_string()))?)
}
pub fn load_y<R>(mut self, filename: &Arc<String>, input: R) -> ParserResult<Self> where R: io::Read
{ let input = BufReader::new(input);
let mut n_line = 0;
let mut lines = input.lines();
let mut part_till_comment: Option<String> = None;
'l: while let Some(line) = lines.next()
{ n_line += 1;
let mut line = line.map_err(|e| LemonMintError::new(filename, n_line, e.to_string()))?;
if let Some(part) = part_till_comment.take()
{ if let Some(pos) = line.find("*/")
{ line.replace_range(.. pos+2, &part);
}
else
{ part_till_comment = Some(part);
continue;
}
}
loop
{ if let Some(pos) = line.find('/')
{ match line.bytes().skip(pos+1).next()
{ Some(b'/') =>
{ line.truncate(pos);
}
Some(b'*') =>
{ if let Some(len_minus_2) = line[pos+2 ..].find("*/")
{ line.replace_range(pos .. pos+len_minus_2+2, "");
continue;
}
else
{ line.truncate(pos);
part_till_comment = Some(line);
continue 'l;
}
}
_ => {}
}
}
break;
}
let mut line = line.trim();
if !line.is_empty()
{ let mut directive = Directive::Rule;
let mut symbol_name: Cow<str> = "".into();
let mut rule_rhs: Cow<str> = "".into();
if line.as_bytes()[0] == b'%'
{ let pos = line.bytes().position(|c| c.is_ascii_whitespace()).unwrap_or(line.len());
let token = &line[1 .. pos];
line = &line[pos ..].trim_start();
directive = match token
{ "token_type" => Directive::TokenType,
"type" => Directive::Type,
"default_type" => Directive::DefaultType,
"start_symbol" => Directive::StartSymbol,
"trace" => Directive::Trace,
"extra_argument" => Directive::ExtraArgument,
"left" => Directive::Left,
"right" => Directive::Right,
"nonassoc" => Directive::Nonassoc,
"fallback" => Directive::Fallback,
"code" | "include" => Directive::Code,
_ =>
{ return Err(LemonMintError::new(filename, n_line, format!("Directive not supported: %{}", token)));
}
};
}
match directive
{ Directive::Type | Directive::Fallback =>
{
let pos = line.bytes().position(|c| !c.is_ascii_alphanumeric() && c!=b'_').unwrap_or(line.len());
if pos == 0
{ return Err(LemonMintError::new(filename, n_line, "Expected symbol name after %type".to_string()));
}
symbol_name = line[.. pos].to_string().into();
line = &line[pos ..].trim_start();
}
Directive::Rule =>
{ let pos = line.find("::=").ok_or_else(|| LemonMintError::new(filename, n_line, "Couldn't interpret this line".to_string()))?;
symbol_name = line[.. pos].trim_end().to_string().into();
line = &line[pos+3 ..].trim_start();
let pos = line.find('.').ok_or_else(|| LemonMintError::new(filename, n_line, "Rules must be terminated with a dot".to_string()))?;
rule_rhs = line[.. pos].trim_end().to_string().into();
line = &line[pos+1 ..].trim_start();
}
_ => {}
}
let mut value: Cow<str> = line.into();
if line.bytes().next() == Some(b'{')
{ line = &line[1 ..].trim_start();
let (mut balance, mut last_close) = (1, usize::MAX);
Self::balanced_braces(filename, n_line, line, &mut balance, &mut last_close)?;
if balance == 0
{ value = line[.. last_close].into();
}
else
{
let mut buffer = String::with_capacity(128);
buffer.push_str(line);
buffer.push('\n');
let from_n_line = n_line;
while let Some(line) = lines.next()
{ n_line += 1;
let line = line.map_err(|e| LemonMintError::new(filename, n_line, e.to_string()))?;
Self::balanced_braces(filename, n_line, &line, &mut balance, &mut last_close)?;
if balance == 0
{ buffer.push_str(&line[.. last_close]);
break;
}
buffer.push_str(&line);
buffer.push('\n');
}
if balance > 0
{ return Err(LemonMintError::new(filename, n_line, format!("Braces not closed at the end of file since line {}", from_n_line)));
}
value = buffer.into();
}
}
else if !line.is_empty() && line.as_bytes()[line.len()-1]==b'.'
{ value = line[.. line.len()-1].trim_end().into();
}
self = match directive
{ Directive::Rule => self.add_rule(filename, n_line, symbol_name.into(), rule_rhs.as_ref(), value.into()),
Directive::TokenType => self.set_token_type(value.into()),
Directive::Type => self.add_type(filename, n_line, symbol_name.into(), value.into()),
Directive::DefaultType => self.set_default_type(value.into()),
Directive::StartSymbol => self.set_start_symbol(filename, n_line, value.into()),
Directive::Trace => self.set_trace_prompt(value.into()),
Directive::ExtraArgument => self.set_extra_argument_type(value.into()),
Directive::Left => self.set_left(filename, n_line, value.as_ref()),
Directive::Right => self.set_right(filename, n_line, value.as_ref()),
Directive::Nonassoc => self.set_nonassoc(filename, n_line, value.as_ref()),
Directive::Fallback => self.add_fallback(filename, n_line, symbol_name.into(), value.as_ref()),
Directive::Code => self.add_code(value.into()),
}?;
}
}
Ok(self)
}
fn balanced_braces(filename: &Arc<String>, n_line: usize, line: &str, balance: &mut i32, last_close: &mut usize) -> ParserResult<()>
{ for (i, c) in line.bytes().enumerate()
{ match c
{ b'{' => *balance += 1,
b'}' => {*balance -= 1; *last_close = i}
_ => {}
}
}
if *balance < 0
{ return Err(LemonMintError::new(filename, n_line, "Braces on line not balanced".to_string()));
}
if *balance == 0
{ if !line[*last_close+1 ..].trim_end().is_empty()
{ return Err(LemonMintError::new(filename, n_line, "Junk after closing brace".to_string()));
}
}
Ok(())
}
pub fn set_start_symbol(mut self, filename: &Arc<String>, n_line: usize, mut value: String) -> ParserResult<Self>
{ let trimmed = value.trim();
if trimmed.len() != value.len()
{ value = trimmed.to_string();
}
if value.find(|c: char| !c.is_ascii_alphanumeric() && c!='_').is_some()
{ return Err(LemonMintError::new(filename, n_line, format!("Invalid start symbol name: {}", value)));
}
if is_terminal_name(&value)
{ return Err(LemonMintError::new(filename, n_line, "Start symbol must be nonterminal".to_string()));
}
self.start_name = value;
Ok(self)
}
pub fn set_token_type(mut self, value: String) -> ParserResult<Self>
{ self.token_type = typename_to_string(value);
Ok(self)
}
pub fn set_default_type(mut self, value: String) -> ParserResult<Self>
{ self.vartype = typename_to_string(value);
Ok(self)
}
pub fn set_trace_prompt(mut self, value: String) -> ParserResult<Self>
{ self.with_trace = true;
self.trace_prompt = value;
Ok(self)
}
pub fn set_extra_argument_type(mut self, value: String) -> ParserResult<Self>
{ self.extra_argument_type = typename_to_string(value);
Ok(self)
}
pub fn set_left(self, filename: &Arc<String>, n_line: usize, symbol_names: &str) -> ParserResult<Self>
{ self.set_associativity(filename, n_line, symbol_names, Associativity::LEFT)
}
pub fn set_right(self, filename: &Arc<String>, n_line: usize, symbol_names: &str) -> ParserResult<Self>
{ self.set_associativity(filename, n_line, symbol_names, Associativity::RIGHT)
}
pub fn set_nonassoc(self, filename: &Arc<String>, n_line: usize, symbol_names: &str) -> ParserResult<Self>
{ self.set_associativity(filename, n_line, symbol_names, Associativity::NONASSOC)
}
fn set_associativity(mut self, filename: &Arc<String>, n_line: usize, symbol_names: &str, assoc: Associativity) -> ParserResult<Self>
{ for symbol_name in symbol_names.trim().split(|c: char| c.is_ascii_whitespace())
{ if !symbol_name.is_empty()
{ if symbol_name.find(|c: char| !c.is_ascii_alphanumeric() && c!='_').is_some()
{ return Err(LemonMintError::new(filename, n_line, format!("Invalid token name: {}", symbol_name)));
}
if !is_terminal_name(symbol_name)
{ return Err(LemonMintError::new(filename, n_line, format!("Can only set precedence for terminal symbols, not \"{}\"", symbol_name)));
}
if let Some(prev) = self.precedence.get(symbol_name)
{ return Err(LemonMintError::new(filename, n_line, format!("Symbol \"{}\" has already be given a precedence at {}:{}", symbol_name, prev.filename, prev.n_line)));
}
self.precedence.insert
( symbol_name.to_string(),
PrecedenceInFile
{ filename: Arc::clone(filename),
n_line,
assoc,
precedence: self.prec_counter,
}
);
}
}
self.prec_counter += 1;
Ok(self)
}
pub fn add_type(mut self, filename: &Arc<String>, n_line: usize, mut symbol_name: String, type_name: String) -> ParserResult<Self>
{ let trimmed = symbol_name.trim();
if trimmed.len() != symbol_name.len()
{ symbol_name = trimmed.to_string();
}
if symbol_name.find(|c: char| !c.is_ascii_alphanumeric() && c!='_').is_some()
{ return Err(LemonMintError::new(filename, n_line, format!("Invalid symbol name: {}", symbol_name)));
}
if is_terminal_name(&symbol_name)
{ return Err(LemonMintError::new(filename, n_line, format!("Can only set type for nonterminal symbols, not \"{}\"", symbol_name)));
}
if let Some(prev) = self.nonterminal_types.get(&symbol_name)
{ return Err(LemonMintError::new(filename, n_line, format!("Type for symbol \"{}\" already defined at {}:{}", symbol_name, prev.filename, prev.n_line)));
}
self.nonterminal_types.insert
( symbol_name,
StringInFile
{ filename: Arc::clone(filename),
n_line,
string: typename_to_string(type_name),
}
);
Ok(self)
}
pub fn add_fallback(mut self, filename: &Arc<String>, n_line: usize, mut fallback_to_symbol_name: String, symbol_names: &str) -> ParserResult<Self>
{ let trimmed = fallback_to_symbol_name.trim();
if trimmed.len() != fallback_to_symbol_name.len()
{ fallback_to_symbol_name = trimmed.to_string();
}
if fallback_to_symbol_name.find(|c: char| !c.is_ascii_alphanumeric() && c!='_').is_some()
{ return Err(LemonMintError::new(filename, n_line, format!("Invalid token name: {}", fallback_to_symbol_name)));
}
let fallback_to_symbol_name = Rc::new(fallback_to_symbol_name);
for symbol_name in symbol_names.trim().split(|c: char| c.is_ascii_whitespace())
{ if !symbol_name.is_empty()
{ if symbol_name.find(|c: char| !c.is_ascii_alphanumeric() && c!='_').is_some()
{ return Err(LemonMintError::new(filename, n_line, format!("Invalid token name: {}", symbol_name)));
}
if !is_terminal_name(symbol_name)
{ return Err(LemonMintError::new(filename, n_line, format!("Can only set fallback to terminal symbols, not \"{}\"", symbol_name)));
}
let symbol_name = Rc::new(symbol_name.to_string());
let sp = self.symbols_builder.add(Rc::clone(&symbol_name));
if sp.borrow().fallback.is_some()
{ return Err(LemonMintError::new(filename, n_line, format!("More than one fallback assigned to token \"{}\"", symbol_name)));
}
let fsp = self.symbols_builder.add(Rc::clone(&fallback_to_symbol_name));
if *fsp.borrow() == *sp.borrow()
{ return Err(LemonMintError::new(filename, n_line, format!("Symbol \"{}\" is asked fallback to self // {}, {}", symbol_name, fsp.borrow().name, sp.borrow().name)));
}
sp.borrow_mut().fallback = Some(fsp);
}
}
Ok(self)
}
pub fn add_rule(mut self, filename: &Arc<String>, n_line: usize, mut lhs_name: String, rhs_names_aliases: &str, code: String) -> ParserResult<Self>
{ let trimmed = lhs_name.trim();
if trimmed.len() != lhs_name.len()
{ lhs_name = trimmed.to_string();
}
if is_terminal_name(&lhs_name)
{ return Err(LemonMintError::new(filename, n_line, format!("Invalid LHS symbol: \"{}\"", lhs_name)));
}
let mut code = code;
if code.trim().is_empty()
{ code = String::new();
}
if self.start_name.is_empty()
{ self.start_name = lhs_name.clone();
}
let lhs_name = Rc::new(lhs_name);
let mut rule = Rule::new(filename, n_line, &self.symbols_builder.add(lhs_name), self.rules.len(), code);
let mut s = rhs_names_aliases.trim_start();
while s.len() != 0
{ let name_len = s.chars().position(|c| !c.is_ascii_alphanumeric() && c!='_').unwrap_or(s.len());
if name_len == 0
{ return Err(LemonMintError::new(filename, n_line, format!("Invalid RHS expression: \"{}\"", rhs_names_aliases)));
}
rule.rhs.push(self.symbols_builder.add(Rc::new(s[.. name_len].to_string())));
s = s[name_len ..].trim_start();
let mut alias = String::new();
if s.bytes().next() == Some(b'(')
{ s = s[1 ..].trim_start();
let alias_len = s.chars().position(|c| !c.is_ascii_alphanumeric() && c!='_').unwrap_or(s.len());
alias = s[.. alias_len].to_string();
s = s[alias_len ..].trim_start();
if alias_len==0 || s.bytes().next() != Some(b')')
{ return Err(LemonMintError::new(filename, n_line, format!("Invalid alias in RHS expression: \"{}\"", rhs_names_aliases)));
}
s = s[1 ..].trim_start();
}
rule.rhs_aliases.push(alias);
}
rule.lhs.borrow_mut().sym_rules_arr.insert(0, self.rules.len());
self.rules.push(rule);
Ok(self)
}
pub fn add_code(mut self, code: String) -> ParserResult<Self>
{ if self.extracode.is_empty()
{ self.extracode = code;
}
else
{ self.extracode.push_str("\n\n");
self.extracode.push_str(&code);
}
Ok(self)
}
pub fn set_no_compress(mut self, new_no_compress: bool) -> ParserResult<Self>
{ self.no_compress = new_no_compress;
Ok(self)
}
pub fn set_no_resort(mut self, new_no_resort: bool) -> ParserResult<Self>
{ self.no_resort = new_no_resort;
Ok(self)
}
pub fn try_into_lemon(mut self) -> ParserResult<LemonMint>
{
let mut rules = mem::replace(&mut self.rules, Vec::new());
if rules.len() == 0
{ return Err(LemonMintError::new(&Arc::new(String::new()), 1, "Empty grammar".to_string()));
}
rules.sort_by
( |a, b|
{ if a.code.is_empty() == b.code.is_empty()
{ a.index.cmp(&b.index)
}
else if a.code.is_empty()
{ Ordering::Greater
}
else
{ Ordering::Less
}
}
);
for (i, rule) in rules.iter_mut().enumerate()
{ rule.index = i;
}
let symbols =
{ let symbols_builder = mem::replace(&mut self.symbols_builder, SymbolsBuilder::new(true));
let nonterminal_types = mem::replace(&mut self.nonterminal_types, HashMap::new());
let precedence = mem::replace(&mut self.precedence, HashMap::new());
symbols_builder.into_symbols(&self.start_name, nonterminal_types, precedence)
}?;
Self::find_rule_precedences(&mut rules);
self.find_first_sets(&symbols, &rules);
if !self.start_name.is_empty()
{ if symbols.start_symbol_index == std::usize::MAX
{ return Err(LemonMintError::new(&Self::get_filename_of_first_rule(&rules), 1, format!("The specified start symbol \"{}\" is not in a nonterminal of the grammar", self.start_name)));
}
}
else
{ return Err(LemonMintError::new(&Self::get_filename_of_first_rule(&rules), 1, "Please, specify a start symbol".to_string()));
}
for rule in rules.iter()
{ for sp in rule.rhs.iter()
{ if *sp.borrow() == *symbols.get_start_symbol().borrow()
{ return Err(LemonMintError::new(&Self::get_filename_of_first_rule(&rules), 1, format!("The start symbol \"{}\" occurs on the right-hand side of a rule", sp.borrow().name)));
}
}
}
dump_symbols(&symbols, &rules);
let mut states = StatesBuilder::build(&mut rules, symbols.get_start_symbol(), symbols.n_terminals, &mut self.actions_enum)?;
dump_states(&states, &rules, &symbols, 1);
Self::find_links(&states);
dump_states(&states, &rules, &symbols, 2);
Self::find_follow_sets(&states);
dump_states(&states, &rules, &symbols, 3);
self.find_actions(&symbols, &mut rules, &mut states)?;
dump_states(&states, &rules, &symbols, 4);
if !self.no_compress
{ self.compress_tables(&symbols, &rules, &mut states, symbols.get_default_symbol());
}
dump_states(&states, &rules, &symbols, 5);
if !self.no_resort
{ self.resort_states(&symbols, &mut states);
}
dump_states(&states, &rules, &symbols, 6);
self.min_shift_reduce = states.array.len();
self.error_action = self.min_shift_reduce + rules.len();
self.accept_action = self.error_action + 1;
self.no_action = self.accept_action + 1;
self.min_reduce = self.no_action + 1;
self.max_action = self.min_reduce + rules.len();
let acttab = self.init_acttab(&symbols, &mut rules, &mut states);
dump_states(&states, &rules, &symbols, 7);
if self.n_conflicts > 0
{ Err(LemonMintError::new(&Self::get_filename_of_first_rule(&rules), 1, format!("{} parsing conflicts", self.n_conflicts)))
}
else
{ self.get_lemon(symbols, rules, states, acttab)
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
enum Lang
{ Rust
}
fn size_type_to_str(lang: Lang, n_bytes: isize) -> &'static str
{ match (lang, n_bytes)
{ (Lang::Rust, -1) => "i8",
(Lang::Rust, -2) => "i16",
(Lang::Rust, -4) => "i32",
(Lang::Rust, 1) => "u8",
(Lang::Rust, 2) => "u16",
(Lang::Rust, 4) => "u32",
(Lang::Rust, _) => "isize",
}
}
#[derive(Debug)]
pub struct LemonMint
{ symbols: Symbols,
rules: Vec<Rule>,
states: States,
token_type: String,
vartype: String,
start_name: String,
start_type: String,
extra_argument_type: String,
extracode: String,
minor_type: Vec<(String, String)>,
token: Vec<Rc<String>>,
sz_code_type: isize,
sz_action_type: isize,
sz_shift_offset_type: isize,
sz_reduce_offset_type: isize,
n_symbols: usize,
n_states: usize,
n_no_tail: usize,
n_fallbacks: usize,
error_symbol: usize,
with_fallback: bool,
shift_count: usize,
reduce_count: usize,
with_trace: bool,
trace_prompt: String,
shift_min: isize,
shift_max: isize,
reduce_min: isize,
reduce_max: isize,
n_terminals: usize,
n_nonterminals: usize,
tables_size: usize,
n_action_table_entries: usize,
lookahead_and_action: Vec<(usize, usize)>,
shift_offset: Vec<isize>,
reduce_offset: Vec<isize>,
default: Vec<usize>,
fallback: Vec<(usize, Rc<String>, Rc<String>)>,
token_names: Vec<Rc<String>>,
}
impl LemonMint
{
pub fn gen_rust<W>(&self, out: &mut W) -> io::Result<()> where W: io::Write
{ writeln!(out, "pub mod code {{")?;
let mut lempar_reader = LemparReader::new();
lempar_reader.copy_part(out)?;
writeln!(out, "pub type TokenValue = {};", if !self.token_type.is_empty() {&self.token_type} else {"String"})?;
writeln!(out, "pub type ExtraArgumentType = {};", if !self.extra_argument_type.is_empty() {&self.extra_argument_type} else {"()"})?;
writeln!(out, "pub type StartType = {};", if !self.start_type.is_empty() {&self.start_type} else {"()"})?;
writeln!(out, "type CodeType = {};", size_type_to_str(Lang::Rust, self.sz_code_type))?;
writeln!(out, "type ActionType = {};", size_type_to_str(Lang::Rust, self.sz_action_type))?;
writeln!(out, "type ShiftOffsetType = {};", size_type_to_str(Lang::Rust, self.sz_shift_offset_type))?;
writeln!(out, "type ReduceOffsetType = {};", size_type_to_str(Lang::Rust, self.sz_reduce_offset_type))?;
write!(out, "enum MinorType\n{{")?;
for (variant, typ) in self.minor_type.iter()
{ if typ.is_empty()
{ writeln!(out, "\t{},", variant)?;
}
else
{ writeln!(out, "\t{}({}),", variant, typ)?;
}
}
writeln!(out, "}}")?;
writeln!(out, "#[repr(u32)]\n#[allow(non_camel_case_types)]")?;
write!(out, "pub enum Token\n{{")?;
for (i, name) in self.token.iter().enumerate()
{ if self.token_type.is_empty()
{ writeln!(out, "\t{:<30} = {:2},", name, i+1)?;
}
else
{ writeln!(out, "\t{:<30} = {:2},", name, i+1)?;
}
}
writeln!(out, "}}\n")?;
writeln!(out, "const NO_CODE: CodeType = {}; // YYNOCODE", self.n_symbols)?;
writeln!(out, "const N_STATES: usize = {}; // YYNSTATE", self.n_no_tail)?;
writeln!(out, "const N_RULES: usize = {}; // YYNRULE", self.rules.len())?;
writeln!(out, "const N_TERMINALS: usize = {}; // YYNTOKEN", self.n_terminals)?;
writeln!(out, "const MAX_SHIFT: usize = N_STATES-1; // YY_MAX_SHIFT")?;
writeln!(out, "const MIN_SHIFTREDUCE: usize = {}; // YY_MIN_SHIFTREDUCE", self.n_states)?;
writeln!(out, "const MAX_SHIFTREDUCE: usize = MIN_SHIFTREDUCE + N_RULES - 1; // YY_MAX_SHIFTREDUCE")?;
writeln!(out, "const ERROR_ACTION: usize = MAX_SHIFTREDUCE + 1; // YY_ERROR_ACTION")?;
writeln!(out, "const ACCEPT_ACTION: usize = ERROR_ACTION + 1; // YY_ACCEPT_ACTION")?;
writeln!(out, "const NO_ACTION: usize = ACCEPT_ACTION + 1; // YY_NO_ACTION")?;
writeln!(out, "const MIN_REDUCE: usize = NO_ACTION + 1; // YY_MIN_REDUCE")?;
writeln!(out, "//const MAX_REDUCE: usize = MIN_REDUCE + N_RULES - 1; // YY_MAX_REDUCE")?;
writeln!(out, "const ERROR_SYMBOL: CodeType = {}; // YYERRORSYMBOL", self.error_symbol)?;
writeln!(out, "const WITH_FALLBACK: bool = {}; // YYFALLBACK", if self.n_fallbacks>0 {"true"} else {"false"})?;
writeln!(out, "const SHIFT_COUNT: usize = {}; // YY_SHIFT_COUNT", self.shift_count)?;
writeln!(out, "const REDUCE_COUNT: usize = {}; // YY_REDUCE_COUNT", self.reduce_count)?;
writeln!(out, "//const SHIFT_MIN: i32 = {}; // YY_SHIFT_MIN", self.shift_min)?;
writeln!(out, "//const SHIFT_MAX: i32 = {}; // YY_SHIFT_MAX", self.shift_max)?;
writeln!(out, "//const REDUCE_MIN: i32 = {}; // YY_REDUCE_MIN", self.reduce_min)?;
writeln!(out, "//const REDUCE_MAX: i32 = {}; // YY_REDUCE_MAX", self.reduce_max)?;
writeln!(out, "const ACTTAB_COUNT: usize = {}; // YY_ACTTAB_COUNT", self.n_action_table_entries)?;
writeln!(out, "const TRACE: bool = {};", if self.with_trace {"true"} else {"false"})?;
if self.trace_prompt.chars().position(|c| c=='\\' || c=='"').is_none()
{ writeln!(out, "const TRACE_PROMPT: &str = \"{}\";", self.trace_prompt)?;
}
else
{ let mut tag = String::with_capacity(self.trace_prompt.len()+1);
for _i in 0 .. self.trace_prompt.len()+1
{ tag.push('#');
if !self.trace_prompt.contains(&tag)
{ break;
}
}
writeln!(out, "const TRACE_PROMPT: &str = r{tag}\"{}\"{tag};", self.trace_prompt, tag=tag)?;
}
write!(out, "const LOOKAHEAD_AND_ACTION: [LookaheadAndAction; {}] =", self.lookahead_and_action.len())?;
let mut formatter = ArrayFormatter::new(2);
for (lookahead, action) in self.lookahead_and_action.iter()
{ formatter.separ(out)?;
write!(out, "LookaheadAndAction {{lookahead: {:4}, action: {:4}}}", lookahead, action)?;
}
formatter.end(out)?;
write!(out, "const SHIFT_OFFSET: [ShiftOffsetType; {}] =", self.shift_offset.len())?;
let mut formatter = ArrayFormatter::new(16);
for offset in self.shift_offset.iter()
{ formatter.separ(out)?;
write!(out, "{:4}", offset)?;
}
formatter.end(out)?;
write!(out, "const REDUCE_OFFSET: [ReduceOffsetType; {}] =", self.reduce_offset.len())?;
let mut formatter = ArrayFormatter::new(16);
for offset in self.reduce_offset.iter()
{ formatter.separ(out)?;
write!(out, "{:4}", offset)?;
}
formatter.end(out)?;
write!(out, "const DEFAULT: [ActionType; {}] =", self.default.len())?;
let mut formatter = ArrayFormatter::new(16);
for action in self.default.iter()
{ formatter.separ(out)?;
write!(out, "{:4}", action)?;
}
formatter.end(out)?;
lempar_reader.copy_part(out)?;
write!(out, "const FALLBACK: [CodeType; {}] =", self.fallback.len())?;
let mut formatter = ArrayFormatter::new(1);
for (index, name, to_name) in self.fallback.iter()
{ formatter.separ(out)?;
write!(out, "{:3} /* {:10} => {} */", index, name, to_name)?;
}
formatter.end(out)?;
lempar_reader.copy_part(out)?;
write!(out, "const TOKEN_NAMES: [&str; {}] =", self.token_names.len())?;
let mut formatter = ArrayFormatter::new(1);
for name in self.token_names.iter()
{ formatter.separ(out)?;
write!(out, "{:<15}", format!("\"{}\"", name))?;
}
formatter.end(out)?;
lempar_reader.copy_part(out)?;
write!(out, "const RULE_NAMES: [&str; {}] =", self.rules.len())?;
let mut formatter = ArrayFormatter::new(1);
for rule in self.rules.iter()
{ formatter.separ(out)?;
write!(out, "\"{}\"", rule)?;
}
formatter.end(out)?;
lempar_reader.copy_part(out)?;
write!(out, "const RULES_INFO: [CodeType; {}] =", self.rules.len())?;
let mut formatter = ArrayFormatter::new(16);
for rule in self.rules.iter()
{ formatter.separ(out)?;
write!(out, "{:4}", rule.lhs.borrow().index)?;
}
formatter.end(out)?;
lempar_reader.copy_part(out)?;
for rule in self.rules.iter()
{ if !rule.code.is_empty()
{ write!(out, "\t\t\t{} =>", rule.index)?;
let has_data_type = !rule.lhs.borrow().data_type.is_empty();
let mut next_indent = "\t\t\t\t";
if has_data_type && !rule.lhs_start
{ write!(out, " MinorType::Symbol{}\n\t\t\t(\t{{", rule.lhs.borrow().dtnum)?;
next_indent = "\t\t\t\t\t";
}
else
{ write!(out, "\n\t\t\t{{")?;
}
let mut indent = "\t";
let mut n_aliases = 0;
let n_args = rule.rhs.len();
for i in (0 .. n_args).rev()
{ let has_alias = !rule.rhs_aliases[i].is_empty();
if has_alias
{ n_aliases += 1;
writeln!(out, "{}let arg{} = self.stack.pop().unwrap();", indent, i+1)?;
}
else
{ writeln!(out, "{}self.stack.pop();", indent)?;
}
indent = next_indent;
}
if n_aliases > 0
{ write!(out, "{}{}match {}", indent, if rule.lhs_start {"let result = "} else {""}, if n_aliases>1 {"("} else {""})?;
let mut comma = "";
for i in 0 .. n_args
{ if !rule.rhs_aliases[i].is_empty()
{ write!(out, "{}arg{}.minor", comma, i+1)?;
comma = ", ";
}
}
write!(out, "{}\n{}{{\t{}", if n_aliases>1 {")"} else {""}, indent, if n_aliases>1 {"("} else {""})?;
comma = "";
for (i, rhs) in rule.rhs.iter().enumerate()
{ if !rule.rhs_aliases[i].is_empty()
{ write!(out, "{}MinorType::Symbol{}(arg{})", comma, rhs.borrow().dtnum, i+1)?;
comma = ", ";
}
}
write!(out, "{} => super::rules::do_rule_{}(&mut self.extra", if n_aliases>1 {")"} else {""}, rule.index)?;
for i in 0 .. n_args
{ if !rule.rhs_aliases[i].is_empty()
{ write!(out, ", arg{}", i+1)?;
}
}
if rule.lhs_start
{ writeln!(out, "),\n{}\t_ => unreachable!()\n{}}};\n\t\t\t\tself.result = Some(result);\n\t\t\t\tMinorType::None\n\t\t\t}},", indent, indent)?;
}
else if has_data_type
{ writeln!(out, "),\n{}\t_ => unreachable!()\n{}}}\n\t\t\t\t}}\n\t\t\t),", indent, indent)?;
}
else
{ writeln!(out, "),\n{}\t_ => unreachable!()\n{}}};\n\t\t\t\tMinorType::None\n\t\t\t}},", indent, indent)?;
}
}
else
{ if rule.lhs_start
{ writeln!(out, "{}self.result = Some(super::rules::do_rule_{}(&mut self.extra));\n\t\t\t\tMinorType::None\n\t\t\t}},", indent, rule.index)?;
}
else if has_data_type
{ writeln!(out, "{}super::rules::do_rule_{}(&mut self.extra)\n\t\t\t}}),", indent, rule.index)?;
}
else
{ writeln!(out, "{}super::rules::do_rule_{}(&mut self.extra);\n\t\t\t\tMinorType::None\n\t\t\t}},", indent, rule.index)?;
}
}
}
}
writeln!(out, "\t\t\t_ =>\n\t\t\t{{\tself.stack.pop(); MinorType::None\n\t\t\t}}")?;
lempar_reader.copy_part(out)?;
writeln!(out, "\n}}\n\n\nmod rules\n{{\tuse super::code::{{TokenValue, ExtraArgumentType}};")?;
for rule in self.rules.iter()
{ let rule = rule;
if !rule.code.is_empty()
{ write!(out, "\n\t/// {}\n", rule)?;
write!(out, "\t#[inline]\n\t#[allow(non_snake_case, unused_variables)]\n")?;
write!(out, "\tpub fn do_rule_{}(extra: &mut ExtraArgumentType", rule.index)?;
for (i, rhs_rule) in rule.rhs.iter().enumerate()
{ if !rule.rhs_aliases[i].is_empty()
{ let rhs_rule = rhs_rule.borrow();
let data_type = if rhs_rule.index < self.n_terminals
{ "TokenValue"
}
else
{ &rhs_rule.data_type
};
write!(out, ", {}: {}", rule.rhs_aliases[i], if data_type.is_empty() {"()"} else {data_type})?;
}
}
write!(out, ")")?;
let lhs = rule.lhs.borrow();
if !lhs.data_type.is_empty()
{ write!(out, " -> {}", lhs.data_type)?;
}
write!(out, "\n\t{{\t")?;
let mut it = rule.code.chars();
'l: while let Some(mut c) = it.next()
{ 'm:while c == '\n'
{ write!(out, "\n\t\t")?;
while let Some(c2) = it.next()
{ if c2=='\n' || !c2.is_ascii_whitespace()
{ c = c2;
continue 'm;
}
}
break 'l;
}
write!(out, "{}", c)?;
}
writeln!(out, "\n\t}}")?;
}
}
writeln!(out, "}}\n")?;
lempar_reader.copy_part(out)?;
if !self.extracode.is_empty()
{ writeln!(out, "\n\n{}", self.extracode)?;
}
Ok(())
}
pub fn gen_log<W>(&self, out: &mut W, basisflag: bool, show_precedence_conflict: bool) -> io::Result<()> where W: io::Write
{ for stp in self.states.array[0 .. self.states.n_no_tail].iter()
{ writeln!(out, "State {}:", stp.n_state)?;
for cfp in stp.configurations.iter()
{ if basisflag
{ let mut is_basis = false;
for bp in stp.basis.iter()
{ if *cfp.borrow() == *bp.borrow()
{ is_basis = true;
break;
}
}
if !is_basis
{ continue;
}
}
if cfp.borrow().dot == self.rules[cfp.borrow().n_rule].rhs.len()
{ let buf = format!("({})", self.rules[cfp.borrow().n_rule].index);
write!(out, " {:>5} ", buf)?;
}
else
{ write!(out, " ")?;
}
Self::config_print(&self.rules, out, cfp)?;
writeln!(out, "")?;
}
writeln!(out, "")?;
for action in stp.actions.iter()
{ if Self::print_action(&self.rules, action, out, 30, show_precedence_conflict)?
{ writeln!(out, "")?;
}
}
writeln!(out, "")?;
}
writeln!(out, "----------------------------------------------------")?;
writeln!(out, "Symbols:")?;
for i in 0 .. self.symbols.n_symbols
{ let sp = &self.symbols.array[i];
write!(out, " {:>3}: {}", i, sp.borrow().name)?;
if sp.borrow().typ == SymbolType::NONTERMINAL
{ write!(out, ":")?;
if sp.borrow().lambda
{ write!(out, " <lambda>")?;
}
for j in 0 .. self.symbols.n_terminals
{ if sp.borrow().firstset.len()>0 && sp.borrow().firstset.contains(j)
{ write!(out, " {}", self.symbols.array[j].borrow().name)?;
}
}
}
writeln!(out, "")?;
}
Ok(())
}
fn config_print<W>(rules: &Vec<Rule>, out: &mut W, cfp: &Rc<RefCell<Config>>) -> io::Result<()> where W: io::Write
{ write!(out, "{} ::=", rules[cfp.borrow().n_rule].lhs.borrow().name)?;
for i in 0 ..= rules[cfp.borrow().n_rule].rhs.len()
{ if i == cfp.borrow().dot
{ write!(out, " *")?;
}
if i == rules[cfp.borrow().n_rule].rhs.len()
{ break;
}
write!(out, " {}", rules[cfp.borrow().n_rule].rhs[i].borrow().name)?;
}
Ok(())
}
fn print_action<W>(rules: &Vec<Rule>, action: &Rc<RefCell<Action>>, out: &mut W, indent: usize, show_precedence_conflict: bool) -> io::Result<bool> where W: io::Write
{ let mut result = false;
match action.borrow().typ
{ ActionType::Shift =>
{ if let StateOrRule::State(n_state) = action.borrow().x
{ write!(out, "{:>w$} shift {:<7}", action.borrow().sp.borrow().name, n_state, w=indent)?;
result = true;
}
}
ActionType::Reduce =>
{ if let StateOrRule::Rule(n_rule) = action.borrow().x
{ write!(out, "{:>w$} reduce {:<7}", action.borrow().sp.borrow().name, rules[n_rule].index, w=indent)?;
Self::rule_print(out, &rules[n_rule], std::usize::MAX)?;
result = true;
}
}
ActionType::ShiftReduce =>
{ if let StateOrRule::Rule(n_rule) = action.borrow().x
{ write!(out, "{:>w$} shift-reduce {:<7}", action.borrow().sp.borrow().name, rules[n_rule].index, w=indent)?;
Self::rule_print(out, &rules[n_rule], std::usize::MAX)?;
result = true;
}
}
ActionType::Accept =>
{ write!(out, "{:>w$} accept", action.borrow().sp.borrow().name, w=indent)?;
result = true;
}
ActionType::Error =>
{ write!(out, "{:>w$} error", action.borrow().sp.borrow().name, w=indent)?;
result = true;
}
ActionType::SrConflict | ActionType::RrConflict =>
{ if let StateOrRule::Rule(n_rule) = action.borrow().x
{ write!(out, "{:>w$} reduce {:<7} ** Parsing conflict **", action.borrow().sp.borrow().name, rules[n_rule].index, w=indent)?;
result = true;
}
}
ActionType::SsConflict =>
{ if let StateOrRule::State(n_state) = action.borrow().x
{ write!(out, "{:>w$} shift {:<7} ** Parsing conflict **", action.borrow().sp.borrow().name, n_state, w=indent)?;
result = true;
}
}
ActionType::ShResolved =>
{ if show_precedence_conflict
{ if let StateOrRule::State(n_state) = action.borrow().x
{ write!(out, "{:>w$} shift {:<7} -- dropped by precedence", action.borrow().sp.borrow().name, n_state, w=indent)?;
result = true;
}
}
}
ActionType::RdResolved =>
{ if show_precedence_conflict
{ if let StateOrRule::Rule(n_rule) = action.borrow().x
{ write!(out, "{:>w$} reduce {:<7} -- dropped by precedence", action.borrow().sp.borrow().name, rules[n_rule].index, w=indent)?;
result = true;
}
}
}
ActionType::NotUsed => {}
}
Ok(result)
}
fn rule_print<W>(out: &mut W, rule: &Rule, cursor: usize) -> io::Result<()> where W: io::Write
{ write!(out, "{} ::=", rule.lhs.borrow().name)?;
for (i, symbol) in rule.rhs.iter().enumerate()
{ if i == cursor
{ write!(out, " *")?;
}
write!(out, " {}", symbol.borrow().name)?;
}
if cursor == rule.rhs.len()
{ write!(out, " *")?;
}
Ok(())
}
pub fn gen_y<W>(&self, out: &mut W) -> io::Result<()> where W: io::Write
{ writeln!(out, "// Symbols:")?;
let mut maxlen = 10;
for sp in self.symbols.array[0 .. self.symbols.n_symbols].iter()
{ if sp.borrow().name.len() > maxlen
{ maxlen = sp.borrow().name.len();
}
}
let mut ncolumns = 76 / (maxlen+5);
if ncolumns < 1
{ ncolumns = 1;
}
let skip = (self.symbols.n_symbols+ncolumns-1) / ncolumns;
for i in 0 .. skip
{ write!(out, "//")?;
for j in (i .. self.symbols.n_symbols).step_by(skip)
{ let sp = &self.symbols.array[j];
assert_eq!(sp.borrow().index, j);
write!(out, " {:3} {:<w$}", maxlen, sp.borrow().name, w=maxlen)?;
}
writeln!(out, "")?;
}
writeln!(out, "")?;
if !self.start_name.is_empty()
{ writeln!(out, "%start_symbol {{{}}}", self.start_name)?;
}
if !self.token_type.is_empty()
{ writeln!(out, "%token_type {{{}}}", self.token_type)?;
}
if !self.vartype.is_empty()
{ writeln!(out, "%default_type {{{}}}", self.vartype)?;
}
if !self.extra_argument_type.is_empty()
{ writeln!(out, "%extra_argument {{{}}}", self.extra_argument_type)?;
}
let mut prec = 0;
loop
{ let mut has_greater_prec = false;
let mut started = false;
for symbol in self.symbols.array[1 .. self.symbols.n_terminals].iter()
{ if symbol.borrow().prec >= prec
{ if symbol.borrow().prec > prec
{ has_greater_prec = true;
}
else
{ if !started
{ write!(out, "{}", if symbol.borrow().assoc== Associativity::LEFT {"%left"} else if symbol.borrow().assoc== Associativity::RIGHT {"%right"} else {"%nonassoc"})?;
started = true;
}
write!(out, " {}", symbol.borrow().name)?;
}
}
}
if started
{ writeln!(out, ".")?;
}
if !has_greater_prec
{ break;
}
prec += 1;
}
for symbol in self.symbols.array[self.symbols.n_terminals+1 .. self.symbols.n_symbols].iter()
{ let symbol = symbol.borrow();
writeln!(out, "%type {} {{{}}}", symbol.name, symbol.data_type)?;
}
for rule in self.rules.iter()
{ write!(out, "{}", rule.lhs.borrow().name)?;
if rule.lhs.borrow().dtnum != 0
{ write!(out, "(RESULT)")?;
}
write!(out, " ::=")?;
for i in 0 .. rule.rhs.len()
{ let sp = &rule.rhs[i];
write!(out, " {}", sp.borrow().name)?;
if rule.rhs_aliases.len()>i && !rule.rhs_aliases[i].is_empty()
{ write!(out, "({})", rule.rhs_aliases[i])?;
}
}
write!(out, ".")?;
if let Some(ref precsym) = rule.precsym
{ write!(out, " [{}]", precsym.borrow().name)?;
}
if !rule.code.is_empty()
{ writeln!(out, " {{\n{}\n}}", rule.code)?;
}
writeln!(out, "")?;
}
Ok(())
}
}
impl TryFrom<LemonMintBuilder> for LemonMint
{ type Error = LemonMintError;
fn try_from(builder: LemonMintBuilder) -> ParserResult<Self>
{ builder.try_into_lemon()
}
}
#[cfg(not(feature = "with-debug"))]
fn dump_symbols(_symbols: &Symbols, _rules: &Vec<Rule>)
{
}
#[cfg(not(feature = "with-debug"))]
fn dump_states(_states: &States, _rules: &Vec<Rule>, _symbols: &Symbols, _n: i32)
{
}
#[cfg(feature = "with-debug")]
fn dump_symbols(symbols: &Symbols, rules: &Vec<Rule>)
{ use std::io::prelude::*;
let mut out = File::create("/tmp/symbols-rust.js").unwrap();
for s in symbols.array.iter()
{ let s = s.borrow();
writeln!(out, "{} ({}) {:?} #{} {:?} {} {}LAM:", s.name, s.index, s.typ, s.dtnum, s.assoc, s.prec, if s.lambda {""} else {"!"}).unwrap();
if s.firstset.len() > 0
{ write!(out, " ").unwrap();
for (j, v) in s.firstset.data.iter().enumerate()
{ write!(out, "{}{}", if j%4==0&&j>0 {" "} else {""}, if *v {'y'} else {'n'}).unwrap();
}
writeln!(out).unwrap();
}
for n_rule in s.sym_rules_arr.iter()
{ let rule = &rules[*n_rule];
writeln!(out, " {} = {}", rule.index, rule.code).unwrap();
}
}
}
#[cfg(feature = "with-debug")]
fn dump_states(states: &States, rules: &Vec<Rule>, symbols: &Symbols, n: i32)
{ use std::io::prelude::*;
let mut out = File::create(format!("/tmp/states-{}-rust.js", n)).unwrap();
for s in states.array.iter()
{ writeln!
( out,
"{}. nTknAct={}, nNtAct={}, iTknOfst={}, iNtOfst={}, iDfltReduce={}, {} dfR{}[{}]",
s.n_state,
s.n_token_act,
s.n_nt_act,
if s.token_offset==std::isize::MIN {-1000} else {s.token_offset},
if s.nt_offset==std::isize::MIN {-1000} else {s.nt_offset},
if s.default_reduce_action==std::usize::MAX {-1} else {s.default_reduce_action as isize},
if s.auto_reduce {"A"} else {"!A"},
if s.default_reduce_rule==std::usize::MAX {-1} else {s.default_reduce_rule as isize},
if s.default_reduce_rule==std::usize::MAX {"".to_string()} else {rules[s.default_reduce_rule].lhs.borrow().name.to_string()}
).unwrap();
for cfp in s.basis.iter()
{ let cfp = cfp.borrow();
writeln!(out, " bs{} R{}[{}] .{}", cfp.n_state as isize, cfp.n_rule, rules[cfp.n_rule].lhs.borrow().name, cfp.dot).unwrap();
}
for cfp in s.configurations.iter()
{ let cfp = cfp.borrow();
writeln!(out, " cf{} R{}[{}] .{}", cfp.n_state as isize, cfp.n_rule, rules[cfp.n_rule].lhs.borrow().name, cfp.dot).unwrap();
let mut delim = "";
write!(out, "{:<12}[", "").unwrap();
for (j, v) in cfp.fws.data.iter().enumerate()
{ if *v
{ write!(out, "{}{}", delim, symbols.array[j].borrow().name).unwrap();
delim = " ";
}
}
writeln!(out, "]").unwrap();
for cfp in cfp.fplp.iter()
{ write!(out, "{:<12}{} (state {:2}) ", "", "To ", cfp.borrow().n_state as isize).unwrap();
LemonMint::config_print(rules, &mut out, cfp).unwrap();
writeln!(out).unwrap();
}
for cfp in cfp.bplp.iter()
{ write!(out, "{:<12}{} (state {:2}) ", "", "From", cfp.borrow().n_state as isize).unwrap();
LemonMint::config_print(rules, &mut out, cfp).unwrap();
writeln!(out).unwrap();
}
}
for action in s.actions.iter()
{ if LemonMint::print_action(rules, action, &mut out, 30, true).unwrap()
{ writeln!(out).unwrap();
}
}
}
}