(* lrparse.ml *)
(* deterministic LALR(1) parser *)

open BatteriesInit
open Lexerint       (* tLexerInterface *)
open Parsetables    (* actionTable, etc. *)
open Useract        (* reductionAction *)


type tStateId = int

let debug : bool = false

let stateStack : tStateId array ref = ref (Array.make 10 0)
let svalStack : Obj.t array ref = ref (Array.make 10 (Obj.repr 0))
let stackLen : int ref = ref 0

let reInit () =
	stateStack := (Array.make 10 0);
	svalStack  := (Array.make 10 (Obj.repr 0));
	stackLen   := 0
;;

let pushStateSval (state : tStateId) (sval : Obj.t) : unit =
begin
  if ((Array.length !stateStack) = !stackLen) then (
    (* must make it bigger *)
    let newStateStack : tStateId array = (Array.make (!stackLen * 2) 0) in
    let newSvalStack : Obj.t array = (Array.make (!stackLen * 2) (Obj.repr 0)) in

    (* copy *)
    (Array.blit
      !stateStack           (* source array *)
      0                     (* source start position *)
      newStateStack         (* dest array *)
      0                     (* dest start position *)
      !stackLen             (* number of elements to copy *)
    );
    (Array.blit
      !svalStack            (* source array *)
      0                     (* source start position *)
      newSvalStack          (* dest array *)
      0                     (* dest start position *)
      !stackLen             (* number of elements to copy *)
    );

    (* switch from old to new *)
    stateStack := newStateStack;
    svalStack := newSvalStack;
  );

  (* put new element into the stack at the end *)
  (!stateStack).(!stackLen) <- state;
  (!svalStack).(!stackLen) <- sval;
  (incr stackLen);
end

let topState() : tStateId =
begin
  (!stateStack).(!stackLen - 1)
end

let parse (lex:tLexerInterface) (tables:tParseTables) (actions:tUserActions) 
  : tSemanticValue =
begin
	reInit();
  (* initial state *)
  (pushStateSval 0 (Obj.repr 0));

  (* loop over all tokens until EOF and stack has just start symbol *)
  while (not ((lex#getTokType()) = 0)) ||
        (!stackLen > 2) do
    let tt:int = (lex#getTokType()) in        (* token type *)
    let state:tStateId = (topState()) in      (* current state *)

    if (debug) then (
      (Printf.printf "state=%d tokType=%d sval=%d desc=\"%s\"\n"
                     state
                     tt
                     (lex#getIntSval())
                     (lex#tokenDesc())
                   );
      (flush stdout);
    );

    (* read from action table *)
    let act:tActionEntry = (getActionEntry tables state tt) in

    (* shift? *)
    if (isShiftAction tables act) then (
      let dest:tStateId = (decodeShift act tt) in   (* destination state *)
      (pushStateSval dest (lex#getSval()));

      (* next token *)
      (lex#getToken());
             
      if (debug) then (
        (Printf.printf "shift to state %d\n" dest);
        (flush stdout);
      );
    )

    (* reduce? *)
    else if (isReduceAction act) then (
      let rule:int = (decodeReduce act state) in    (* reduction rule *)
      let ruleLen:int = (getProdInfo_rhsLen tables rule) in
      let lhs:int = (getProdInfo_lhsIndex tables rule) in

      (* make an array of semantic values for the action rule; this does
       * an extra copy if we're already using a linear stack, but will
       * be needed for GLR so I'll do it this way *)
      let svalArray : Obj.t array = (Array.make ruleLen (Obj.repr 0)) in
      (Array.blit
        !svalStack            (* source array *)
        (!stackLen - ruleLen) (* source start position *)
        svalArray             (* dest array *)
        0                     (* dest start position *)
        ruleLen               (* number of elements to copy *)
      );

      (* invoke user's reduction action *)
      let sval:Obj.t = (actions.reductionAction rule svalArray) in

      (* pop 'ruleLen' elements *)
      stackLen := (!stackLen - ruleLen);
      let newTopState:int = (topState()) in

      (* get new state *)
      let dest:tStateId = (decodeGoto (getGotoEntry tables newTopState lhs) lhs) in
      (pushStateSval dest sval);

      if (debug) then (
        (Printf.printf "reduce by rule %d (len=%d, lhs=%d), goto state %d\n"
                       rule ruleLen lhs dest);
        (flush stdout);
      );
    )

    (* error? *)
    else if (isErrorAction act) then (
      (Printf.printf "parse error in state %d\n" state);
      (flush stdout);
      (failwith "parse error");
    )

    (* bad code? *)
    else (
      (failwith "bad action code");
    );
  done;

  (* print final parse stack *)
  if (debug) then (
    (Printf.printf "final parse stack (up is top):\n");
    let i:int ref = ref (pred !stackLen) in
    while (!i >= 0) do
      (Printf.printf "  %d\n" (!stateStack).(!i));
      (decr i);
    done;
    (flush stdout);
  );

  (* return value: sval of top element *)
  let topSval:Obj.t = (!svalStack).(!stackLen - 1) in

  topSval
end


(* EOF *)
