(****************************************************************************)
(*                                                                          *)
(*                                   OCaml                                  *)
(*                                                                          *)
(*                            INRIA Rocquencourt                            *)
(*                                                                          *)
(*  Copyright  2006   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 LICENSE at the top of the Camlp4      *)
(*  source tree.                                                            *)
(*                                                                          *)
(****************************************************************************)

(* Authors:
 * - Daniel de Rauglaudre: initial version
 * - Nicolas Pouillard: refactoring
 *)

(* PR#5090: don't do lookahead on get_prev_loc. *)
value get_prev_loc_only = ref False;

module Make (Structure : Structure.S) = struct
  open Structure;

  value empty_entry ename _ =
    raise (Stream.Error ("entry [" ^ ename ^ "] is empty"));

  value rec stream_map f = parser
    [ [: ` x; strm :] -> [: ` (f x); stream_map f strm :]
    | [: :] -> [: :] ];

  value keep_prev_loc strm =
    match Stream.peek strm with
    [ None -> [: :]
    | Some (tok0,init_loc) ->
      let rec go prev_loc strm1 =
        if get_prev_loc_only.val then
          [: `(tok0, {prev_loc; cur_loc = prev_loc; prev_loc_only = True});
             go prev_loc strm1 :]
        else
          match strm1 with parser
          [ [: `(tok,cur_loc); strm :] ->
              [: `(tok, {prev_loc; cur_loc; prev_loc_only = False});
                 go cur_loc strm :]
          | [: :] -> [: :] ]
      in go init_loc strm ];

  value drop_prev_loc strm = stream_map (fun (tok,r) -> (tok,r.cur_loc)) strm;

  value get_cur_loc strm =
    match Stream.peek strm with
    [ Some (_,r) -> r.cur_loc
    | None -> Loc.ghost ];

  value get_prev_loc strm =
    begin
      get_prev_loc_only.val := True;
      let result = match Stream.peek strm with
        [ Some (_, {prev_loc; prev_loc_only = True}) ->
            begin Stream.junk strm; prev_loc end
        | Some (_, {prev_loc; prev_loc_only = False}) -> prev_loc
        | None -> Loc.ghost ];
      get_prev_loc_only.val := False;
      result
    end;

  value is_level_labelled n lev =
    match lev.lname with
    [ Some n1 -> n = n1
    | None -> False ];

  value warning_verbose = ref True;

  value rec get_token_list entry tokl last_tok tree =
    match tree with
    [ Node {node = (Stoken _ | Skeyword _ as tok); son = son; brother = DeadEnd} ->
        get_token_list entry [last_tok :: tokl] tok son
    | _ ->
        if tokl = [] then None
        else Some (List.rev [last_tok :: tokl], last_tok, tree) ];

  value is_antiquot s =
    let len = String.length s in
    len > 1 && s.[0] = '$';

  value eq_Stoken_ids s1 s2 =
    not (is_antiquot s1) && not (is_antiquot s2) && s1 = s2;

  value logically_eq_symbols entry =
    let rec eq_symbols s1 s2 =
      match (s1, s2) with
      [ (Snterm e1, Snterm e2) -> e1.ename = e2.ename
      | (Snterm e1, Sself) -> e1.ename = entry.ename
      | (Sself, Snterm e2) -> entry.ename = e2.ename
      | (Snterml e1 l1, Snterml e2 l2) -> e1.ename = e2.ename && l1 = l2
      | (Slist0 s1, Slist0 s2) |
        (Slist1 s1, Slist1 s2) |
        (Sopt s1, Sopt s2) |
        (Stry s1, Stry s2) -> eq_symbols s1 s2
      | (Slist0sep s1 sep1, Slist0sep s2 sep2) |
        (Slist1sep s1 sep1, Slist1sep s2 sep2) ->
          eq_symbols s1 s2 && eq_symbols sep1 sep2
      | (Stree t1, Stree t2) -> eq_trees t1 t2
      | (Stoken (_, s1), Stoken (_, s2)) -> eq_Stoken_ids s1 s2
      | _ -> s1 = s2 ]
    and eq_trees t1 t2 =
      match (t1, t2) with
      [ (Node n1, Node n2) ->
          eq_symbols n1.node n2.node && eq_trees n1.son n2.son &&
          eq_trees n1.brother n2.brother
      | (LocAct _ _ | DeadEnd, LocAct _ _ | DeadEnd) -> True
      | _ -> False ]
    in
    eq_symbols;

  value rec eq_symbol s1 s2 =
    match (s1, s2) with
    [ (Snterm e1, Snterm e2) -> e1 == e2
    | (Snterml e1 l1, Snterml e2 l2) -> e1 == e2 && l1 = l2
    | (Slist0 s1, Slist0 s2) |
      (Slist1 s1, Slist1 s2) |
      (Sopt s1, Sopt s2) |
      (Stry s1, Stry s2) -> eq_symbol s1 s2
    | (Slist0sep s1 sep1, Slist0sep s2 sep2) |
      (Slist1sep s1 sep1, Slist1sep s2 sep2) ->
        eq_symbol s1 s2 && eq_symbol sep1 sep2
    | (Stree _, Stree _) -> False
    | (Stoken (_, s1), Stoken (_, s2)) -> eq_Stoken_ids s1 s2
    | _ -> s1 = s2 ]
  ;
end;
