(*
  Public Domain. Use at your own risk!
  Author: Martin Jambon <martin_jambon@emailuser.net>

  This syntax extension provides an efficient "for in" construct,
  which can replace List.iter for better readability.

  The general syntax is:

  for pattern in list do
    expr
  done

  Example 1:

    for s in ["Hello"; "World"] do 
      fprintf oc ">>>%s<<< " s
    done

  is a replacement for:

    List.iter 
      (fun s -> fprintf oc ">>>%s<<< " s) 
      ["Hello"; "World"]

  with these important differences:

  1) the implementation uses a while loop and doesn't build a closure.

  2) the list constructor is inlined, i.e. the ["Hello"; "World"] list
  is not built at all. Tuples in lists of tuples are not built either.
  Because of point (1), the loop body will be replicated, which can
  result in pretty big preprocessed programs if not used wisely.

  3) side effect of list inlining: the list items do not have to have 
  the same type. The following works:
    for l in [ []; [1]; ["a";"b"] ] do
      Printf.printf "%i " (List.length l)
    done


  Example 2:

  let pairs = [ (1, "OCaml"); (3, "Python"); (2, "C") ] in
  for (id, lang) in pairs do
    Printf.printf "(%i, %s) " id lang
  done

  Here, the list definition is part of regular OCaml code so it is
  not inlined. We want to illustrate that the loop variable can be 
  destructured directly just as if we were using "fun" or "let".
*)

let unique =
  let n = ref 0 in
  fun () -> incr n; "__pa_forin" ^ string_of_int !n

let iter _loc p l e =
  let id = unique () in
  let id2 = unique () in
  <:expr< 
  let $lid:id$ = ref $l$ in 
  while $lid:id$.val != [] do { 
    let $lid:id2$ = $lid:id$.val in
    let $p$ = List.hd $lid:id2$ in do { 
      $e$; 
      $lid:id$.val := List.tl $lid:id2$ 
    } 
  } >>

let rec expand ?(use_list_iter = false) _loc p l e =
  match l with
      <:expr< [ $head$ :: $tail$ ] >> -> 
            let pel =
              match p, head with
                  <:patt< ( $list:pl$ ) >>, <:expr< ( $list:el$ ) >> -> 
                        List.combine pl el
                | _ -> [(p, head)] in
            let e1 = <:expr< let $list:pel$ in $e$ >> in
            (match tail with
                 <:expr< [ ] >> -> e1
               | _ ->
                 <:expr< do { $e1$; $expand _loc p tail e$ } >>)
    | <:expr< [ ] >> -> <:expr< () >>
    | _ -> 
        if use_list_iter then
          <:expr< List.iter (fun [ $p$ -> $e$ ]) $l$ >>
        else 
          iter _loc p l e

EXTEND
  Pcaml.expr: LEVEL "expr1" [
    [ "for"; p = Pcaml.patt; "in"; l = Pcaml.expr; 
      "do"; e = Pcaml.expr; "done" -> expand _loc p l e ]
  ];
END