//! The main parser interface

export parse_sess;
export new_parse_sess, new_parse_sess_special_handler;
export next_node_id;
export new_parser_from_file, new_parser_etc_from_file;
export new_parser_from_source_str;
export new_parser_from_tt;
export parse_crate_from_file, parse_crate_from_crate_file;
export parse_crate_from_source_str;
export parse_expr_from_source_str, parse_item_from_source_str;
export parse_stmt_from_source_str;
export parse_from_source_str;

use parser::parser;
use attr::parser_attr;
use common::parser_common;
use ast::node_id;
use util::interner;
use diagnostic::{span_handler, mk_span_handler, mk_handler, emitter};
use lexer::{reader, string_reader};
use parse::token::{ident_interner, mk_ident_interner};

type parse_sess = @{
    cm: codemap::codemap,
    mut next_id: node_id,
    span_diagnostic: span_handler,
    interner: @ident_interner,
    // these two must be kept up to date
    mut chpos: uint,
    mut byte_pos: uint
};

fn new_parse_sess(demitter: Option<emitter>) -> parse_sess {
    let cm = codemap::new_codemap();
    return @{cm: cm,
             mut next_id: 1,
             span_diagnostic: mk_span_handler(mk_handler(demitter), cm),
             interner: mk_ident_interner(),
             mut chpos: 0u, mut byte_pos: 0u};
}

fn new_parse_sess_special_handler(sh: span_handler, cm: codemap::codemap)
    -> parse_sess {
    return @{cm: cm,
             mut next_id: 1,
             span_diagnostic: sh,
             interner: mk_ident_interner(),
             mut chpos: 0u, mut byte_pos: 0u};
}

fn parse_crate_from_file(input: &Path, cfg: ast::crate_cfg,
                         sess: parse_sess) -> @ast::crate {
    if input.filetype() == Some(~".rc") {
        parse_crate_from_crate_file(input, cfg, sess)
    } else if input.filetype() == Some(~".rs") {
        parse_crate_from_source_file(input, cfg, sess)
    } else {
        sess.span_diagnostic.handler().fatal(~"unknown input file type: " +
                                             input.to_str())
    }
}

fn parse_crate_from_crate_file(input: &Path, cfg: ast::crate_cfg,
                               sess: parse_sess) -> @ast::crate {
    let (p, rdr) = new_parser_etc_from_file(sess, cfg, input,
                                            parser::CRATE_FILE);
    let lo = p.span.lo;
    let prefix = input.dir_path();
    let leading_attrs = p.parse_inner_attrs_and_next();
    let { inner: crate_attrs, next: first_cdir_attr } = leading_attrs;
    let cdirs = p.parse_crate_directives(token::EOF, first_cdir_attr);
    sess.chpos = rdr.chpos;
    sess.byte_pos = sess.byte_pos + rdr.pos;
    let cx = @{sess: sess, cfg: /* FIXME (#2543) */ copy p.cfg};
    let companionmod = input.filestem().map(|s| Path(*s));
    let (m, attrs) = eval::eval_crate_directives_to_mod(
        cx, cdirs, &prefix, &companionmod);
    let mut hi = p.span.hi;
    p.expect(token::EOF);
    p.abort_if_errors();
    return @ast_util::respan(ast_util::mk_sp(lo, hi),
                          {directives: cdirs,
                           module: m,
                           attrs: vec::append(crate_attrs, attrs),
                           config: /* FIXME (#2543) */ copy p.cfg});
}

fn parse_crate_from_source_file(input: &Path, cfg: ast::crate_cfg,
                                sess: parse_sess) -> @ast::crate {
    let (p, rdr) = new_parser_etc_from_file(sess, cfg, input,
                                            parser::SOURCE_FILE);
    let r = p.parse_crate_mod(cfg);
    sess.chpos = rdr.chpos;
    sess.byte_pos = sess.byte_pos + rdr.pos;
    return r;
}

fn parse_crate_from_source_str(name: ~str, source: @~str, cfg: ast::crate_cfg,
                               sess: parse_sess) -> @ast::crate {
    let (p, rdr) = new_parser_etc_from_source_str(sess, cfg, name,
                                                  codemap::fss_none, source);
    let r = p.parse_crate_mod(cfg);
    p.abort_if_errors();
    sess.chpos = rdr.chpos;
    sess.byte_pos = sess.byte_pos + rdr.pos;
    return r;
}

fn parse_expr_from_source_str(name: ~str, source: @~str, cfg: ast::crate_cfg,
                              sess: parse_sess) -> @ast::expr {
    let (p, rdr) = new_parser_etc_from_source_str(sess, cfg, name,
                                                  codemap::fss_none, source);
    let r = p.parse_expr();
    p.abort_if_errors();
    sess.chpos = rdr.chpos;
    sess.byte_pos = sess.byte_pos + rdr.pos;
    return r;
}

fn parse_item_from_source_str(name: ~str, source: @~str, cfg: ast::crate_cfg,
                              +attrs: ~[ast::attribute],
                              sess: parse_sess) -> Option<@ast::item> {
    let (p, rdr) = new_parser_etc_from_source_str(sess, cfg, name,
                                                  codemap::fss_none, source);
    let r = p.parse_item(attrs);
    p.abort_if_errors();
    sess.chpos = rdr.chpos;
    sess.byte_pos = sess.byte_pos + rdr.pos;
    return r;
}

fn parse_stmt_from_source_str(name: ~str, source: @~str, cfg: ast::crate_cfg,
                              +attrs: ~[ast::attribute],
                              sess: parse_sess) -> @ast::stmt {
    let (p, rdr) = new_parser_etc_from_source_str(sess, cfg, name,
                                                  codemap::fss_none, source);
    let r = p.parse_stmt(attrs);
    p.abort_if_errors();
    sess.chpos = rdr.chpos;
    sess.byte_pos = sess.byte_pos + rdr.pos;
    return r;
}

fn parse_from_source_str<T>(f: fn (p: parser) -> T,
                            name: ~str, ss: codemap::file_substr,
                            source: @~str, cfg: ast::crate_cfg,
                            sess: parse_sess)
    -> T
{
    let (p, rdr) = new_parser_etc_from_source_str(sess, cfg, name, ss,
                                                  source);
    let r = f(p);
    if !p.reader.is_eof() {
        p.reader.fatal(~"expected end-of-string");
    }
    p.abort_if_errors();
    sess.chpos = rdr.chpos;
    sess.byte_pos = sess.byte_pos + rdr.pos;
    move r
}

fn next_node_id(sess: parse_sess) -> node_id {
    let rv = sess.next_id;
    sess.next_id += 1;
    // ID 0 is reserved for the crate and doesn't actually exist in the AST
    assert rv != 0;
    return rv;
}

fn new_parser_etc_from_source_str(sess: parse_sess, cfg: ast::crate_cfg,
                                  +name: ~str, +ss: codemap::file_substr,
                                  source: @~str) -> (parser, string_reader) {
    let ftype = parser::SOURCE_FILE;
    let filemap = codemap::new_filemap_w_substr
        (name, ss, source, sess.chpos, sess.byte_pos);
    sess.cm.files.push(filemap);
    let srdr = lexer::new_string_reader(sess.span_diagnostic, filemap,
                                        sess.interner);
    return (parser(sess, cfg, srdr as reader, ftype), srdr);
}

fn new_parser_from_source_str(sess: parse_sess, cfg: ast::crate_cfg,
                              +name: ~str, +ss: codemap::file_substr,
                              source: @~str) -> parser {
    let (p, _) = new_parser_etc_from_source_str(sess, cfg, name, ss, source);
    move p
}


fn new_parser_etc_from_file(sess: parse_sess, cfg: ast::crate_cfg,
                            path: &Path, ftype: parser::file_type) ->
   (parser, string_reader) {
    let res = io::read_whole_file_str(path);
    match res {
      result::Ok(_) => { /* Continue. */ }
      result::Err(e) => sess.span_diagnostic.handler().fatal(e)
    }
    let src = @result::unwrap(res);
    let filemap = codemap::new_filemap(path.to_str(), src,
                                       sess.chpos, sess.byte_pos);
    sess.cm.files.push(filemap);
    let srdr = lexer::new_string_reader(sess.span_diagnostic, filemap,
                                        sess.interner);
    return (parser(sess, cfg, srdr as reader, ftype), srdr);
}

fn new_parser_from_file(sess: parse_sess, cfg: ast::crate_cfg, path: &Path,
                        ftype: parser::file_type) -> parser {
    let (p, _) = new_parser_etc_from_file(sess, cfg, path, ftype);
    move p
}

fn new_parser_from_tt(sess: parse_sess, cfg: ast::crate_cfg,
                      tt: ~[ast::token_tree]) -> parser {
    let trdr = lexer::new_tt_reader(sess.span_diagnostic, sess.interner,
                                    None, tt);
    return parser(sess, cfg, trdr as reader, parser::SOURCE_FILE)
}
