Single point of configuration to add many let%foo directives

This commit is contained in:
Georges Dupéron 2019-04-19 11:59:32 +02:00
parent 2df862bdbf
commit abe8126151
4 changed files with 46 additions and 56 deletions

View File

@ -12,34 +12,14 @@ module List = struct
;; ;;
end end
module Extension_name = struct
type t =
| Bind
| Bind_open
| Map
| Map_open
let operator_name = function
| Bind | Bind_open -> "bind"
| Map | Map_open -> "map"
;;
let to_string = function
| Bind -> "bind"
| Bind_open -> "bind_open"
| Map -> "map"
| Map_open -> "map_open"
;;
end
let let_syntax ~modul : Longident.t = let let_syntax ~modul : Longident.t =
match modul with match modul with
| None -> Lident "Let_syntax" | None -> Lident "Let_syntax"
| Some id -> Ldot (id.txt, "Let_syntax") | Some id -> Ldot (id.txt, "Let_syntax")
;; ;;
let open_on_rhs ~loc ~modul = let open_on_rhs ~loc ~modul ~extension_name_s =
Located.mk ~loc (Longident.Ldot (let_syntax ~modul, "Open_on_rhs")) Located.mk ~loc (Longident.Ldot (let_syntax ~modul, "Open_on_rhs_" ^ extension_name_s))
;; ;;
let eoperator ~loc ~modul func = let eoperator ~loc ~modul func =
@ -65,21 +45,20 @@ let expand_with_tmp_vars ~loc bindings expr ~f =
pexp_let ~loc Nonrecursive s_lhs_tmp_var (f ~loc s_rhs_tmp_var expr) pexp_let ~loc Nonrecursive s_lhs_tmp_var (f ~loc s_rhs_tmp_var expr)
;; ;;
let bind_apply ~loc ~modul extension_name ~arg ~fn = let bind_apply ~loc ~modul extension_name_s ~arg ~fn =
pexp_apply pexp_apply
~loc ~loc
(eoperator ~loc ~modul (Extension_name.operator_name extension_name)) (eoperator ~loc ~modul extension_name_s)
[ Nolabel, arg; Labelled "f", fn ] [ Nolabel, arg; Labelled "f", fn ]
;; ;;
let maybe_open extension_name ~to_open:module_to_open expr = (* Change by Georges: Always open for all extension names. *)
let maybe_open ~to_open:module_to_open expr =
let loc = expr.pexp_loc in let loc = expr.pexp_loc in
match (extension_name : Extension_name.t) with pexp_open ~loc Override (module_to_open ~loc) expr
| Bind | Map -> expr
| Bind_open | Map_open -> pexp_open ~loc Override (module_to_open ~loc) expr
;; ;;
let expand_let extension_name ~loc ~modul bindings body = let expand_let extension_name_s ~loc ~modul bindings body =
if List.is_empty bindings if List.is_empty bindings
then invalid_arg "expand_let: list of bindings must be non-empty"; then invalid_arg "expand_let: list of bindings must be non-empty";
(* Build expression [both E1 (both E2 (both ...))] *) (* Build expression [both E1 (both E2 (both ...))] *)
@ -99,17 +78,17 @@ let expand_let extension_name ~loc ~modul bindings body =
bind_apply bind_apply
~loc ~loc
~modul ~modul
extension_name extension_name_s
~arg:nested_boths ~arg:nested_boths
~fn:(pexp_fun ~loc Nolabel None nested_patterns body) ~fn:(pexp_fun ~loc Nolabel None nested_patterns body)
;; ;;
let expand_match extension_name ~loc ~modul expr cases = let expand_match extension_name_s ~loc ~modul expr cases =
bind_apply bind_apply
~loc ~loc
~modul ~modul
extension_name extension_name_s
~arg:(maybe_open extension_name ~to_open:(open_on_rhs ~modul) expr) ~arg:(maybe_open ~to_open:(open_on_rhs ~modul ~extension_name_s) expr)
~fn:(pexp_function ~loc cases) ~fn:(pexp_function ~loc cases)
;; ;;
@ -123,7 +102,7 @@ let expand_if extension_name ~loc expr then_ else_ =
] ]
;; ;;
let expand ~modul extension_name expr = let expand ~modul extension_name_s expr =
let loc = expr.pexp_loc in let loc = expr.pexp_loc in
let expansion = let expansion =
match expr.pexp_desc with match expr.pexp_desc with
@ -145,16 +124,16 @@ let expand ~modul extension_name expr =
{ vb with { vb with
pvb_pat pvb_pat
; pvb_expr = ; pvb_expr =
maybe_open extension_name ~to_open:(open_on_rhs ~modul) vb.pvb_expr maybe_open ~to_open:(open_on_rhs ~modul ~extension_name_s) vb.pvb_expr
}) })
in in
expand_with_tmp_vars ~loc bindings expr ~f:(expand_let extension_name ~modul) expand_with_tmp_vars ~loc bindings expr ~f:(expand_let extension_name_s ~modul)
| Pexp_let (Recursive, _, _) -> | Pexp_let (Recursive, _, _) ->
Location.raise_errorf Location.raise_errorf
~loc ~loc
"'let%%%s' may not be recursive" "'let%%%s' may not be recursive"
(Extension_name.to_string extension_name) extension_name_s
| Pexp_match (expr, cases) -> expand_match extension_name ~loc ~modul expr cases | Pexp_match (expr, cases) -> expand_match extension_name_s ~loc ~modul expr cases
| Pexp_ifthenelse (expr, then_, else_) -> | Pexp_ifthenelse (expr, then_, else_) ->
let else_ = let else_ =
match else_ with match else_ with
@ -163,14 +142,14 @@ let expand ~modul extension_name expr =
Location.raise_errorf Location.raise_errorf
~loc ~loc
"'if%%%s' must include an else branch" "'if%%%s' must include an else branch"
(Extension_name.to_string extension_name) extension_name_s
in in
expand_if extension_name ~loc ~modul expr then_ else_ expand_if extension_name_s ~loc ~modul expr then_ else_
| _ -> | _ ->
Location.raise_errorf Location.raise_errorf
~loc ~loc
"'%%%s' can only be used with 'let', 'match', and 'if'" "'%%%s' can only be used with 'let', 'match', and 'if'"
(Extension_name.to_string extension_name) extension_name_s
in in
{ expansion with pexp_attributes = expr.pexp_attributes @ expansion.pexp_attributes } { expansion with pexp_attributes = expr.pexp_attributes @ expansion.pexp_attributes }
;; ;;

View File

@ -1,13 +1,3 @@
open Ppxlib open Ppxlib
module Extension_name : sig val expand : modul:longident loc option -> string -> expression -> expression
type t =
| Bind
| Bind_open
| Map
| Map_open
val to_string : t -> string
end
val expand : modul:longident loc option -> Extension_name.t -> expression -> expression

View File

@ -1,16 +1,19 @@
open Ppxlib open Ppxlib
let ext extension_name = let ext extension_name_s =
Extension.declare_with_path_arg Extension.declare_with_path_arg
(Ppx_let_expander.Extension_name.to_string extension_name) extension_name_s
Extension.Context.expression Extension.Context.expression
Ast_pattern.(single_expr_payload __) Ast_pattern.(single_expr_payload __)
(fun ~loc:_ ~path:_ ~arg expr -> (fun ~loc:_ ~path:_ ~arg expr ->
Ppx_let_expander.expand extension_name ~modul:arg expr) Ppx_let_expander.expand extension_name_s ~modul:arg expr)
;; ;;
let () = let () =
Driver.register_transformation Driver.register_transformation
"let" "let"
~extensions:[ ext Bind; ext Bind_open; ext Map; ext Map_open ] ~extensions:(List.map ext [
"bind";
"xxx";
])
;; ;;

View File

@ -1,3 +1,20 @@
module Monad_example = struct
module Let_syntax = struct
let bind x ~f = f x
module Open_on_rhs_bind = struct
let return _ = "foo"
end
end
let _mf a =
let%bind xyz = return a in
(int_of_string xyz + 1)
;;
end
(* TODO: re-enable some tests *)
(*
module Monad_example = struct module Monad_example = struct
module X : sig module X : sig
type 'a t type 'a t
@ -169,3 +186,4 @@ module Example_without_open = struct
x + 1 x + 1
;; ;;
end end
*)