Skip to content
Snippets Groups Projects
monad_ppx.ml 2.76 KiB
open Ast_mapper
open Ast_helper
open Asttypes
open Parsetree
open Longident

let monad_mapping =
   [("run", "if_run");
    ("string", "if_string");
    ("object", "if_object");
    ("value", "if_value");
    ("prim", "if_prim");
    ("number", "if_number");
    ("some", "if_some");
    ("bool", "if_bool");
    ("void", "if_void");
    ("success", "if_success");
   ]

(* e.g. 

  let%some x = expr in cont
becomes
   if_some expr (fun x -> cont)

  let%if_spec (s,x) = expr in cont
becomes
   if_spec expr (fun s x -> cont) 

 *)


let generate_mapper namesid = function argv ->
  { default_mapper with
    expr = fun mapper expr ->
      let aux e = mapper.expr mapper e in
      match expr with
      (* Is this an extension node? *)
      | { pexp_desc =
            Pexp_extension ({ txt = name; loc }, pstr)} ->
        begin
          try
            let ident = List.assoc name namesid in
            match pstr with
            | PStr [{ pstr_desc =
                        Pstr_eval ({ pexp_loc  = loc;
                                     pexp_desc = Pexp_let
                                         (rf,
                                          [{pvb_pat = {ppat_desc = Ppat_var _} as p;
                                            pvb_expr = e}],
                                          cont)
                                   }, _)}] ->
              Exp.apply ~loc (Exp.ident
                                (Location.mkloc
                                   (Longident.Lident ident) Location.none))
                [("", aux e);
                 ("", Exp.fun_ "" None p (aux cont))]
            | PStr [{ pstr_desc =
                        Pstr_eval ({ pexp_loc  = loc;
                                     pexp_desc = Pexp_let
                                         (rf,
                                          [{pvb_pat =
                                              {ppat_desc =
                                                 Ppat_tuple [p1;p2]};
                                            pvb_expr = e}],
                                          cont)
                                   }, _)}] ->
              Exp.apply ~loc (Exp.ident
                                (Location.mkloc
                                   (Longident.Lident ident) Location.none))
                [("", aux e);
                 ("", Exp.fun_ "" None p1 (Exp.fun_ "" None p2 (aux cont)))]
            | _ ->
              raise (Location.Error (
                  Location.error ~loc ("error with let%"^name)))
          with
          | Not_found ->
            raise (Location.Error (
                Location.error ~loc ("no let%"^name)))
        end
      (* Delegate to the default mapper. *)
      | x -> default_mapper.expr mapper x;
  }

let () = register "my_monads" (generate_mapper monad_mapping)