package com.headius.racc;

import org.jruby.Ruby; import org.jruby.RubyArray; import org.jruby.RubyBasicObject; import org.jruby.RubyClass; import org.jruby.RubyContinuation; import org.jruby.RubyFixnum; import org.jruby.RubyHash; import org.jruby.RubyModule; import org.jruby.RubyNumeric; import org.jruby.RubyObject; import org.jruby.RubySymbol; import org.jruby.anno.JRubyMethod; import org.jruby.exceptions.JumpException; import org.jruby.internal.runtime.methods.AttrReaderMethod; import org.jruby.internal.runtime.methods.AttrWriterMethod; import org.jruby.internal.runtime.methods.CallConfiguration; import org.jruby.runtime.Arity; import org.jruby.runtime.Block; import org.jruby.runtime.BlockCallback; import org.jruby.runtime.CallBlock19; import org.jruby.runtime.CallSite; import org.jruby.runtime.Helpers; import org.jruby.runtime.MethodIndex; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.Visibility; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.load.Library;

public class Cparse implements Library {

public static final String RACC_VERSION = "1.4.12"; // TODO: parse from Cparse.c

public enum TokenType {
    DEFAULT(-1),
    FINAL(0),
    ERROR(1);

    private final int id;
    TokenType(int id) { this.id = id; }
}

private RubyFixnum vDEFAULT_TOKEN;
private RubyFixnum vERROR_TOKEN;
private RubyFixnum vFINAL_TOKEN;

private RubyClass RaccBug;
private RubyClass CparseParams;

private static final String ID_YYDEBUG = "@yydebug";
private static final String ID_NEXTTOKEN = "next_token";
private static final String ID_ONERROR = "on_error";
private static final String ID_NOREDUCE = "_reduce_none";
private static final String ID_ERRSTATUS = "@racc_error_status";

private static final String ID_D_SHIFT = "racc_shift";
private static final String ID_D_REDUCE = "racc_reduce";
private static final String ID_D_ACCEPT = "racc_accept";
private static final String ID_D_READ_TOKEN = "racc_read_token";
private static final String ID_D_NEXT_STATE = "racc_next_state";
private static final String ID_D_E_POP = "racc_e_pop";

private RubySymbol sym_noreduce;
private CallSite call_nexttoken;
private CallSite call_onerror;
private CallSite call_d_shift;
private CallSite call_d_reduce;
private CallSite call_d_accept;
private CallSite call_d_read_token;
private CallSite call_d_next_state;
private CallSite call_d_e_pop;
private AttrWriterMethod set_errstatus;
private AttrReaderMethod get_errstatus;

private static RubySymbol value_to_id(ThreadContext context, IRubyObject v) {
    if (!(v instanceof RubySymbol)) {
        throw context.runtime.newTypeError("not symbol");
    }
    return (RubySymbol)v;
}

private static int num_to_int(IRubyObject n) {
    return assert_integer(n);
}

private static IRubyObject AREF(ThreadContext context, IRubyObject s, int idx) {
    return ((0 <= idx && idx < ((RubyArray)s).size()) ? ((RubyArray)s).entry(idx) : context.nil);
}

private static IRubyObject get_stack_tail(ThreadContext context, RubyArray stack, int len) {
    if (len < 0) return context.nil;
    int size = stack.size();
    len = Math.min(len, size);
    return stack.subseq(size - len, len);
}

private static void cut_stack_tail(ThreadContext context, RubyArray stack, int len) {
    while (len > 0) {
        stack.pop(context);
        len--;
    }
}

private static final int STACK_INIT_LEN = 64;
private static RubyArray NEW_STACK(ThreadContext context) {
    return context.runtime.newArray(STACK_INIT_LEN);
}
private static IRubyObject PUSH(RubyArray stack, IRubyObject i) {
    return stack.append(i);
}
private static IRubyObject POP(ThreadContext context, RubyArray stack) {
    return stack.pop(context);
}
private static IRubyObject LAST_I(ThreadContext context, RubyArray stack) {
    return stack.size() > 0 ? stack.last() : context.nil;
}
private static IRubyObject GET_TAIL(ThreadContext context, RubyArray stack, int len) {
    return get_stack_tail(context, stack, len);
}
private static void CUT_TAIL(ThreadContext context, RubyArray stack, int len) {
    cut_stack_tail(context, stack, len);
}

static final int CP_FIN_ACCEPT = 1;
static final int CP_FIN_EOT = 2;
static final int CP_FIN_CANTPOP = 3;

public class CparseParams extends RubyObject {
    public CparseParams(Ruby runtime, RubyClass rubyClass) {
        super(runtime, rubyClass);
    }

    public void initialize_params(ThreadContext context, Parser parser, IRubyObject arg, IRubyObject lexer, IRubyObject lexmid) {
        Ruby runtime = context.runtime;
        this.parser = parser;
        this.lexer = lexer;
        if (!lexmid.isNil()) {
            this.lexmid = value_to_id(context, lexmid);
            this.call_lexmid = MethodIndex.getFunctionalCallSite(this.lexmid.toString());
        }

        this.debug           = parser.getInstanceVariable(ID_YYDEBUG).isTrue();

        RubyArray argAry = arg.convertToArray();
        if (!(13 <= argAry.size() && argAry.size() <= 14)) {
            throw runtime.newRaiseException(RaccBug, "[Racc Bug] wrong arg.size " + argAry.size());
        }
        this.action_table     = assert_array(argAry.eltOk(0));
        this.action_check     = assert_array(argAry.eltOk(1));
        this.action_default   = assert_array(argAry.eltOk(2));
        this.action_pointer   = assert_array(argAry.eltOk(3));
        this.goto_table       = assert_array(argAry.eltOk(4));
        this.goto_check       = assert_array(argAry.eltOk(5));
        this.goto_default     = assert_array(argAry.eltOk(6));
        this.goto_pointer     = assert_array(argAry.eltOk(7));
        this.nt_base          = assert_integer(argAry.eltOk(8));
        this.reduce_table     = assert_array(argAry.eltOk(9));
        this.token_table      = assert_hash(argAry.eltOk(10));
        this.shift_n          = assert_integer(argAry.eltOk(11));
        this.reduce_n         = assert_integer(argAry.eltOk(12));
        if (argAry.size() > 13) {
            this.use_result_var = argAry.eltOk(13).isTrue();
         } else {
            this.use_result_var = true;
        }

        this.tstack          = this.debug ? NEW_STACK(context) : null;
        this.vstack          = NEW_STACK(context);
        this.state           = NEW_STACK(context);
        this.curstate        = 0;
        PUSH(this.state, RubyFixnum.zero(runtime));
        this.t               = runtime.newFixnum(TokenType.FINAL.id + 1); // must not init to FINAL_TOKEN
        this.nerr            = 0;
        this.errstatus       = 0;
        set_errstatus.call(context, parser, parser.getMetaClass(), ID_ERRSTATUS, RubyNumeric.int2fix(runtime, this.errstatus));

        this.retval          = context.nil;
        this.fin             = 0;

        this.lex_is_iterator = false;

        parser.setInstanceVariable("@vstack", this.vstack);
        if (this.debug) {
            parser.setInstanceVariable("@tstack", this.tstack);
        }
        else {
            parser.setInstanceVariable("@tstack", context.nil);
        }
    }

    public void extract_user_token(ThreadContext context, IRubyObject block_args, IRubyObject[] tokVal) {
        if (block_args.isNil()) {
               EOF   
            tokVal[0] = context.runtime.getFalse();
            tokVal[1] = context.runtime.newString("$");
            return;
        }

        if (!(block_args instanceof RubyArray)) {
            throw context.runtime.newTypeError(
                    (lex_is_iterator ? lexmid.asJavaString() : "next_token") +
                            " " +
                            (lex_is_iterator ? "yielded" : "returned") +
                            " " +
                            block_args.getMetaClass().getName() +
                            " (must be Array[2])");
        }
        RubyArray block_args_ary = (RubyArray)block_args;
        if (block_args_ary.size() != 2) {
            throw context.runtime.newTypeError(
                    (lex_is_iterator ? lexmid.asJavaString() : "next_token") +
                            " " +
                            (lex_is_iterator ? "yielded" : "returned") +
                            " wrong size of array (" +
                            block_args_ary.size() +
                            " for 2)");
        }
        tokVal[0] = ((RubyArray) block_args).eltOk(0);
        tokVal[1] = ((RubyArray) block_args).eltOk(1);
    }

    private static final int RESUME = 1;
    private static final int NOTFOUND = 2;
    private static final int ERROR_RECOVERED = 3;
    private static final int ERROR = 4;
    private static final int HANDLE_ACT = 5;
    private static final int ACT_FIXED = 6;
    private static final int ACCEPT = 7;
    private static final int USER_YYERROR = 8;
    private static final int ERROR_POP = 9;
    private static final int TRANSIT = 9;

    private void SHIFT(ThreadContext context, int act, IRubyObject tok, IRubyObject val) {
        shift(context, act, tok, val);
    }

    private int REDUCE(ThreadContext context, int act) {
        return reduce(context, act);
    }

    public void parse_main(ThreadContext context, IRubyObject tok, IRubyObject val, boolean resume) {
        Ruby runtime = context.runtime;

        int i = 0;                 table index   
        int act = 0;            /* action type */
        IRubyObject act_value;     /* action type, VALUE version */
        boolean read_next = true;   /* true if we need to read next token */
        IRubyObject tmp;

        int branch = 0;

        if (resume) {
            branch = RESUME;
        }

        BRANCH: while (true) {
            switch (branch) {
                case 0:

                    D_puts("");
                    D_puts("---- enter new loop ----");
                    D_puts("");

                    D_printf("(act) k1=%ld\n", this.curstate);
                    tmp = AREF(context, this.action_pointer, this.curstate);
                    if (tmp.isNil()) {branch = NOTFOUND; continue BRANCH;}
                    D_puts("(act) pointer[k1] ok");
                    i = assert_integer(tmp);

                    D_printf("read_next=%d\n", read_next);
                    if (read_next && (this.t != vFINAL_TOKEN)) {
                        if (this.lex_is_iterator) {
                            D_puts("resuming...");
                            if (this.fin != 0) throw runtime.newArgumentError("token given after EOF");
                            this.i = i;  /* save i */
                            return;

                            // remainder of case duplicated from here for RESUME case
                            //D_puts(this, "resumed");
                            //i = this.i;  /* load i */
                        }
                        else {
                            D_puts("next_token");
                            tmp = call_nexttoken.call(context, this.parser, this.parser);
                            IRubyObject[] tokVal = {tok, val};
                            extract_user_token(context, tmp, tokVal);
                            tok = tokVal[0];
                            val = tokVal[1];
                        }
                        /* convert token */
                        this.t = ((RubyHash)this.token_table).op_aref(context, tok);
                        if (this.t.isNil()) {
                            this.t = vERROR_TOKEN;
                        }
                        D_printf("(act) t(k2)=%ld\n", assert_integer(this.t));
                        if (this.debug) {
                            call_d_read_token.call(context, this.parser, this.parser, this.t, tok, val);
                        }
                    }

                    // duplicated logic from above for RESUME case
                case RESUME:
                    if (branch == RESUME) {
                        D_puts("resumed");
                        i = this.i;  /* load i */

                        /* convert token */
                        this.t = ((RubyHash)this.token_table).op_aref(context, tok);
                        if (this.t.isNil()) {
                            this.t = vERROR_TOKEN;
                        }
                        D_printf("(act) t(k2)=%ld\n", assert_integer(this.t));
                        if (this.debug) {
                            call_d_read_token.call(context, this.parser, this.parser, this.t, tok, val);
                        }
                    }

                    read_next = false;

                    i += assert_integer(this.t);
                    D_printf("(act) i=%ld\n", i);
                    if (i < 0) {branch = NOTFOUND; continue BRANCH;}

                    act_value = AREF(context, this.action_table, i);
                    if (act_value.isNil()) {branch = NOTFOUND; continue BRANCH;}
                    act = assert_integer(act_value);
                    D_printf("(act) table[i]=%ld\n", act);

                    tmp = AREF(context, this.action_check, i);
                    if (tmp.isNil()) {branch = NOTFOUND; continue BRANCH;}
                    if (assert_integer(tmp) != this.curstate) {branch = NOTFOUND; continue BRANCH;}
                    D_printf("(act) check[i]=%ld\n", assert_integer(tmp));

                    D_puts("(act) found");

                case ACT_FIXED:
                    D_printf("act=%ld\n", act);
                    branch = HANDLE_ACT; continue BRANCH;

                case NOTFOUND:
                    D_puts("(act) not found: use default");
                    act_value = AREF(context, this.action_default, this.curstate);
                    act = assert_integer(act_value);
                    branch = ACT_FIXED; continue BRANCH;

                case HANDLE_ACT:
                    if (act > 0 && act < this.shift_n) {
                        D_puts("shift");
                        if (this.errstatus > 0) {
                            this.errstatus--;
                            set_errstatus.call(context, this.parser, this.parser.getMetaClass(), ID_ERRSTATUS, runtime.newFixnum(this.errstatus));
                        }
                        SHIFT(context, act, this.t, val);
                        read_next = true;
                    }
                    else if (act < 0 && act > -(this.reduce_n)) {
                        D_puts("reduce");
                        REDUCE(context, act);
                    }
                    else if (act == -(this.reduce_n)) {
                        branch = ERROR; continue BRANCH;
                    }
                    else if (act == this.shift_n) {
                        D_puts("accept");
                        branch = ACCEPT; continue BRANCH;
                    }
                    else {
                        throw runtime.newRaiseException(RaccBug, "[Cparse Bug] unknown act value " + act);
                    }

                case ERROR_RECOVERED:

                    if (this.debug) {
                        call_d_next_state.call(context, this.parser, this.parser, runtime.newFixnum(this.curstate), this.state);
                    }
                    branch = 0; continue BRANCH;

                /* not reach */

                case ACCEPT:
                    if (this.debug) call_d_accept.call(context, this.parser, this.parser);
                    this.retval = this.vstack.eltOk(0);
                    this.fin = CP_FIN_ACCEPT;
                    return;

                case ERROR:
                    D_printf("error detected, status=%ld\n", this.errstatus);
                    if (this.errstatus == 0) {
                        this.nerr++;
                        call_onerror.call(context, this.parser, this.parser, this.t, val, this.vstack);
                    }

                case USER_YYERROR:
                    if (this.errstatus == 3) {
                        if (this.t == vFINAL_TOKEN) {
                            this.retval = runtime.getFalse();
                            this.fin = CP_FIN_EOT;
                            return;
                        }
                        read_next = true;
                    }
                    this.errstatus = 3;
                    set_errstatus.call(context, this.parser, this.parser.getMetaClass(), ID_ERRSTATUS, runtime.newFixnum(this.errstatus));

                    /* check if we can shift/reduce error token */
                    D_printf("(err) k1=%ld\n", this.curstate);
                    D_printf("(err) k2=%d (error)\n", TokenType.ERROR.id);

                    int branch2 = 0;

                    BRANCH2: while (true) {
                        switch (branch2) {
                            case 0:
                                tmp = AREF(context, this.action_pointer, this.curstate);
                                if (tmp.isNil()) {branch2 = ERROR_POP; continue BRANCH2;}
                                D_puts("(err) pointer[k1] ok");

                                i = assert_integer(tmp) + TokenType.ERROR.id;
                                D_printf("(err) i=%ld\n", i);
                                if (i < 0) {branch2 = ERROR_POP; continue BRANCH2;}

                                act_value = AREF(context, this.action_table, i);
                                if (act_value.isNil()) {
                                    D_puts("(err) table[i] == nil");
                                    branch2 = ERROR_POP; continue BRANCH2;
                                }
                                act = assert_integer(act_value);
                                D_printf("(err) table[i]=%ld\n", act);

                                tmp = AREF(context, this.action_check, i);
                                if (tmp.isNil()) {
                                    D_puts("(err) check[i] == nil");
                                    branch2 = ERROR_POP; continue BRANCH2;
                                }
                                if (assert_integer(tmp) != this.curstate) {
                                    D_puts("(err) check[i] != k1");
                                    branch2 = ERROR_POP; continue BRANCH2;
                                }

                                D_puts("(err) found: can handle error token");
                                break BRANCH2;

                            case ERROR_POP:
                                D_puts("(err) act not found: can't handle error token; pop");

                                if (this.state.size() <= 1) {
                                    this.retval = context.nil;
                                    this.fin = CP_FIN_CANTPOP;
                                    return;
                                }
                                POP(context, this.state);
                                POP(context, this.vstack);
                                this.curstate = assert_integer(LAST_I(context, this.state));
                                if (this.debug) {
                                    POP(context, this.tstack);
                                    call_d_e_pop.call(context, this.parser, this.parser, this.state, this.tstack, this.vstack);
                                }
                        }
                    }

                    /* shift/reduce error token */
                    if (act > 0 && act < this.shift_n) {
                        D_puts("e shift");
                        SHIFT(context, act, runtime.newFixnum(TokenType.ERROR.id), val);
                    }
                    else if (act < 0 && act > -(this.reduce_n)) {
                        D_puts("e reduce");
                        REDUCE(context, act);
                    }
                    else if (act == this.shift_n) {
                        D_puts("e accept");
                        branch = ACCEPT; continue BRANCH;
                    }
                    else {
                        throw runtime.newRaiseException(RaccBug, "[Cparse Bug] unknown act value " + act);
                    }
                    branch = ERROR_RECOVERED; continue BRANCH;
            }
        }
    }

    private void shift(ThreadContext context, int act, IRubyObject tok, IRubyObject val) {
        PUSH(vstack, val);
        if (debug) {
            PUSH(tstack, tok);
            call_d_shift.call(context, this.parser, this.parser, tok, tstack, vstack);
        }
        curstate = act;
        PUSH(state, context.runtime.newFixnum(curstate));
    }

    private int reduce(ThreadContext context, int act) {
        IRubyObject code;
        ruleno = -act * 3;
        IRubyObject tag = context.runtime.newSymbol("racc_jump");
        RubyContinuation rbContinuation = new RubyContinuation(context.runtime, context.runtime.newSymbol("racc_jump"));
        try {
            context.pushCatch(rbContinuation.getContinuation());
            code = reduce0(context);
            errstatus = assert_integer(get_errstatus.call(context, parser, parser.getMetaClass(), ID_ERRSTATUS));
        } finally {
            context.popCatch();
        }
        return assert_integer(code);
    }

    private IRubyObject reduce0(ThreadContext context) {
        Ruby runtime = context.runtime;

        IRubyObject reduce_to, reduce_len, method_id;
        int len;
        RubySymbol mid;
        IRubyObject tmp, tmp_t = RubyBasicObject.UNDEF, tmp_v = RubyBasicObject.UNDEF;
        int i, k1 = 0, k2;
        IRubyObject goto_state = context.nil;

        reduce_len = this.reduce_table.entry(this.ruleno);
        reduce_to  = this.reduce_table.entry(this.ruleno+1);
        method_id  = this.reduce_table.entry(this.ruleno+2);
        len = assert_integer(reduce_len);
        mid = value_to_id(context, method_id);

        int branch = 0;
        BRANCH: while (true) {
            switch (branch) {
                case 0:

                    /* call action */
                    if (len == 0) {
                        tmp = context.nil;
                        if (mid != sym_noreduce)
                            tmp_v = runtime.newArray();
                        if (this.debug)
                            tmp_t = runtime.newArray();
                    }
                    else {
                        if (mid != sym_noreduce) {
                            tmp_v = GET_TAIL(context, this.vstack, len);
                            tmp = ((RubyArray)tmp_v).entry(0);
                        }
                        else {
                            tmp = this.vstack.entry(this.vstack.size() - len);
                        }
                        CUT_TAIL(context, this.vstack, len);
                        if (this.debug) {
                            tmp_t = GET_TAIL(context, this.tstack, len);
                            CUT_TAIL(context, this.tstack, len);
                        }
                        CUT_TAIL(context, this.state, len);
                    }
                    if (mid != sym_noreduce) {
                        if (this.use_result_var) {
                            tmp = Helpers.invoke(context, this.parser, mid.toString(), tmp_v, this.vstack, tmp);
                        }
                        else {
                            tmp = Helpers.invoke(context, this.parser, mid.toString(), tmp_v, this.vstack);
                        }
                    }

                    /* then push result */
                    PUSH(this.vstack, tmp);
                    if (this.debug) {
                        PUSH(this.tstack, reduce_to);
                        call_d_reduce.call(context, this.parser, this.parser, tmp_t, reduce_to, this.tstack, this.vstack);
                    }

                    /* calculate transition state */
                    if (state.size() == 0)
                        throw runtime.newRaiseException(RaccBug, "state stack unexpectedly empty");
                    k2 = assert_integer(LAST_I(context, this.state));
                    k1 = assert_integer(reduce_to) - this.nt_base;
                    D_printf("(goto) k1=%ld\n", k1);
                    D_printf("(goto) k2=%ld\n", k2);

                    tmp = AREF(context, this.goto_pointer, k1);
                    if (tmp.isNil()) {branch = NOTFOUND; continue BRANCH;}

                    i = assert_integer(tmp) + k2;
                    D_printf("(goto) i=%ld\n", i);
                    if (i < 0) {branch = NOTFOUND; continue BRANCH;}

                    goto_state = AREF(context, this.goto_table, i);
                    if (goto_state.isNil()) {
                        D_puts("(goto) table[i] == nil");
                        branch = NOTFOUND; continue BRANCH;
                    }
                    D_printf("(goto) table[i]=%ld (goto_state)\n", goto_state.convertToInteger().getLongValue());

                    tmp = AREF(context, this.goto_check, i);
                    if (tmp.isNil()) {
                        D_puts("(goto) check[i] == nil");
                        branch = NOTFOUND; continue BRANCH;
                    }
                    if (tmp != runtime.newFixnum(k1)) {
                        D_puts("(goto) check[i] != table[i]");
                        branch = NOTFOUND; continue BRANCH;
                    }
                    D_printf("(goto) check[i]=%ld\n", tmp.convertToInteger().getLongValue());

                    D_puts("(goto) found");

                case TRANSIT:
                    PUSH(this.state, goto_state);
                    this.curstate = assert_integer(goto_state);
                    return RubyFixnum.zero(runtime);

                case NOTFOUND:
                    D_puts("(goto) not found: use default");
                    /* overwrite `goto-state' by default value */
                    goto_state = AREF(context, this.goto_default, k1);
                    branch = TRANSIT; continue BRANCH;
            }
        }
    }

    private void D_puts(String msg) {
        if (sys_debug) {
            System.out.println(msg);
        }
    }

    private void D_printf(String fmt, long arg) {
        if (sys_debug) {
            System.out.println(fmt + ": " + arg);
        }
    }

    private void D_printf(String fmt, boolean arg) {
        if (sys_debug) {
            System.out.println(fmt + ": " + arg);
        }
    }

    Parser parser;          /* parser object */

    boolean lex_is_iterator;
    IRubyObject lexer;           /* scanner object */
    RubySymbol lexmid;          /* name of scanner method (must be an iterator) */
    CallSite call_lexmid;       /* call site for scanner method */

    /* State transition tables (immutable)
       Data structure is from Dragon Book 4.9 */
    /* action table */
    IRubyObject action_table;
    IRubyObject action_check;
    IRubyObject action_default;
    IRubyObject action_pointer;
    /* goto table */
    IRubyObject goto_table;
    IRubyObject goto_check;
    IRubyObject goto_default;
    IRubyObject goto_pointer;

    int nt_base;         /* NonTerminal BASE index */
    RubyArray reduce_table;    /* reduce data table */
    IRubyObject token_table;     /* token conversion table */

    /* parser stacks and parameters */
    RubyArray state;
    int curstate;
    RubyArray vstack;
    RubyArray tstack;
    IRubyObject t;
    int shift_n;
    int reduce_n;
    int ruleno;

    int errstatus;         /* nonzero in error recovering mode */
    int nerr;              /* number of error */

    boolean use_result_var;

    IRubyObject retval;           /* return IRubyObject of parser routine */
    int fin;               /* parse result status */

    boolean debug;              /* user level debug */
    boolean sys_debug;          /* system level debug */

    int i;                 /* table index */
}

private static RubyArray assert_array(IRubyObject a) {
    return a.convertToArray();
}

private static RubyHash assert_hash(IRubyObject h) {
    return h.convertToHash();
}

private static int assert_integer(IRubyObject i) {
    return (int)i.convertToInteger().getLongValue();
}

public class Parser extends RubyObject {
    public Parser(Ruby runtime, RubyClass rubyClass) {
        super(runtime, rubyClass);
    }

    public static final String Racc_Runtime_Core_Version_C = RACC_VERSION;
    public static final String Racc_Runtime_Core_Id_C = "$originalId: cparse.c,v 1.8 2006/07/06 11:39:46 aamine Exp $";

    @JRubyMethod(name = "_racc_do_parse_c", frame = true)
    public IRubyObject racc_cparse(ThreadContext context, IRubyObject arg, IRubyObject sysdebug) {
        CparseParams v = new CparseParams(context.runtime, CparseParams);

        v.D_puts("starting cparse");
        v.sys_debug = sysdebug.isTrue();
        v.initialize_params(context, this, arg, context.nil, context.nil);
        v.lex_is_iterator = false;
        v.parse_main(context, context.nil, context.nil, false);

        return v.retval;
    }

    @JRubyMethod(name = "_racc_yyparse_c", frame = true, required = 4)
    public IRubyObject racc_yyparse(ThreadContext context, IRubyObject[] args) {
        Ruby runtime = context.runtime;
        CparseParams v = new CparseParams(context.runtime, CparseParams);

        IRubyObject lexer = args[0], lexmid = args[1], arg = args[2], sysdebug = args[3];

        v.sys_debug = sysdebug.isTrue();
        v.D_puts("start C yyparse");
        v.initialize_params(context, this, arg, lexer, lexmid);
        v.lex_is_iterator = true;
        v.D_puts("params initialized");
        v.parse_main(context, context.nil, context.nil, false);
        call_lexer(context, v);
        if (v.fin == 0) {
            throw runtime.newArgumentError(v.lexmid + " is finished before EndOfToken");
        }

        return v.retval;
    }

    private void call_lexer(ThreadContext context, final CparseParams v) {
        final int frame = context.getFrameJumpTarget();
        try {
            v.call_lexmid.call(context, v.lexer, v.lexer, CallBlock19.newCallClosure(v, v.getMetaClass(), Arity.ONE_ARGUMENT, new BlockCallback() {
                @Override
                public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block) {
                    Ruby runtime = context.getRuntime();
                    if (v.fin != 0) {
                        throw runtime.newArgumentError("extra token after EndOfToken");
                    }
                    IRubyObject[] tokVal = {null, null};
                    v.extract_user_token(context, args[0], tokVal);
                    v.parse_main(context, tokVal[0], tokVal[1], true);
                    if (v.fin != 0 && v.fin != CP_FIN_ACCEPT) {
                        throw new JumpException.BreakJump(frame, context.nil);
                    }

                    return context.nil;
                }
            }, context));
        } catch (JumpException.BreakJump bj) {
            if (bj.getTarget() == frame) {
                return;
            }
        }
    }
}

public void load(Ruby runtime, boolean wrap) {
    RubyModule racc = runtime.getOrCreateModule("Racc");
    RubyClass parser = racc.defineOrGetClassUnder("Parser", runtime.getObject());
    parser.setAllocator(new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) {
            return new Parser(ruby, rubyClass);
        }
    });

    parser.defineAnnotatedMethods(Parser.class);

    parser.defineConstant("Racc_Runtime_Core_Version_C", runtime.newString(Parser.Racc_Runtime_Core_Version_C));
    parser.defineConstant("Racc_Runtime_Core_Id_C", runtime.newString(Parser.Racc_Runtime_Core_Id_C));

    CparseParams = racc.defineClassUnder("CparseParams", runtime.getObject(), new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) {
            return new CparseParams(ruby, rubyClass);
        }
    });

    RaccBug = runtime.getRuntimeError();
    sym_noreduce = runtime.newSymbol(ID_NOREDUCE);
    call_nexttoken = MethodIndex.getFunctionalCallSite(ID_NEXTTOKEN);
    call_onerror = MethodIndex.getFunctionalCallSite(ID_ONERROR);
    call_d_shift = MethodIndex.getFunctionalCallSite(ID_D_SHIFT);
    call_d_reduce = MethodIndex.getFunctionalCallSite(ID_D_REDUCE);
    call_d_accept = MethodIndex.getFunctionalCallSite(ID_D_ACCEPT);
    call_d_read_token = MethodIndex.getFunctionalCallSite(ID_D_READ_TOKEN);
    call_d_next_state = MethodIndex.getFunctionalCallSite(ID_D_NEXT_STATE);
    call_d_e_pop = MethodIndex.getFunctionalCallSite(ID_D_E_POP);

    // hacky utility for caching instance var accessor
    set_errstatus = new AttrWriterMethod(parser, Visibility.PUBLIC, CallConfiguration.FrameNoneScopeNone, ID_ERRSTATUS);
    get_errstatus = new AttrReaderMethod(parser, Visibility.PUBLIC, CallConfiguration.FrameNoneScopeNone, ID_ERRSTATUS);

    vDEFAULT_TOKEN      = runtime.newFixnum(TokenType.DEFAULT.id);
    vERROR_TOKEN      = runtime.newFixnum(TokenType.ERROR.id);
    vFINAL_TOKEN      = runtime.newFixnum(TokenType.FINAL.id);
}

}