Skip to content
Snippets Groups Projects
js_of_ast.ml 33.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • open Params
    
    module L = Logged (Token_generator) (struct let size = 256 end)
    
    Thomas Wood's avatar
    Thomas Wood committed
    (* TODO: Field annotations for builtin type constructors *)
    
    charguer's avatar
    charguer committed
    let string_of_longident i =
      String.concat "." @@ Longident.flatten @@ i
    
    
    charguer's avatar
    charguer committed
    
    (****************************************************************)
    (* STRING UTILITIES *)
    
    
     * Useful functions (Warning: shadows `show_list' from Mytools)
    
    let show_list_f f sep l = l
    
      |> List.map f
      |> List.fold_left (fun acc x -> acc ^ (if acc = "" then "" else sep) ^ x) ""
    
      List.fold_left (fun acc x -> acc ^ (if acc = "" then "" else sep) ^ x) "" l
    
    let rec zip l1 l2 = match l1, l2 with
      | [], x :: xs | x :: xs, [] -> failwith "zip: list must have the same length."
      | [], [] -> []
      | x :: xs, y :: ys -> (x, y) :: zip xs ys
    
    
    Thomas Wood's avatar
    Thomas Wood committed
    let unzip l =
    
      let rec aux acc1 acc2 = function
      | [] -> List.rev acc1, List.rev acc2
      | (x, y) :: xs -> aux (x :: acc1) (y :: acc2) xs
    in aux [] [] l
    
    
    let string_fold_lefti f acc s =
      let len = String.length s in
      let rec aux f acc i =
        if i = len then acc
        else aux f (f acc i s.[i]) (succ i)
      in aux f acc 0
    
    let string_fold_righti f s acc =
     let len = String.length s in
      let rec aux f i acc =
        if i = len then acc
        else f i s.[i] (aux f (succ i) acc)
      in aux f 0 acc
    
    
    charguer's avatar
    charguer committed
    
    (****************************************************************)
    (* RECOGNIZING EXPRESSIONS *)
    
    let is_sbool x = List.mem x ["true" ; "false"]
    
    
    Alan Schmitt's avatar
    Alan Schmitt committed
    let is_unit x = x = "()"
    
    let unit_repr = "{}"
    
    
    charguer's avatar
    charguer committed
    (* Given an expression, check whether it is a primitive type or a constructed type *)
    let exp_type_is_constant exp =
      List.exists (Ctype.matches exp.exp_env exp.exp_type)
      [Predef.type_bool; Predef.type_int; Predef.type_char; Predef.type_string; Predef.type_float]
    
    
    let is_infix f args = match args with
      | _ :: [] | [] -> false
      | x :: xs ->
         let open Location in
         let f_loc = (f.exp_loc.loc_start, f.exp_loc.loc_end) in
         let args_loc = (x.exp_loc.loc_start, x.exp_loc.loc_end) in
         if fst args_loc < fst f_loc then true else false
    
    charguer's avatar
    charguer committed
    exception Map_fields_elements_mismatch_number_args
    
    (* here, bind is the function to be applied to a field and an element,
       and it returns an option, with None when the entry should be ignored,
       and with a result otherwise, to be added to the list of results *)
    
    let map_filter_fields_elements bind fields elements =
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      let rec aux = function
        | [], [] -> []
        | f :: fs, e :: es ->
          let res = aux (fs,es) in
    
    Alan Schmitt's avatar
    Alan Schmitt committed
          begin match bind f e with
    
    Alan Schmitt's avatar
    Alan Schmitt committed
            | None -> res
            | Some p -> p :: res  (* p is a pair identifier, code to be bound *)
          end
    
    charguer's avatar
    charguer committed
        | _ -> raise Map_fields_elements_mismatch_number_args
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      in aux (fields, elements)
    
    charguer's avatar
    charguer committed
    
    let map_cstr_fields ?loc bind (cstr : constructor_description) elements =
      let fields = extract_cstr_attrs cstr in
      try map_filter_fields_elements bind fields elements 
      with   Map_fields_elements_mismatch_number_args -> 
         error ?loc ("Insufficient fieldnames for arguments to " ^ cstr.cstr_name)
      
    
    charguer's avatar
    charguer committed
    (****************************************************************)
    (* PPF HELPERS *)
    
    
    (**
     * Before-hand definitions of Pretty-Printer-Format for converting ocaml
     * to ECMAScript, therefore all of them are in a single place.
     *)
    
    
    let ppf_lambda_wrap s =
    
      Printf.sprintf "(function () {@;<1 2>@[<v 0>%s@]@,}())@," s
    
    let ppf_branch case binders expr =
    
      Printf.sprintf "%s: @[<v 0>%s@,return %s;@]"
    
                     case binders expr
    
    let ppf_let_in decl exp =
    
        Printf.sprintf "%s@,return %s;"
    
                       decl exp
    
      in ppf_lambda_wrap s
    
      (L.log_line (Printf.sprintf "function (%s) {" args) [L.Enter; (L.CreateCtx args)]) ^ (Printf.sprintf "@;<1 2>@[<v 0>return %s;@]@,}" body)
    
      Printf.sprintf "%s(%s)"
    
                     f args
    
    let ppf_apply_infix f arg1 arg2 =
    
      Printf.sprintf "%s %s %s"
    
                     arg1 f arg2
    
    let ppf_match value cases const =
    
    charguer's avatar
    charguer committed
      let cons_fld = if const then "" else ".tag" in
    
    charguer's avatar
    charguer committed
      let cases = 
        match !current_mode with
    
    charguer's avatar
    charguer committed
        | Mode_cmi -> assert false
    
    charguer's avatar
    charguer committed
        | Mode_unlogged -> cases
        | Mode_line_token
    
    charguer's avatar
    charguer committed
        | Mode_logged -> cases 
          (* TODO: put back if there is not already a default case:
              ^ "@,default: throw \"No matching case for switch\";" *)
    
    charguer's avatar
    charguer committed
        in
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      let s = Printf.sprintf "switch (%s%s) {@;<1 2>@[<v 0>%s@]@,}@,"
    
    charguer's avatar
    charguer committed
      in s
    
    let ppf_match_case c =
      Printf.sprintf "case %s" c
    
    let ppf_match_binders binders =
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      if binders = [] then "" else
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      let binds = show_list ", " (List.map (fun (id,se) -> Printf.sprintf "%s = %s" id se) binders) in
    
    charguer's avatar
    charguer committed
      Printf.sprintf "var %s;@," binds
    
      Printf.sprintf "[%s]"
    
                     values
    
    let ppf_tuple = ppf_array
    
    let ppf_ifthen cond iftrue =
    
    charguer's avatar
    charguer committed
      Printf.sprintf "(function () {@;<1 2>@[<v 2>@,if (%s) {@,return %s;@,}@]@,})()"
    
                     cond iftrue
    
    
    let ppf_ifthenelse cond iftrue iffalse =
    
    charguer's avatar
    charguer committed
      Printf.sprintf "@[<v 2>@,if (%s) {@, %s @,} else {@, %s @,} @]@,"
    
                     cond iftrue iffalse
    
    
    let ppf_sequence exp1 exp2 =
    
    charguer's avatar
    charguer committed
      Printf.sprintf "%s;@,%s"
    
                     exp1 exp2
    
      let s =
    
        Printf.sprintf "@[<v 2>while(%s) {@;<1 2>%s@,@]}"
    
                       cd body
      in ppf_lambda_wrap s
    
    let ppf_for id start ed flag body =
      let fl_to_string = function
        | Upto   -> "++"
        | Downto -> "--" in
      let fl_to_symbl = function
        | Upto   -> "<="
    
        | Downto -> ">=" in
      let s =
    
        Printf.sprintf "@[<v 2>for (%s = %s ; %s %s %s ; %s%s) {@,%s@]@,}"
    
                       id start id (fl_to_symbl flag) ed (fl_to_string flag) id body
      in ppf_lambda_wrap s
    
    Paul Iannetta's avatar
    Paul Iannetta committed
    (*let ppf_single_cstr tag =
    
      Printf.sprintf "%s"
    
    let ppf_cstr tag value =
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      Some (Printf.sprintf "%s: %s" tag value)
    
    charguer's avatar
    charguer committed
    (* deprecated:
      let expanded_constructors = map_cstr_fields (*~loc*) ppf_cstr cd args in
    *)
    
    charguer's avatar
    charguer committed
    let ppf_cstrs styp cstr_name rest =
      let comma = if rest = "" then "" else "," in
      let styp_full =
        match !current_mode with
        | Mode_cmi -> assert false
        | Mode_unlogged -> ""
        | Mode_line_token
        | Mode_logged -> Printf.sprintf "type: \"%s\", " styp
        in
    
    charguer's avatar
    charguer committed
      Printf.sprintf "{@[<v 2>%stag: \"%s\"%s %s@]}" (* TODO: cleanup *)
    
    charguer's avatar
    charguer committed
        styp_full cstr_name comma rest
    
    let ppf_cstrs_fct cstr_fullname args =
       ppf_apply cstr_fullname (show_list ", " args)
    
    Paul IANNETTA's avatar
    Paul IANNETTA committed
    
    
    let ppf_record llde =
      let rec aux acc = function
    
    charguer's avatar
    charguer committed
        | []               -> Printf.sprintf "{@[<v 0>%s@]@,}" (*"@[<v 2>{@;<1 2>%s@]@,}"*) (* TODO: cleanup *) acc
    
        | (lbl, exp) :: [] -> aux (acc ^ Printf.sprintf "%s: %s" lbl exp) []
        | (lbl, exp) :: xs -> aux (acc ^ Printf.sprintf "%s: %s,@," lbl exp) xs
    
      in aux "" llde
    
    let ppf_decl id expr = Printf.sprintf "@[<v 0>%s: %s,@,@]" id expr
    
    
    let ppf_pat_array id_list array_expr =
    
      Printf.sprintf "var __%s = %s;@," "array" array_expr ^
    
        List.fold_left2 (fun acc (name, exp_type) y -> acc ^ Printf.sprintf "@[<v 0>var %s = __%s[%d];@,@]" name "array" y)
                        "" id_list @@ range 0 (List.length id_list - 1)
    
    let ppf_field_access expr field =
      Printf.sprintf "%s.%s" expr field
    
    
    (* ' is not permitted in JS identifier names, and $ is not permitted in OCaml ones *)
    let ppf_ident_name =
      String.map (function '\'' -> '$' | c -> c)
    
    let ppf_ident i =
      i |> Ident.name |> ppf_ident_name
    
    
    Thomas Wood's avatar
    Thomas Wood committed
    let ppf_path =
      Path.name
    
    Paul IANNETTA's avatar
    Paul IANNETTA committed
    
    
    let ppf_module content =
      Printf.sprintf "{@,%s@,}" content
    
    charguer's avatar
    charguer committed
    let ppf_module_wrap name content names_bound =
      let bindings = show_list ", " (List.map (fun id -> Printf.sprintf "@\n  %s: %s" id id) names_bound) in
      Printf.sprintf "var %s = (function() {@,@, %s @,@,@\nreturn {@\n %s };@,@\n})();@," name content bindings
    
    charguer's avatar
    charguer committed
    
    
    
    charguer's avatar
    charguer committed
    
    (****************************************************************)
    (* FRESH ID NAMES *)
    
    
    Alan Schmitt's avatar
    Alan Schmitt committed
    let id_fresh =
    
    charguer's avatar
    charguer committed
      let r = ref 0 in
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      fun prefix -> (incr r; prefix ^ string_of_int !r)
    
    charguer's avatar
    charguer committed
    
    
    
    Alan Schmitt's avatar
    Alan Schmitt committed
    (****************************************************************)
    (* FRESH TOKEN NAMES *)
    
    
    charguer's avatar
    charguer committed
    let token_basename_ref = ref "no_token_basename_registered"
    
    let token_register_basename basename =
      token_basename_ref := basename
    
    
    Alan Schmitt's avatar
    Alan Schmitt committed
    let token_fresh =
      let r = ref 0 in
    
    charguer's avatar
    charguer committed
      fun () -> (incr r; 
        let token_start = Printf.sprintf "#<%d#" !r in
        let token_stop = Printf.sprintf "#%d>#" !r in
        let token_lineof = Printf.sprintf "lineof(\"%s.js\", %d)" !token_basename_ref !r in  
        (token_start, token_stop, token_lineof))
    
    Alan Schmitt's avatar
    Alan Schmitt committed
    
    
    
    charguer's avatar
    charguer committed
    (****************************************************************)
    (* CONTEXTS *)
    
    (** Fresh name generator for contexts *)
    
    let ctx_fresh =
      let r = ref 0 in
      fun () -> (incr r; "ctx_" ^ string_of_int !r)
    
    let ctx_initial =
      "ctx_empty"
    
    
    (****************************************************************)
    (* LOGGED CONSTRUCTORS *)
    
    
    charguer's avatar
    charguer committed
    let generate_logged_case spat binders ctx newctx sbody need_break =
      (* Note: binders is a list of pairs of id *)
    
    charguer's avatar
    charguer committed
      (* Note: if binders = [], then newctx = ctx *)
    
    charguer's avatar
    charguer committed
      let (token_start, token_stop, token_lineof) = token_fresh() in
      let (shead, sintro) =
        match !current_mode with
    
    charguer's avatar
    charguer committed
        | Mode_cmi -> assert false
    
    charguer's avatar
    charguer committed
        | Mode_line_token -> 
          (token_start, token_stop)
        | Mode_logged ->
          let ids = List.map fst binders in
          let mk_binding x =
            Printf.sprintf "{key: \"%s\", val: %s}" x x
          in
          let bindings =
            Printf.sprintf "[%s]" (show_list ", " (List.map mk_binding ids))
          in 
          let spreintro =
            if binders = [] then ""
            else Printf.sprintf "var %s = ctx_push(%s, %s);@," newctx ctx bindings
          in
          let sintro = Printf.sprintf "%slog_event(%s, %s, \"case\");@,"
            spreintro token_lineof newctx in
          ("", sintro)
        | Mode_unlogged -> ("", "")
    
    Alan Schmitt's avatar
    Alan Schmitt committed
        in
      let sbinders = ppf_match_binders binders in
    
    charguer's avatar
    charguer committed
      (Printf.sprintf "@[<v 0>%s%s:@;<1 2>@[<v 0>%s%s%s%s@]@]"
         shead spat sbinders sintro sbody
    
    Alan Schmitt's avatar
    Alan Schmitt committed
         (if need_break then "@,break;" else ""))
    
    charguer's avatar
    charguer committed
    
    
    charguer's avatar
    charguer committed
    
    (* generate_logged_case implement using
    [insertCaseCode(caseBody,bindings,ctx,newctx,sbody)]
    
    charguer's avatar
    charguer committed
    £4424;caseBody;codeOf(bindings);sbody;break
    
    charguer's avatar
    charguer committed
    case(caseBody); codeOf(bindings); newctx=ctx_push(ctx,bindings); logEvent(LINEOF(432423), "case", newctx);sbody;break
    
    charguer's avatar
    charguer committed
    
    with help of
    
      if binders = [] then L.log_line (ppf_branch spat binders se) [(L.Exit)]
      else
        let typ = match List.rev (Str.split (Str.regexp " ") spat) with
          | [] -> assert false
          | x :: xs -> String.sub x 0 (String.length x)
        in L.log_line (ppf_branch spat binders se) [(L.Exit); (L.ReturnStrip); (L.Add (binders, typ))]
    
    *)
    
    
    Alan Schmitt's avatar
    Alan Schmitt committed
    (* LATER: optimize return when it's a value *)
    
    charguer's avatar
    charguer committed
    
    
    charguer's avatar
    charguer committed
    let generate_logged_return ctx sbody = 
    
    charguer's avatar
    charguer committed
      let (token_start, token_stop, token_lineof) = token_fresh() in
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      match !current_mode with
    
    charguer's avatar
    charguer committed
      | Mode_cmi -> assert false
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      | Mode_line_token ->
    
    charguer's avatar
    charguer committed
         Printf.sprintf "%sreturn %s; %s" token_start sbody token_stop
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      | Mode_logged ->
        let id = id_fresh "_return_" in
    
    charguer's avatar
    charguer committed
        Printf.sprintf "var %s = %s;@,log_event(%s, ctx_push(%s, [{key: \"return_value\", value: %s}]), \"return\");@,return %s; "
    
    charguer's avatar
    charguer committed
          id sbody token_lineof ctx id id
    
    charguer's avatar
    charguer committed
      | Mode_unlogged -> 
    
    charguer's avatar
    charguer committed
         Printf.sprintf "return %s; " sbody
    
    charguer's avatar
    charguer committed
         (* Printf.sprintf "@[<v 0>return %s;@]" sbody *)
    
    charguer's avatar
    charguer committed
    (*
    ----
      [insertReturnCode(e,ctx)]
    
    TOKEN(432423);return e
    
    var t=e; logEvent(LINEOF(432423), ctx_push(ctx, {"return",t}), "return"); return t
    ----
    *)
    
    
    charguer's avatar
    charguer committed
    
    
    let generate_logged_let ids ctx newctx sdecl sbody =
    
    charguer's avatar
    charguer committed
      let (token_start, token_stop, token_lineof) = token_fresh() in
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      match !current_mode with
    
    charguer's avatar
    charguer committed
      | Mode_cmi -> assert false
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      | Mode_line_token ->
    
    charguer's avatar
    charguer committed
         Printf.sprintf "%s%s%s@,%s" token_start sdecl token_stop sbody  
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      | Mode_logged ->
        let mk_binding x =
          Printf.sprintf "{key: \"%s\", val: %s}" x x
        in
        let bindings =
          Printf.sprintf "[%s]" (show_list ", " (List.map mk_binding ids))
        in 
    
    charguer's avatar
    charguer committed
        Printf.sprintf "%s@,var %s = ctx_push(%s, %s);@,log_event(%s, %s, \"let\");@,%s@,"
          sdecl newctx ctx bindings token_lineof newctx sbody
    
    charguer's avatar
    charguer committed
      | Mode_unlogged -> 
         Printf.sprintf "%s@,%s" sdecl sbody
    
    charguer's avatar
    charguer committed
    
    (*
    
    ----
      [insertLetCode(x,e,ctx,newctx,sbody)]
    
    TOKEN(432423);var x = e;sbody
    
    var x=e; var newctx=ctx_push(ctx,x,e); logEvent(LINEOF(432423), "let", ctx);sbody
    ----
    *)
    
    
    Alan Schmitt's avatar
    Alan Schmitt committed
    (* LATER: factoriser les bindings *)
    
    
    charguer's avatar
    charguer committed
    let generate_logged_enter arg_ids ctx newctx sbody = 
    
    charguer's avatar
    charguer committed
      let (token_start, token_stop, token_lineof) = token_fresh() in
      let (shead1, shead2, sintro) =
    
    Alan Schmitt's avatar
    Alan Schmitt committed
        match !current_mode with
    
    charguer's avatar
    charguer committed
        | Mode_cmi -> assert false
    
    charguer's avatar
    charguer committed
        | Mode_line_token -> (token_start, token_stop, "")
    
    Alan Schmitt's avatar
    Alan Schmitt committed
        | Mode_logged ->
          let mk_binding x =
            Printf.sprintf "{key: \"%s\", val: %s}" x x
          in
          let bindings =
            Printf.sprintf "[%s]" (show_list ", " (List.map mk_binding arg_ids))
          in 
    
    charguer's avatar
    charguer committed
          let sintro = Printf.sprintf "var %s = ctx_push(%s, %s);@,log_event(%s, %s, \"enter\");@,"
            newctx ctx bindings token_lineof newctx in
          ("", "", sintro)
        | Mode_unlogged -> ("", "", "")
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      in
      let args = String.concat ", " arg_ids in
    
    charguer's avatar
    charguer committed
      Printf.sprintf "%sfunction (%s)%s {@;<1 2>@[<v 0>%s%s@]@,}" shead1 args shead2 sintro sbody
    
    charguer's avatar
    charguer committed
    
    (*
    
    charguer's avatar
    charguer committed
    
    
    charguer's avatar
    charguer committed
    ----
    function(x,y) {
      [isnertEnterCode(bindings,ctx,newctx)]fdqfdsf
      }
    
    TOKEN(432423);sbody
    
    var newctx = ctx_push(bindings);
    logEvent(LINEOF(432423), newctx, "enter");sbody
    ----
    
    may reuse 
        ppf_function args body
    
    *)
    
    charguer's avatar
    charguer committed
    
    
    
    (****************************************************************)
    (* DESTINATIONS *)
    
    (** Destination-style translation of expressions *)
    
    type dest = 
      | Dest_ignore
      | Dest_return
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      | Dest_assign of string
    
    charguer's avatar
    charguer committed
      | Dest_inline
    
    charguer's avatar
    charguer committed
    
    
    Alan Schmitt's avatar
    Alan Schmitt committed
    let apply_dest ctx dest sbody =
    
    charguer's avatar
    charguer committed
      match dest with
      | Dest_ignore -> sbody
      | Dest_return -> generate_logged_return ctx sbody
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      | Dest_assign id -> Printf.sprintf "var %s = %s;" id sbody
    
    charguer's avatar
    charguer committed
      | Dest_inline -> sbody
    
    charguer's avatar
    charguer committed
    
    (* LATER: pull out the "var" out of switch *)
    
    
    charguer's avatar
    charguer committed
    exception Not_good_for_dest_inline
    
    let reject_inline dest =
      if dest = Dest_inline then raise Not_good_for_dest_inline
    
    
    charguer's avatar
    charguer committed
    
    (****************************************************************)
    (* TRANSLATION *)
    
    Thomas Wood's avatar
    Thomas Wood committed
    
    
    charguer's avatar
    charguer committed
    (* takes a list of pairs made of: list of strings, and list of strings, 
       and return a pair of a string (the string concat with newlines of the fst strings),
       and a list of strings (the list flatten of the snd strings) *)
    
    let combine_list_output args = 
       let (strs,bss) = List.split args in
       (show_list "@,@," strs), (List.flatten bss)
    
    
    let rec js_of_structure s =
    
    charguer's avatar
    charguer committed
       let rec extract_opens acc items =
          match items with
          | { str_desc = Tstr_open od }::items2 ->
             extract_opens (od.open_path::acc) items2
          | _ -> (List.rev acc, items)
          in
       let open_paths, items = extract_opens [] s.str_items in
       let contents, namesbound = combine_list_output (List.map (fun strct -> js_of_structure_item strct) items) in
       let prefix = List.fold_left (fun str path -> str ^ "with (" ^ ppf_path path ^ ") {@\n") "" open_paths in
       let postfix = List.fold_left (fun str path -> str ^ "@\n}// end of with " ^ ppf_path path) "" open_paths in
       (prefix ^ contents ^ postfix, namesbound)
    
    and js_of_submodule m =
    
    charguer's avatar
    charguer committed
      Printf.printf "warning: code generation is incorrect for local modules\n"; 
    
      let loc = m.mod_loc in
      match m.mod_desc with
    
    charguer's avatar
    charguer committed
      | Tmod_structure  s -> ppf_module (fst (*TODO*) (js_of_structure s))
    
      | Tmod_functor (id, _, mtyp, mexp) -> ppf_function (ppf_ident id) (js_of_submodule mexp)
    
    charguer's avatar
    charguer committed
      | Tmod_apply (m1, m2, _) -> ppf_apply (js_of_submodule m1) (js_of_submodule m2)
    
    Thomas Wood's avatar
    Thomas Wood committed
      | Tmod_ident (p,_) -> ppf_path p
    
      | Tmod_constraint _ -> out_of_scope loc "module constraint"
      | Tmod_unpack     _ -> out_of_scope loc "module unpack"
    
    
    charguer's avatar
    charguer committed
    and show_value_binding ctx vb = (* dest is Ignore *)
      js_of_let_pattern ctx vb.vb_pat vb.vb_expr
    
    
    and js_of_structure_item s =
    
      let loc = s.str_loc in
    
    charguer's avatar
    charguer committed
      | Tstr_eval (e, _)     -> 
         let str = Printf.sprintf "%s" @@ js_of_expression ctx_initial Dest_ignore e in
         (str, [])
      | Tstr_value (_, vb_l) -> 
         combine_list_output (~~ List.map vb_l (fun vb -> 
            let id = ident_of_pat vb.vb_pat in
            let sbody = js_of_expression_inline_or_wrap ctx_initial vb.vb_expr in
            let s = Printf.sprintf "@\n@\n var %s = %s;" id sbody in
            (s, [id])))
    
    charguer's avatar
    charguer committed
      | Tstr_type decls -> 
    
    charguer's avatar
    charguer committed
         combine_list_output (~~ List.map decls (fun decl -> 
    
    charguer's avatar
    charguer committed
            match decl.typ_type.type_kind with
            | Type_variant cstr_decls ->
               let styp = decl.typ_name.txt in
    
    charguer's avatar
    charguer committed
               combine_list_output (~~ List.map cstr_decls (fun (cd:Types.constructor_declaration) -> 
    
    charguer's avatar
    charguer committed
                  let cstr_name = cd.Types.cd_id.Ident.name in
                  let fields = extract_cstr_attrs_basic cstr_name cd.cd_attributes in
                  let sargs = show_list ", " fields in
                  let sbindings = map_filter_fields_elements ppf_cstr fields fields in
                  let rest = show_list ", " sbindings in
                  let sobj = ppf_cstrs styp cstr_name rest in 
    
    charguer's avatar
    charguer committed
                  let sbody = Printf.sprintf "function %s(%s) { return %s; }" cstr_name sargs sobj in
                  (sbody, [cstr_name])
                  ))
            | _ -> ("", [])
            ))
      | Tstr_open       _  -> ("",[]) (* Handle modules by use of multiple compilation/linking *)
      | Tstr_modtype    _  -> ("",[])
      | Tstr_module     b  -> 
         let id = ppf_ident b.mb_id in
         let sbody = ppf_decl id (js_of_submodule b.mb_expr) in
         (sbody, [id])
    
      | Tstr_primitive  _  -> out_of_scope loc "primitive functions"
      | Tstr_typext     _  -> out_of_scope loc "type extensions"
      | Tstr_exception  _  -> out_of_scope loc "exceptions"
      | Tstr_recmodule  _  -> out_of_scope loc "recursive modules"
      | Tstr_class      _  -> out_of_scope loc "objects"
      | Tstr_class_type _  -> out_of_scope loc "class types"
      | Tstr_include    _  -> out_of_scope loc "includes"
    
      | Tstr_attribute  _  -> out_of_scope loc "attributes"
    
    charguer's avatar
    charguer committed
    and js_of_branch ctx dest b eobj =
      let spat, binders = js_of_pattern b.c_lhs eobj in
    
    charguer's avatar
    charguer committed
      let newctx = if binders = [] then ctx else ctx_fresh() in
      let sbody = js_of_expression newctx dest b.c_rhs in
    
    charguer's avatar
    charguer committed
      let need_break = (dest <> Dest_return) in
      generate_logged_case spat binders ctx newctx sbody need_break 
         
    and js_of_expression_inline_or_wrap ctx e = 
      try 
        js_of_expression ctx Dest_inline e
      with Not_good_for_dest_inline ->
        js_of_expression_wrapped ctx e
    
    and js_of_expression_wrapped ctx e = (* dest = Dest_return *)
      ppf_lambda_wrap (js_of_expression ctx Dest_return e)
    
    charguer's avatar
    charguer committed
    and js_of_expression_naming_argument_if_non_variable ctx obj name_prefix = 
      match obj.exp_desc with
    
    charguer's avatar
    charguer committed
      | Texp_ident (path, ident,  _) -> 
          "", (js_of_path_longident path ident)
    
    charguer's avatar
    charguer committed
      | _ ->  (* generate  var id = sexp;  *)
          let id = id_fresh "_switch_arg_" in
          let sintro = js_of_expression ctx (Dest_assign id) obj in
          (sintro ^ "@,"), id
    
    
    Alan Schmitt's avatar
    Alan Schmitt committed
    and js_of_expression ctx dest e =
    
    charguer's avatar
    charguer committed
      let inline_of_wrap = js_of_expression_inline_or_wrap ctx in (* shorthand *)
    
      let loc = e.exp_loc in
    
    charguer's avatar
    charguer committed
    
    
    charguer's avatar
    charguer committed
      | Texp_ident (path, ident,  _) -> 
          let sexp = js_of_path_longident path ident in
    
    Alan Schmitt's avatar
    Alan Schmitt committed
          apply_dest ctx dest sexp
    
    charguer's avatar
    charguer committed
    
    
    charguer's avatar
    charguer committed
      | Texp_constant c -> 
    
    charguer's avatar
    charguer committed
          let sexp = js_of_constant c in
    
    Alan Schmitt's avatar
    Alan Schmitt committed
          apply_dest ctx dest sexp
    
    charguer's avatar
    charguer committed
    
    
    charguer's avatar
    charguer committed
      | Texp_let (_, vb_l, e) ->
    
    charguer's avatar
    charguer committed
        reject_inline dest;
    
    charguer's avatar
    charguer committed
        let (ids, sdecl) = begin match vb_l with  
          | [ { vb_pat = { pat_desc = Tpat_tuple el }; vb_expr = obj } ] -> (* binding tuples *)
    
    charguer's avatar
    charguer committed
             let (sintro, seobj) = js_of_expression_naming_argument_if_non_variable ctx obj "_tuple_arg_" in     
    
    charguer's avatar
    charguer committed
             let bind i var = 
                match var.pat_desc with
                | Tpat_var (id, _) -> 
                    let sid = ppf_ident id in
                    (sid, Printf.sprintf "%s[%d]" seobj i)
    
    charguer's avatar
    charguer committed
                | Tpat_any -> out_of_scope var.pat_loc "Underscore pattern in let-tuple"
    
    charguer's avatar
    charguer committed
                | _ -> out_of_scope var.pat_loc "Nested pattern matching"
                in
              let binders = List.mapi bind el in
              let ids = List.map fst binders in
              let sdecl = ppf_match_binders binders in
              (ids, sdecl)
    
    charguer's avatar
    charguer committed
          | [ { vb_pat = { pat_desc = Tpat_record (args, closed_flag) }; vb_expr = obj } ] -> (* binding records *)
              (* args : (Longident.t loc * label_description * pattern) list *)
             let (sintro, seobj) = js_of_expression_naming_argument_if_non_variable ctx obj "_record_arg_" in     
             let bind (arg_loc,label_descr,pat) = 
                let name = label_descr.lbl_name in
                match pat.pat_desc with
                | Tpat_var (id, _) -> 
                    let sid = ppf_ident id in
                    (sid, Printf.sprintf "%s.%s" seobj name)
                | Tpat_any -> out_of_scope e.exp_loc "Underscore pattern in let-record"
                | _ -> out_of_scope e.exp_loc "Nested pattern matching"
                in
              let binders = List.map bind args in
              let ids = List.map fst binders in
              let sdecl = ppf_match_binders binders in
              (ids, sdecl)
    
    charguer's avatar
    charguer committed
          | _ -> (* other cases *)
            let (ids,sdecls) = List.split (List.map (fun vb -> show_value_binding ctx vb) @@ vb_l) in
            let sdecl = String.concat lin1 @@ sdecls in
            (ids, sdecl)
          end in
    
    charguer's avatar
    charguer committed
        let newctx = ctx_fresh() in
        let sbody = js_of_expression newctx dest e in
    
    charguer's avatar
    charguer committed
        let sexp = generate_logged_let ids ctx newctx sdecl sbody in
    
    Alan Schmitt's avatar
    Alan Schmitt committed
        sexp
    
    charguer's avatar
    charguer committed
    
    
      | Texp_function (_, c :: [], Total) ->
        let rec explore pats e = match e.exp_desc with
          | Texp_function (_, c :: [], Total) ->
    
    charguer's avatar
    charguer committed
            let (p, e) = (c.c_lhs, c.c_rhs) in 
            explore (p :: pats) e
    
    charguer's avatar
    charguer committed
          | _ ->
    
    charguer's avatar
    charguer committed
            List.map ident_of_pat @@ List.rev @@ pats, e 
          in
        let arg_ids, body = explore [c.c_lhs] c.c_rhs in
        let newctx = ctx_fresh() in
    
    charguer's avatar
    charguer committed
        let sbody = js_of_expression newctx Dest_return body in
    
    charguer's avatar
    charguer committed
        let sexp = generate_logged_enter arg_ids ctx newctx sbody in
    
    Alan Schmitt's avatar
    Alan Schmitt committed
        apply_dest ctx dest sexp
    
    charguer's avatar
    charguer committed
    
    
    charguer's avatar
    charguer committed
      | Texp_apply (f, exp_l) ->
    
    charguer's avatar
    charguer committed
         (* first check not partial application *)
         let is_result_arrow = 
            let ty = e.exp_type in
            let ty = Ctype.repr ty in
            match ty.desc with
            | Tarrow(l, ty1, ty2, _) -> true
            | _ -> false
            in
         if is_result_arrow then out_of_scope loc "partial application";
         
    
    charguer's avatar
    charguer committed
         let sl' = exp_l  (* only used to know if infix *)
    
    charguer's avatar
    charguer committed
                   |> List.map (fun (_, eo, _) -> match eo with 
                                                  | None -> out_of_scope loc "optional apply arguments"
                                                  | Some ei -> ei) in
    
    charguer's avatar
    charguer committed
         let sl_clean = exp_l
    
    charguer's avatar
    charguer committed
                  |> List.map (fun (_, eo, _) -> match eo with 
    
    charguer's avatar
    charguer committed
                                                 | None -> out_of_scope loc "optional apply arguments" 
                                                 | Some ei -> ei) in
    
    charguer's avatar
    charguer committed
         let sl = sl_clean |> List.map (fun ei -> inline_of_wrap ei) in
         let se = inline_of_wrap f in
    
    charguer's avatar
    charguer committed
         let sexp = 
    
    charguer's avatar
    charguer committed
            if is_primitive_comparison f then begin
              if (List.length exp_l <> 2) 
                then out_of_scope loc "=== should be applied to 2 arguments";
              let typ = (List.hd sl_clean).exp_type in
              let stype = Print_type.string_of_type_exp typ in
              let stype = Str.global_replace (Str.regexp "\\.") "_" stype in
              ppf_apply ("_compare_" ^ stype) (String.concat ", " sl)
            end else if is_infix f sl' && List.length exp_l = 2 then begin
               ppf_apply_infix se (List.hd sl) (List.hd (List.tl sl))
            end else begin
               ppf_apply se (String.concat ", " sl)
            end in
    
    Alan Schmitt's avatar
    Alan Schmitt committed
         apply_dest ctx dest sexp
    
    charguer's avatar
    charguer committed
      | Texp_match (obj, l, [], Total) ->
    
    charguer's avatar
    charguer committed
         reject_inline dest;
    
    charguer's avatar
    charguer committed
         let (sintro, seobj) = js_of_expression_naming_argument_if_non_variable ctx obj "_switch_arg_" in     
    
    charguer's avatar
    charguer committed
         let sb = String.concat "@," (List.map (fun b -> js_of_branch ctx dest b seobj) l) in
    
    charguer's avatar
    charguer committed
         let const = exp_type_is_constant obj in
    
    Alan Schmitt's avatar
    Alan Schmitt committed
         let sexp = sintro ^ (ppf_match seobj sb const) in
    
    Alan Schmitt's avatar
    Alan Schmitt committed
         sexp
    
    charguer's avatar
    charguer committed
      | Texp_tuple (tl) -> 
    
    charguer's avatar
    charguer committed
         let sexp = ppf_tuple @@ show_list_f (fun exp -> inline_of_wrap exp) ", " tl in
    
    Alan Schmitt's avatar
    Alan Schmitt committed
         apply_dest ctx dest sexp
    
    charguer's avatar
    charguer committed
      | Texp_construct (p, cd, el) ->
        let cstr_fullname = string_of_longident p.txt in
        let cstr_name = cd.cstr_name in
    
    charguer's avatar
    charguer committed
        let cstr_fullname = 
          if cstr_fullname = "[]" then "mk_nil" 
          else if cstr_fullname = "::" then "mk_cons" 
          else cstr_fullname in  (* TODO: clean up this hack *)
    
    charguer's avatar
    charguer committed
        (*let styp = string_of_type_exp e.exp_type in*)
    
    charguer's avatar
    charguer committed
        let sexp =
    
    Alan Schmitt's avatar
    Alan Schmitt committed
          if is_sbool cstr_name then cstr_name else
          if is_unit cstr_name then unit_repr else
            begin
              let expr_strs = List.map (fun exp -> inline_of_wrap exp) el in
              ppf_cstrs_fct cstr_fullname expr_strs
            end in
    
    Alan Schmitt's avatar
    Alan Schmitt committed
        apply_dest ctx dest sexp
    
    charguer's avatar
    charguer committed
      | Texp_array      (exp_l)           -> ppf_array @@ show_list_f (fun exp -> inline_of_wrap exp) ", " exp_l
      | Texp_ifthenelse (e1, e2, None)    -> out_of_scope loc "if without else"
        (* ppf_ifthen (js_of_expression e1) (js_of_expression e2) *)
      | Texp_ifthenelse (e1, e2, Some e3) ->
         reject_inline dest;
         ppf_ifthenelse (inline_of_wrap e1) (js_of_expression ctx dest e2) (js_of_expression ctx dest e3)
    
    charguer's avatar
    charguer committed
      | Texp_sequence (e1, e2) -> 
         ppf_sequence (inline_of_wrap e1) (js_of_expression ctx dest e2)
    
    charguer's avatar
    charguer committed
      | Texp_while      (cd, body)        -> out_of_scope loc "while"
        (* ppf_while (js_of_expression cd) (js_of_expression body) *)
      | Texp_for        (id, _, st, ed, fl, body) -> out_of_scope loc "for"
        (* ppf_for (ppf_ident id) (js_of_expression st) (js_of_expression ed) fl (js_of_expression body) *)
    
    charguer's avatar
    charguer committed
      | Texp_record     (llde,_)          -> 
          let sexp = ppf_record (List.map (fun (_, lbl, exp) -> (lbl.lbl_name, inline_of_wrap exp)) llde) in
          apply_dest ctx dest sexp
    
    
    charguer's avatar
    charguer committed
      | Texp_field      (exp, _, lbl)     -> ppf_field_access (inline_of_wrap exp) lbl.lbl_name
    
    charguer's avatar
    charguer committed
      | Texp_assert      e                -> 
          let sexp = inline_of_wrap e in
          Printf.sprintf "throw %s;" sexp
    
    charguer's avatar
    charguer committed
      | Texp_function (label, cases, Total) when label = "" -> 
          let mk_pat pat_des =
            { pat_desc = pat_des;
              pat_loc = e.exp_loc;
              pat_extra = [];
              pat_type = e.exp_type;
              pat_env = e.exp_env;
              pat_attributes = [];
             } in
          let mk_exp exp_desc =
             { exp_desc = exp_desc;  
               exp_loc = e.exp_loc;
               exp_extra = [];
               exp_type = e.exp_type;
               exp_env = e.exp_env;
               exp_attributes = [];
             } in
          let name = "_fun_arg_" in
          let arg = Ident.create name in
          let thearg_lident = { txt = Longident.Lident name; loc = Location.none } in
          let thearg = mk_exp (Texp_ident (Path.Pident arg, thearg_lident, Obj.magic ())) in
          let thecase = {  
               c_lhs = mk_pat (Tpat_var (arg, Location.mknoloc name));
               c_guard = None;
               c_rhs = mk_exp (Texp_match (thearg, cases, [], Total));
              } in
          let exp = mk_exp (Texp_function (label, [thecase], Total)) in
          js_of_expression ctx dest exp
    
    
      | Texp_match      (_,_,_, Partial)  -> out_of_scope loc "partial matching"
      | Texp_match      (_,_,_,_)         -> out_of_scope loc "matching with exception branches"
      | Texp_try        (_,_)             -> out_of_scope loc "exceptions"
    
    charguer's avatar
    charguer committed
      | Texp_function (_, _, _) -> out_of_scope loc "use of labels"
    
    
      | Texp_variant    (_,_)             -> out_of_scope loc "polymorphic variant"
      | Texp_setfield   (_,_,_,_)         -> out_of_scope loc "setting field"
      | Texp_send       (_,_,_)           -> out_of_scope loc "objects"
      | Texp_new        (_,_,_)           -> out_of_scope loc "objects"
      | Texp_instvar    (_,_,_)           -> out_of_scope loc "objects"
      | Texp_setinstvar (_,_,_,_)         -> out_of_scope loc "objects"
      | Texp_override   (_,_)             -> out_of_scope loc "objects"
      | Texp_letmodule  (_,_,_,_)         -> out_of_scope loc "local modules"
      | Texp_lazy        _                -> out_of_scope loc "lazy expressions"
      | Texp_object     (_,_)             -> out_of_scope loc "objects"
      | Texp_pack        _                -> out_of_scope loc "packing"
    
    and js_of_constant = function
      | Const_int       n     -> string_of_int n
      | Const_char      c     -> String.make 1 c
    
    charguer's avatar
    charguer committed
      | Const_string   (s, _) -> "\"" ^ (String.escaped (String.escaped s)) ^ "\"" (* Warning: 2 levels of printf *)
    
      | Const_float     f     -> f
      | Const_int32     n     -> Int32.to_string n
      | Const_int64     n     -> Int64.to_string n
      | Const_nativeint n     -> Nativeint.to_string n
    
    charguer's avatar
    charguer committed
    
    and js_of_path_longident path ident =
      match String.concat "." @@ Longident.flatten ident.txt with
    
    charguer's avatar
    charguer committed
      (* for unit: *)
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      | "()"  -> unit_repr
    
    charguer's avatar
    charguer committed
      (* for bool: *)
      | "&&"  -> "&&"
      | "||"  -> "||"
      (* for float: *)
    
      | "+."  -> "+"
      | "*."  -> "*"
      | "-."  -> "-"
      | "~-." -> "-"
      | "/."  -> "/"
    
    charguer's avatar
    charguer committed
      | "<"   -> "<"
      | ">"   -> ">"
      | "<="   -> "<="
      | ">="   -> ">="
      (* for int: *)
      | "+"  -> "+"
      | "*"  -> "*"
      | "-"  -> "-"
      | "/"  -> "/"
      (* for string *)
    
    charguer's avatar
    charguer committed
      | "^"   -> "+" (* !!TODO: we want to claim ability to type our sublanguage, so we should not use this *)
    
    charguer's avatar
    charguer committed
      | res   -> 
          let res = if !generate_qualified_names && (Path.head path).name <> "Stdlib" 
                       then ppf_path path else res in
          ppf_ident_name res
    
    charguer's avatar
    charguer committed
    and is_primitive_comparison e =
       match e.exp_desc with
    
    charguer's avatar
    charguer committed
       | Texp_ident (path, ident,  _) ->
          let sexp = js_of_path_longident path ident in
    
    charguer's avatar
    charguer committed
          sexp = "==="
    
    charguer's avatar
    charguer committed
          (* TODO: this text could be optimized *)
    
    charguer's avatar
    charguer committed
       | _ -> false
    
    
    and ident_of_pat pat = match pat.pat_desc with
    
      | Tpat_var (id, _) -> ppf_ident id
    
    charguer's avatar
    charguer committed
      | Tpat_any         -> id_fresh "_pat_any_"
    
      | _ -> error ~loc:pat.pat_loc "functions can't deconstruct values"
    
    charguer's avatar
    charguer committed
    (* returns the name bound and the code that assigns a value to this name *)
    and js_of_let_pattern ctx pat expr =
      let id = 
        match pat.pat_desc with
    
    Alan Schmitt's avatar
    Alan Schmitt committed
        | Tpat_var (id, _) -> ppf_ident id
    
        | Tpat_any -> Printf.printf "warning: unsupported let-any\n"; ""
        | Tpat_alias _ -> Printf.printf "warning: unsupported let-alias\n"; ""
        | Tpat_constant _ -> Printf.printf "warning: unsupported let-constant\n"; ""
        | Tpat_tuple _ -> Printf.printf "warning: unsupported let-tuple\n"; ""
        | Tpat_construct _ -> Printf.printf "warning: unsupported let-construct\n"; ""
        | Tpat_variant _ -> Printf.printf "warning: unsupported let-variant\n"; ""
        | Tpat_record _ -> Printf.printf "warning: unsupported let-record\n"; ""
        | Tpat_array _ -> Printf.printf "warning: unsupported let-array\n"; ""
        | Tpat_or _ -> Printf.printf "warning: unsupported let-or\n"; ""
        | Tpat_lazy _ -> Printf.printf "warning: unsupported let-lazy\n"; ""
    
    charguer's avatar
    charguer committed
          (*  error ~loc:pat.pat_loc "let can't deconstruct values"  *)
    
    charguer's avatar
    charguer committed
        in
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      (id, js_of_expression ctx (Dest_assign id) expr)
    
    charguer's avatar
    charguer committed
    
      (* LATER: for   let (x,y) = e,  encode as  translate(e,assign z); x = z[0]; y=z[1] 
        | Tpat_tuple (pat_l)
        | Tpat_array (pat_l) ->
           let l = List.map
                     (function pat ->
                               match pat.pat_desc with
                               | Tpat_var (id, _) -> (ppf_ident id, string_of_type_exp pat.pat_type)
                               | _ -> out_of_scope pat.pat_loc "nested pattern-matching in tuples or arrays"
                     ) pat_l in
           ppf_pat_array l sexpr
           *)
    
    (* [js_of_pattern] translates a pattern to a "case" statement of a switch,
       and a list of assignements of variables (pairs of identifier and body).
       Nested patterns are not supported.
       It returns a pair: spat (the "case" instruction), binders (the assignements) *)
    and js_of_pattern pat obj = 
    
      let loc = pat.pat_loc in
    
      match pat.pat_desc with
    
    charguer's avatar
    charguer committed
      | Tpat_any -> 
         "default", []
      | Tpat_constant c -> 
         ppf_match_case (js_of_constant c), []
    
      | Tpat_construct (_, cd, el) ->
    
         let c = cd.cstr_name in
    
         let spat = if is_sbool c then ppf_match_case c else ppf_match_case ("\"" ^ c ^ "\"") in
    
    charguer's avatar
    charguer committed
         let bind field var = 
            match var.pat_desc with
            | Tpat_var (id, _) -> 
    
    Alan Schmitt's avatar
    Alan Schmitt committed
                Some (ppf_ident id, Printf.sprintf "%s.%s" obj field)
            | Tpat_any -> None
    
    charguer's avatar
    charguer committed
            | _ -> out_of_scope var.pat_loc "Nested pattern matching"
            in
          let binders = map_cstr_fields ~loc bind cd el in
          spat, binders
      | Tpat_var (id, _) -> unsupported ~loc "Tpat_var"
    
    charguer's avatar
    charguer committed
      | Tpat_tuple el -> unsupported ~loc "tuple matching, if not in a simple let-binding"
    
      | Tpat_array el -> unsupported ~loc "array-match"
      | Tpat_record (_,_) -> unsupported ~loc "record"
    
    charguer's avatar
    charguer committed
      | Tpat_or (_,_,_) -> error ~loc "or pattern not implemented yet"
    
      | Tpat_alias (_,_,_) -> out_of_scope loc "alias-pattern"
      | Tpat_variant (_,_,_) -> out_of_scope loc "polymorphic variants in pattern matching"
      | Tpat_lazy _ -> out_of_scope loc "lazy-pattern"
    
    Thomas Wood's avatar
    Thomas Wood committed
    
    
    charguer's avatar
    charguer committed
    let to_javascript basename module_name typedtree =
      token_register_basename basename;
    
    charguer's avatar
    charguer committed
      let (content,names_bound) = js_of_structure typedtree in
      let pre_res = ppf_module_wrap module_name content names_bound in
    
    Alan Schmitt's avatar
    Alan Schmitt committed
      let str_ppf = Format.str_formatter in
      Format.fprintf str_ppf (Scanf.format_from_string pre_res "");
      Format.flush_str_formatter ()
    
    charguer's avatar
    charguer committed
    
    
    charguer's avatar
    charguer committed
    
    
    charguer's avatar
    charguer committed
    (****************************************************************)
    (* COMMENTS *)
    
    charguer's avatar
    charguer committed
    
    (*
    ctx_empty
    
    charguer's avatar
    charguer committed
    ctx_push(ctx, bindings)   where bindings = [ { key: "ls", val: ls}, { key:"xs", val:xs } ]
    
    charguer's avatar
    charguer committed
    
    
    charguer's avatar
    charguer committed
    push("ls", ls, push("v", v, push("y", y, ctx314)); 
    
    
    charguer's avatar
    charguer committed
    example:  
      ctx321 = ctx_push(ctx320, bindings); log(|line|, ctx321, "ctx_push")
    
    
    
    charguer's avatar
    charguer committed
      enter  (or call)   => arguments of the call + name of new ctx
      return (was exit)  => return value
      let (on the "in")  => new binding + name of new ctx
      case               => bound variables + name of new ctx
    
    
    
    
    
    
    
      type token_info = ctx_operation * current ctx
    
    charguer's avatar
    charguer committed
    
      
      if  ==> viewed as match with case true/false.
    
    
    ctx_empty is passed on each structure_item
    on each ctx extension, we need a fresh name (enter, let, match_branch)
    (for return values, do the extension on the fly)
    
       
       return f(x);
    translates as
       var v213 = f(x);
       log(|line|, ctx_push(ctx320, {key: "return", val: v213}), "return")
    
    
    
      match v with | None -> x | Some y -> y
    translates as
      function() { 
    
    
      
    ----------------------
      let f ... =
        match ...
    
    => 
      switch
        case:
          return;
    
    ----------------------
      let f ... =
        match .. -> 
          match ...
    
    =>
      return
    
    ----------------------
      let x = match ... in ...
    => 
      switch ...
        case:
          x = ..; break;
        case:
          x = ..; break;
    
    
    ----------------------
      let x = 
        match .. ->
          match .. ->
    => 
      would not work without wrapping
    
    ----------------------
    
      f (match ...)
    => 
      requires A-normalization
    
    
    Alan Schmitt's avatar
    Alan Schmitt committed
    *)