/***********************************************************************/
/*                                                                     */
/*                           Objective Caml                            */
/*                                                                     */
/*            Xavier Leroy, projet Cristal, INRIA Rocquencourt         */
/*                                                                     */
/*  Copyright 1996 Institut National de Recherche en Informatique et   */
/*  en Automatique.  All rights reserved.  This file is distributed    */
/*  under the terms of the GNU Library General Public License, with    */
/*  the special exception on linking described in file ../LICENSE.     */
/*                                                                     */
/***********************************************************************/

/* $Id: parsing.c 8983 2008-08-06 09:38:25Z xleroy $ */

/* The PDA automaton for parsers generated by camlyacc */

/* The pushdown automata */

/**
 * caml_lex_array("abcd")
 * [25185, 25699]
 * @param s
 * @returns {any[]}
 * TODO: duplicated with module {!Caml_lex}
 */
function caml_lex_array(s) {
    var l = s.length / 2;
    var a = new Array(l);
    for (var i = 0; i < l; i++)
        a[i] = (s.charCodeAt(2 * i) | (s.charCodeAt(2 * i + 1) << 8)) << 16 >> 16;
    return a;
}
/**
 * Note that TS enum is not friendly to Closure compiler
 * @enum{number}
 */
var Automata = {
    START: 0,
    LOOP: 6,
    TOKEN_READ: 1,
    TEST_SHIFT: 7,
    ERROR_DETECTED: 5,
    SHIFT: 8,
    SHIFT_RECOVER: 9,
    STACK_GROWN_1: 2,
    REDUCE: 10,
    STACK_GROWN_2: 3,
    SEMANTIC_ACTION_COMPUTED: 4
};
/**
 * @enum{number}
 */
var Result = {
    READ_TOKEN: 0,
    RAISE_PARSE_ERROR: 1,
    GROW_STACKS_1: 2,
    GROW_STACKS_2: 3,
    COMPUTE_SEMANTIC_ACTION: 4,
    CALL_ERROR_FUNCTION: 5
};
var PARSER_TRACE = false;
;

var parse_engine = (function (tables /* parser_table */, env /* parser_env */, cmd /* parser_input*/, arg /* Obj.t*/) {
    var ERRCODE = 256;
    //var START = 0;
    //var TOKEN_READ = 1;
    //var STACKS_GROWN_1 = 2;
    //var STACKS_GROWN_2 = 3;
    //var SEMANTIC_ACTION_COMPUTED = 4;
    //var ERROR_DETECTED = 5;
    //var loop = 6;
    //var testshift = 7;
    //var shift = 8;
    //var shift_recover = 9;
    //var reduce = 10;
    // Parsing.parser_env
    var env_s_stack = 's_stack'; // array
    var env_v_stack = 'v_stack'; // array
    var env_symb_start_stack = 'symb_start_stack'; // array
    var env_symb_end_stack = 'symb_end_stack'; // array
    var env_stacksize = 'stacksize';
    var env_stackbase = 'stackbase';
    var env_curr_char = 'curr_char';
    var env_lval = 'lval'; // Obj.t
    var env_symb_start = 'symb_start'; // position
    var env_symb_end = 'symb_end'; // position
    var env_asp = 'asp';
    var env_rule_len = 'rule_len';
    var env_rule_number = 'rule_number';
    var env_sp = 'sp';
    var env_state = 'state';
    var env_errflag = 'errflag';
    // Parsing.parse_tables
    // var _tbl_actions = 1;
    var tbl_transl_const = 'transl_const'; // array
    var tbl_transl_block = 'transl_block'; // array
    var tbl_lhs = 'lhs';
    var tbl_len = 'len';
    var tbl_defred = 'defred';
    var tbl_dgoto = 'dgoto';
    var tbl_sindex = 'sindex';
    var tbl_rindex = 'rindex';
    var tbl_gindex = 'gindex';
    var tbl_tablesize = 'tablesize';
    var tbl_table = 'table';
    var tbl_check = 'check';
    // var _tbl_error_function = 14;
    // var _tbl_names_const = 15;
    // var _tbl_names_block = 16;
    if (!tables.preprocessed) {
        tables.defred = caml_lex_array(tables[tbl_defred]);
        tables.sindex = caml_lex_array(tables[tbl_sindex]);
        tables.check = caml_lex_array(tables[tbl_check]);
        tables.rindex = caml_lex_array(tables[tbl_rindex]);
        tables.table = caml_lex_array(tables[tbl_table]);
        tables.len = caml_lex_array(tables[tbl_len]);
        tables.lhs = caml_lex_array(tables[tbl_lhs]);
        tables.gindex = caml_lex_array(tables[tbl_gindex]);
        tables.dgoto = caml_lex_array(tables[tbl_dgoto]);
        tables.preprocessed = true;
    }
    var res;
    var n, n1, n2, state1;
    // RESTORE
    var sp = env[env_sp];
    var state = env[env_state];
    var errflag = env[env_errflag];
    exit: for (;;) {
        //console.error("State", Automata[cmd]);
        switch (cmd) {
            case Automata.START:
                state = 0;
                errflag = 0;
            // Fall through
            case Automata.LOOP:
                n = tables.defred[state];
                if (n != 0) {
                    cmd = Automata.REDUCE;
                    break;
                }
                if (env[env_curr_char] >= 0) {
                    cmd = Automata.TEST_SHIFT;
                    break;
                }
                res = Result.READ_TOKEN;
                break exit;
            /* The ML code calls the lexer and updates */
            /* symb_start and symb_end */
            case Automata.TOKEN_READ:
                if (typeof arg !== 'number') {
                    env[env_curr_char] = tables[tbl_transl_block][arg.TAG | 0 /* + 1 */];
                    env[env_lval] = arg._0; // token carries payload
                }
                else {
                    env[env_curr_char] = tables[tbl_transl_const][arg /* + 1 */];
                    env[env_lval] = 0; // const token
                }
                if (PARSER_TRACE) {
                    console.error("State %d, read token", state, arg);
                }
            // Fall through
            case Automata.TEST_SHIFT:
                n1 = tables.sindex[state];
                n2 = n1 + env[env_curr_char];
                if (n1 != 0 && n2 >= 0 && n2 <= tables[tbl_tablesize] &&
                    tables.check[n2] == env[env_curr_char]) {
                    cmd = Automata.SHIFT;
                    break;
                }
                n1 = tables.rindex[state];
                n2 = n1 + env[env_curr_char];
                if (n1 != 0 && n2 >= 0 && n2 <= tables[tbl_tablesize] &&
                    tables.check[n2] == env[env_curr_char]) {
                    n = tables.table[n2];
                    cmd = Automata.REDUCE;
                    break;
                }
                if (errflag <= 0) {
                    res = Result.CALL_ERROR_FUNCTION;
                    break exit;
                }
            // Fall through
            /* The ML code calls the error function */
            case Automata.ERROR_DETECTED:
                if (errflag < 3) {
                    errflag = 3;
                    for (;;) {
                        state1 = env[env_s_stack][sp /* + 1*/];
                        n1 = tables.sindex[state1];
                        n2 = n1 + ERRCODE;
                        if (n1 != 0 && n2 >= 0 && n2 <= tables[tbl_tablesize] &&
                            tables.check[n2] == ERRCODE) {
                            cmd = Automata.SHIFT_RECOVER;
                            break;
                        }
                        else {
                            if (sp <= env[env_stackbase])
                                return Result.RAISE_PARSE_ERROR;
                            /* The ML code raises Parse_error */
                            sp--;
                        }
                    }
                }
                else {
                    if (env[env_curr_char] == 0)
                        return Result.RAISE_PARSE_ERROR;
                    /* The ML code raises Parse_error */
                    env[env_curr_char] = -1;
                    cmd = Automata.LOOP;
                    break;
                }
            // Fall through
            case Automata.SHIFT:
                env[env_curr_char] = -1;
                if (errflag > 0)
                    errflag--;
            // Fall through
            case Automata.SHIFT_RECOVER:
                if (PARSER_TRACE) {
                    console.error("State %d: shift to state %d", state, tables.table[n2]);
                }
                state = tables.table[n2];
                sp++;
                if (sp >= env[env_stacksize]) {
                    res = Result.GROW_STACKS_1;
                    break exit;
                }
            // Fall through
            /* The ML code resizes the stacks */
            case Automata.STACK_GROWN_1:
                env[env_s_stack][sp /* + 1 */] = state;
                env[env_v_stack][sp /* + 1 */] = env[env_lval];
                env[env_symb_start_stack][sp /* + 1 */] = env[env_symb_start];
                env[env_symb_end_stack][sp /* + 1 */] = env[env_symb_end];
                cmd = Automata.LOOP;
                break;
            case Automata.REDUCE:
                if (PARSER_TRACE) {
                    console.error("State %d : reduce by rule %d", state, n);
                }
                var m = tables.len[n];
                env[env_asp] = sp;
                env[env_rule_number] = n;
                env[env_rule_len] = m;
                sp = sp - m + 1;
                m = tables.lhs[n];
                state1 = env[env_s_stack][sp - 1]; //
                n1 = tables.gindex[m];
                n2 = n1 + state1;
                if (n1 != 0 && n2 >= 0 && n2 <= tables[tbl_tablesize] &&
                    tables.check[n2] == state1)
                    state = tables.table[n2];
                else
                    state = tables.dgoto[m];
                if (sp >= env[env_stacksize]) {
                    res = Result.GROW_STACKS_2;
                    break exit;
                }
            // Fall through
            /* The ML code resizes the stacks */
            case Automata.STACK_GROWN_2:
                res = Result.COMPUTE_SEMANTIC_ACTION;
                break exit;
            /* The ML code calls the semantic action */
            case Automata.SEMANTIC_ACTION_COMPUTED:
                env[env_s_stack][sp /* + 1 */] = state;
                env[env_v_stack][sp /* + 1*/] = arg;
                var asp = env[env_asp];
                env[env_symb_end_stack][sp /* + 1*/] = env[env_symb_end_stack][asp /* + 1*/];
                if (sp > asp) {
                    /* This is an epsilon production. Take symb_start equal to symb_end. */
                    env[env_symb_start_stack][sp /* + 1*/] = env[env_symb_end_stack][asp /*+ 1*/];
                }
                cmd = Automata.LOOP;
                break;
            /* Should not happen */
            default:
                return Result.RAISE_PARSE_ERROR;
        }
    }
    // SAVE
    env[env_sp] = sp;
    env[env_state] = state;
    env[env_errflag] = errflag;
    return res;
});

var set_parser_trace = (function (v) {
    var old = PARSER_TRACE;
    PARSER_TRACE = v;
    return old;
});

export {
  parse_engine ,
  set_parser_trace ,
}
/*  Not a pure module */