use lexer::dfa::codegen;
use grammar::parse_tree::InternToken;
use grammar::repr::Grammar;
use rust::RustWrite;
use std::io::{self, Write};
pub fn compile<W: Write>(
grammar: &Grammar,
intern_token: &InternToken,
out: &mut RustWrite<W>)
-> io::Result<()>
{
let prefix = &grammar.prefix;
rust!(out, "mod {}intern_token {{", prefix);
try!(out.write_standard_uses(prefix));
rust!(out, "pub struct {}Matcher<'input> {{", prefix);
rust!(out, "text: &'input str,"); rust!(out, "consumed: usize,"); rust!(out, "}}");
rust!(out, "");
try!(codegen::compile_tokenize_fn(prefix, &intern_token.dfa, out));
rust!(out, "");
rust!(out, "impl<'input> {}Matcher<'input> {{", prefix);
rust!(out, "pub fn new(s: &'input str) -> {}Matcher<'input> {{", prefix);
rust!(out, "{}Matcher {{ text: s, consumed: 0 }}", prefix);
rust!(out, "}}");
rust!(out, "}}");
rust!(out, "");
rust!(out, "impl<'input> Iterator for {}Matcher<'input> {{", prefix);
rust!(out, "type Item = Result<(usize, (usize, &'input str), usize), \
{}lalrpop_util::ParseError<usize,(usize, &'input str),{}>>;",
prefix, grammar.types.error_type());
rust!(out, "");
rust!(out, "fn next(&mut self) -> Option<Self::Item> {{");
rust!(out, "let {}text = self.text.trim_left();", prefix);
rust!(out, "let {}whitespace = self.text.len() - {}text.len();", prefix, prefix);
rust!(out, "let {}start_offset = self.consumed + {}whitespace;", prefix, prefix);
rust!(out, "if {}text.is_empty() {{", prefix);
rust!(out, "self.text = {}text;", prefix);
rust!(out, "self.consumed = {}start_offset;", prefix);
rust!(out, "None");
rust!(out, "}} else {{");
rust!(out, "match {}tokenize({}text) {{", prefix, prefix);
rust!(out, "Some(({}index, {}length)) => {{", prefix, prefix);
rust!(out, "let {}result = &{}text[..{}length];", prefix, prefix, prefix);
rust!(out, "let {}remaining = &{}text[{}length..];", prefix, prefix, prefix);
rust!(out, "let {}end_offset = {}start_offset + {}length;", prefix, prefix, prefix);
rust!(out, "self.text = {}remaining;", prefix);
rust!(out, "self.consumed = {}end_offset;", prefix);
rust!(out, "Some(Ok(({}start_offset, ({}index, {}result), {}end_offset)))",
prefix, prefix, prefix, prefix);
rust!(out, "}}"); rust!(out, "None => {{");
rust!(out, "Some(Err({}lalrpop_util::ParseError::InvalidToken {{ location: {}start_offset }}))",
prefix, prefix);
rust!(out, "}}"); rust!(out, "}}"); rust!(out, "}}");
rust!(out, "}}"); rust!(out, "}}"); rust!(out, "}}");
Ok(())
}