#include <../src/parse/parser_state.hh>

namespace CppClingo::Input::Parse {

// Note that re2c treats the 0 byte like any other character. Hence, none of
// the regular expressions below consume null bytes except for the ones
// handling the end of input.

/*!re2c
    re2c:api:style             = free-form;
    re2c:define:YYCTYPE        = "unsigned char";
    re2c:define:YYCURSOR       = state_.cursor();
    re2c:define:YYMARKER       = state_.marker();
    re2c:define:YYCTXMARKER    = state_.ctxmarker();
    re2c:define:YYLIMIT        = state_.limit();
    re2c:define:YYFILL         = "state_.fill(@@, YYMAXFILL);";
    re2c:define:YYGETCONDITION = cond_;
    re2c:define:YYSETCONDITION = "cond_ = @@;";
    re2c:cond:goto             = "continue;";

    POS        = [0]|([1-9][0-9]*);
    DEC        = POS|([1-9][0-9]{0,2}(['][0-9]{3})+);
    HEX        = "0x"([0-9A-Fa-f]+);
    OCT        = "0o"([1-7]+);
    BIN        = "0b"([0-1]+);
    NUMBER     = DEC | HEX | OCT | BIN;
    WS         = [\t\r\n ]*;
    IDENTIFIER = [_']*[a-z]['A-Za-z0-9_]*;
    VARIABLE   = [_']*[A-Z]['A-Za-z0-9_]*;
    STRING     = "\"" ([^\\"\n\000]|"\\\""|"\\\\"|"\\n")* "\"";
    INCSTRING  = "<" IDENTIFIER ">";
    THEORYOP   = [/!<=>+\-*\\?&@|:;~^.]+;
    KEYWORD    = "#" [a-zA-Z0-9_]*;
    SUP        = "#sup"("remum")?;
    INF        = "#inf"("imum")?;
    MAXI       = "#maximi"[zs]"e";
    MINI       = "#minimi"[zs]"e";
    NULL       = [\000];
    END        = [\000\n];
    ANY        = [^\000];
    DOT        = [^\000\n];
    COMMENT    = "%"[^*\n\000][^\000\n]*;
    SP         = [ ];
    NL         = [\r\n]+;
    NOTEND     = ([^#] | '#' [^e] | '#' 'e' [^n] | '#' 'e' 'n' [^d])*;
    ACCESSOR   = [.] IDENTIFIER | "[" POS "]";
    CONVERSION = [!][rs];
    ALIGN      = [<>=^];
    SIGN       = [-+ ];
    GROUPING   = [,_];
    TYPE       = [bcdoxXns];
    FLIT       = ([^\\"\n{}\000] | "\\\"" | "\\\\" | "\\n" | "{{" | "}}")*;
    FSPEC      = ACCESSOR* CONVERSION? ([:] (DOT? ALIGN)? SIGN? [#]? POS? GROUPING? TYPE?)?;
*/

/*!header:re2c:on*/
/*!max:re2c*/
/*!conditions:re2c*/
/*!header:re2c:off*/

auto ParserState::lex_() -> TokenType {
    state_.start();
    for (;;) {
        /*!re2c
            <program> "asp" / SP POS       { return TokenType::aspif; }
            <program> "" / ANY             :=> normal
            <normal,theory> "not"          { return cond_ == yycnormal ? TokenType::not_ : TokenType::theory_op; }
            <normal,theory> "_"            { return TokenType::anon; }
            <normal,theory> ":"            { return TokenType::colon; }
            <normal,theory> ","            { return TokenType::comma; }
            <normal,theory> "."            { return TokenType::dot; }
            <normal,theory> "{"            { return TokenType::lbrace; }
            <normal,theory> "["            { return TokenType::lbrack; }
            <normal,theory> "("            { return TokenType::lpar; }
            <normal,theory> "}"            { return TokenType::rbrace; }
            <normal,theory> "]"            { return TokenType::rbrack; }
            <normal,theory> ")"            { return TokenType::rpar; }
            <normal,theory> ";"            { return TokenType::sem; }
            <normal,theory> ":-"           { return TokenType::if_; }
            <normal> "&"                   { return TokenType::amp; }
            <normal> "@"                   { return TokenType::at; }
            <normal> "|"                   { return TokenType::bar; }
            <normal> "\\"                  { return TokenType::bslash; }
            <normal> "^"                   { return TokenType::caret; }
            <normal> ".."                  { return TokenType::ddot; }
            <normal> "**"                  { return TokenType::dstar; }
            <normal> "="                   { return TokenType::eq; }
            <normal> ":="                  { return TokenType::assign; }
            <normal> "=="                  { return TokenType::eq; }
            <normal> ">="                  { return TokenType::ge; }
            <normal> ">"                   { return TokenType::gt; }
            <normal> "<="                  { return TokenType::le; }
            <normal> "<"                   { return TokenType::lt; }
            <normal> "-"                   { return TokenType::minus; }
            <normal> "!="                  { return TokenType::ne; }
            <normal> "<>"                  { return TokenType::ne; }
            <normal> "+"                   { return TokenType::plus; }
            <normal> "?"                   { return TokenType::qmark; }
            <normal> "/"                   { return TokenType::slash; }
            <normal> "*"                   { return TokenType::star; }
            <normal> "~"                   { return TokenType::tilde; }
            <normal> ":~"                  { return TokenType::wif; }
            <normal> "#const"              { return TokenType::const_; }
            <normal> "#parts"              { return TokenType::parts; }
            <normal> "#count"              { return TokenType::count; }
            <normal> "#defined"            { return TokenType::defined; }
            <normal> "#edge"               { return TokenType::edge; }
            <normal> "#external"           { return TokenType::external; }
            <normal> "#false"              { return TokenType::false_; }
            <normal> "#heuristic"          { return TokenType::heuristic; }
            <normal> "#include"            { return TokenType::include; }
            <normal> "#maximi"[zs]"e"      { return TokenType::maximize; }
            <normal> "#max"                { return TokenType::max; }
            <normal> "#minimi"[zs]"e"      { return TokenType::minimize; }
            <normal> "#min"                { return TokenType::min; }
            <normal> "#program"            { return TokenType::program; }
            <normal> "#project"            { return TokenType::project; }
            <normal> "#script"             { return TokenType::script; }
            <normal> "#show"               { return TokenType::show; }
            <normal> "#showf"              { return TokenType::showf; }
            <normal> "#sum"                { return TokenType::sum; }
            <normal> "#sum+"               { return TokenType::sump; }
            <normal> "#some"               { return TokenType::some; }
            <normal> "#theory"             { return TokenType::theory; }
            <normal> "#true"               { return TokenType::true_; }
            <normal> "#end"                { return TokenType::script_end; }
            <normal,theory> "%*"           { bc_ = 1; bc_cond_ = cond_; cond_ = yycblockcomment; continue; }
            <normal,theory> COMMENT        { push_comment(); continue; }
            <normal,theory> "%" / END      { push_comment(); continue; }
            <normal,theory> "#!" DOT*      { push_comment(); continue; }
            <normal,theory> IDENTIFIER     { return TokenType::id; }
            <normal,theory> INF            { return TokenType::inf; }
            <normal,theory> NUMBER         { return TokenType::num; }
            <normal,theory,include> STRING { return TokenType::str; }
            <normal,theory> SUP            { return TokenType::sup; }
            <normal,theory> VARIABLE       { return TokenType::var; }
            <normal> "f\""                 { return TokenType::fstring_start; }
            <fstring_spec> FSPEC / [}]     { return TokenType::fstring_spec; }
            <fstring> FLIT [{] / [^{]      { return TokenType::fstring_cont; }
            <fstring> FLIT ["]             { return TokenType::fstring_close; }
            <theory> THEORYOP              { return TokenType::theory_op; }
            <include> INCSTRING            { return TokenType::str_include; }
            <normal> KEYWORD               { return TokenType::error; }
            <normal,theory,include> WS     { state_.start(); continue; }
            <script> NOTEND / "#end"       { return TokenType::script_content; }
            <blockcomment> "*%" {
                if(--bc_ == 0) {
                    push_comment(true);
                    cond_ = bc_cond_;
                }
                continue;
            }
            <blockcomment> "%*"            { ++bc_; continue; }
            <blockcomment> COMMENT         { continue; }
            <blockcomment> NULL            { return TokenType::error_bc; }
            <blockcomment> ANY             { continue; }
            <program,normal,theory,include,script,fstring,fstring_spec> NULL
                                           { return state_.end(YYMAXFILL) ? TokenType::end : TokenType::error; }
            <*> *                          { return TokenType::error; }
        */
    }
}

auto ParserState::lex_str(size_t n) -> AspifToken {
    state_.start();
    for (; n > 0; --n) {
        /*!re2c
        [^\000]              { continue; }
        NULL                 { return state_.end(YYMAXFILL) ? AspifToken::end : AspifToken::error; }
        *                    { return AspifToken::error; }
        */
    }
    return AspifToken::str;
}

auto ParserState::lex_str() -> AspifToken {
    state_.start();
    for (;;) {
        /*!re2c
        [^\r\n\000]*         { return AspifToken::str; }
        NULL                 { return state_.end(YYMAXFILL) ? AspifToken::end : AspifToken::error; }
        *                    { return AspifToken::error; }
        */
    }
}

auto ParserState::lex_aspif() -> AspifToken {
    state_.start();
    for (;;) {
        /*!re2c
        DEC                  { return AspifToken::num_pos; }
        "-" DEC              { return AspifToken::num_neg; }
        SP                   { return AspifToken::space; }
        "incremental"        { return AspifToken::incremental; }
        "symbols"            { return AspifToken::symbols; }
        NL                   { return AspifToken::newline; }
        NULL                 { return state_.end(YYMAXFILL) ? AspifToken::end : AspifToken::error; }
        *                    { return AspifToken::error; }
        */
    }
}

} // namespace CppClingo::Input::Parse
