use analysis;
use fsa::dfa;
use regex;
use unicode;
use syntax::ast::Expr;
use syntax::ast::Ident;
use syntax::ast::Name;
use syntax::ast::Ty;
use syntax::codemap::Span;
use syntax::ext::base::ExtCtxt;
use syntax::ext::base::MacResult;
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;
pub struct Rule {
pub pattern: regex::Regex,
pub action: P<Expr>
}
pub struct Condition {
pub name: Name,
pub rules: Vec<Rule>,
pub span: Span
}
pub type Prop = (Name, P<Ty>, P<Expr>);
pub struct LexerDef {
pub tokens: Ident,
pub ident: Ident,
pub defs: Vec<regex::Regex>,
pub properties: Vec<Prop>,
pub conditions: Vec<Condition>
}
pub struct Lexer {
pub tokens:Ident,
pub ident:Ident,
pub auto: dfa::Automaton<regex::Action>,
pub actions: Vec<P<Expr>>,
pub conditions: Vec<(Name, usize)>,
pub properties: Vec<Prop>
}
impl Lexer {
pub fn new(def: LexerDef, cx: &ExtCtxt) -> Lexer {
let dummy_expr = cx.expr_unreachable(cx.call_site());
let mut acts = vec!(dummy_expr);
let mut id = 1usize;
let mut dfas = dfa::Automaton::new();
let mut conds = Vec::new();
for cond in def.conditions.iter() {
let mut asts = Vec::with_capacity(cond.rules.len());
let name = cond.name;
for &Rule { ref pattern, ref action } in cond.rules.iter() {
asts.push((pattern.clone(), regex::Action(id)));
acts.push(action.clone());
id += 1;
}
info!("building...");
let nfa = regex::build_nfa::<unicode::Ascii>(&asts[..], &def.defs[..]);
info!("determinizing...");
conds.push((name, dfas.determinize(&nfa)));
}
for &s in dfas.initials.iter() {
let regex::Action(act) = dfas.states[s].data;
if act != 0 {
cx.struct_span_err(acts[act].span, "this rule accepts the empty word")
.help("this might cause the automaton to loop on some inputs")
.emit();
}
}
info!("checking reachability and completeness... ");
let analysis::Analysis { unreachable, incomplete } =
analysis::check_automaton(&dfas, acts.len());
for regex::Action(act) in unreachable.into_iter() {
cx.struct_span_err(acts[act].span, "unreachable pattern")
.help("make sure it is not included in another pattern ; latter patterns have precedence")
.emit();
}
for cond in incomplete.into_iter() {
cx.struct_span_err(def.conditions[cond].span, "this automaton is incomplete")
.help("maybe add a catch-all rule?")
.emit();
}
cx.parse_sess.span_diagnostic.abort_if_errors();
info!("minimizing...");
Lexer {
tokens: def.tokens,
ident: def.ident,
auto: dfas.minimize(),
actions: acts,
conditions: conds,
properties: def.properties.clone()
}
}
pub fn gen_code<'cx>(&self, cx: &'cx mut ExtCtxt, sp: Span) -> Box<MacResult + 'cx> {
info!("generating code...");
::codegen::codegen(self, cx, sp) as Box<MacResult + 'cx>
}
}