Skip to content

Commit 60c1e27

Browse files
author
Hongbo Zhang
committed
[feature] improve ppx to handle arbitrary arity -- design: should we fall back to built-in when the arity is low?
1 parent 746024e commit 60c1e27

File tree

16 files changed

+349
-270
lines changed

16 files changed

+349
-270
lines changed

jscomp/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ install:
9595
cp ./bin/bsc ../bin/
9696
cp ./runtime/*.cmt* ../lib/ocaml/
9797
cp ./runtime/*.cmj* ../lib/ocaml/
98-
cp ./runtime/js.cmi ./runtime/fn.cmi ../lib/ocaml/
98+
cp ./runtime/js.cmi ../lib/ocaml/
9999
cp ./stdlib/*.cm* ../lib/ocaml/
100100

101101
TMP_OCAMLLIB=$(shell ocamlopt.opt -where)

jscomp/ext_list.ml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,3 +264,24 @@ let split_map
264264

265265
aux [] [] xs
266266

267+
268+
(*
269+
{[
270+
reduce_from_right (-) [1;2;3];;
271+
- : int = 2
272+
# reduce_from_right (-) [1;2;3; 4];;
273+
- : int = -2
274+
# reduce_from_right (-) [1];;
275+
- : int = 1
276+
# reduce_from_right (-) [1;2;3; 4; 5];;
277+
- : int = 3
278+
]}
279+
*)
280+
let reduce_from_right fn lst =
281+
begin match List.rev lst with
282+
| last :: rest ->
283+
List.fold_left (fun x y -> fn y x) last rest
284+
| _ -> invalid_arg "Ext_list.reduce"
285+
end
286+
287+

jscomp/ext_list.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,5 @@ val find_opt : ('a -> 'b option) -> 'a list -> 'b option
8888

8989
(** [f] is applied follow the list order *)
9090
val split_map : ('a -> 'b * 'c) -> 'a list -> 'b list * 'c list
91+
92+
val reduce_from_right : ('a -> 'a -> 'a) -> 'a list -> 'a

jscomp/ppx_entry.ml

Lines changed: 176 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ let predef_val_unit =
6666
let prim = "js_pure_expr"
6767
let prim_stmt = "js_pure_stmt"
6868
let prim_debugger = "js_debugger"
69-
69+
let curry_type_id = Longident.Ldot (Lident "Js", "fn")
70+
let ignore_id = Longident.Ldot (Lident "Pervasives", "ignore")
71+
let js_unsafe_downgrade_id = Longident.Ldot (Ldot (Lident "Js", "Unsafe"), "!")
7072
(* note we first declare its type is [unit],
7173
then [ignore] it, [ignore] is necessary since
7274
the js value maybe not be of type [unit] and
@@ -77,7 +79,7 @@ let discard_js_value loc e : Parsetree.expression =
7779
{pexp_desc =
7880
Pexp_apply
7981
({pexp_desc =
80-
Pexp_ident {txt = Ldot (Lident "Pervasives", "ignore") ; loc};
82+
Pexp_ident {txt = ignore_id ; loc};
8183
pexp_attributes = [];
8284
pexp_loc = loc},
8385
[("",
@@ -95,6 +97,135 @@ let discard_js_value loc e : Parsetree.expression =
9597
}
9698

9799

100+
let gen_fn_run loc arity args : Parsetree.expression_desc =
101+
let open Parsetree in
102+
let ptyp_attributes = [] in
103+
let local_module_name = "Tmp" in
104+
let local_fun_name = "run" in
105+
let pval_prim = Printf.sprintf "js_fn_run_%02d" arity in
106+
let tyvars =
107+
(Ext_list.init (arity + 1) (fun i ->
108+
{ptyp_desc = Ptyp_var ("a" ^ string_of_int i);
109+
ptyp_attributes ;
110+
ptyp_loc = loc})) in
111+
let tuple_type_desc =
112+
if arity = 0 then
113+
(List.hd tyvars).ptyp_desc
114+
(* avoid single tuple *)
115+
else
116+
Parsetree.Ptyp_tuple tyvars
117+
in
118+
let uncurry_fn =
119+
{ptyp_desc =
120+
Ptyp_constr ({txt = curry_type_id; loc},
121+
[{ptyp_desc = tuple_type_desc ;
122+
ptyp_attributes;
123+
ptyp_loc = loc }]);
124+
ptyp_attributes;
125+
ptyp_loc = loc} in
126+
let arrow a b =
127+
{ptyp_desc =
128+
Ptyp_arrow ("", a, b);
129+
ptyp_attributes ;
130+
ptyp_loc = loc} in
131+
(** could be optimized *)
132+
let pval_type =
133+
Ext_list.reduce_from_right arrow (uncurry_fn :: tyvars) in
134+
Pexp_letmodule
135+
({txt = local_module_name; loc},
136+
{pmod_desc =
137+
Pmod_structure
138+
[{pstr_desc =
139+
Pstr_primitive
140+
{pval_name = {txt = local_fun_name; loc};
141+
pval_type ;
142+
pval_loc = loc;
143+
pval_prim = [pval_prim];
144+
pval_attributes = []};
145+
pstr_loc = loc;
146+
}];
147+
pmod_loc = loc;
148+
pmod_attributes = []},
149+
{
150+
pexp_desc =
151+
Pexp_apply
152+
(({pexp_desc = Pexp_ident {txt = Ldot (Lident local_module_name, local_fun_name);
153+
loc};
154+
pexp_attributes = [] ;
155+
pexp_loc = loc} : Parsetree.expression),
156+
args);
157+
pexp_attributes = [];
158+
pexp_loc = loc
159+
})
160+
161+
let gen_fn_mk loc arity args : Parsetree.expression_desc =
162+
let open Parsetree in
163+
let ptyp_attributes = [] in
164+
let local_module_name = "Tmp" in
165+
let local_fun_name = "mk" in
166+
let pval_prim = Printf.sprintf "js_fn_mk_%02d" arity in
167+
let tyvars =
168+
(Ext_list.init (arity + 1) (fun i ->
169+
{ptyp_desc = Ptyp_var ("a" ^ string_of_int i);
170+
ptyp_attributes ;
171+
ptyp_loc = loc})) in
172+
let tuple_type_desc =
173+
if arity = 0 then
174+
(List.hd tyvars).ptyp_desc
175+
(* avoid single tuple *)
176+
else
177+
Parsetree.Ptyp_tuple tyvars
178+
in
179+
let uncurry_fn =
180+
{ptyp_desc =
181+
Ptyp_constr ({txt = curry_type_id; loc},
182+
[{ptyp_desc = tuple_type_desc ;
183+
ptyp_attributes;
184+
ptyp_loc = loc }]);
185+
ptyp_attributes;
186+
ptyp_loc = loc} in
187+
let arrow a b =
188+
{ptyp_desc =
189+
Ptyp_arrow ("", a, b);
190+
ptyp_attributes ;
191+
ptyp_loc = loc} in
192+
(** could be optimized *)
193+
let pval_type =
194+
if arity = 0 then
195+
arrow (arrow predef_unit_type (List.hd tyvars) ) uncurry_fn
196+
else
197+
arrow (Ext_list.reduce_from_right arrow tyvars) uncurry_fn in
198+
199+
Pexp_letmodule
200+
({txt = local_module_name; loc},
201+
{pmod_desc =
202+
Pmod_structure
203+
[{pstr_desc =
204+
Pstr_primitive
205+
{pval_name = {txt = local_fun_name; loc};
206+
pval_type ;
207+
pval_loc = loc;
208+
pval_prim = [pval_prim];
209+
pval_attributes = []};
210+
pstr_loc = loc;
211+
}];
212+
pmod_loc = loc;
213+
pmod_attributes = []},
214+
{
215+
pexp_desc =
216+
Pexp_apply
217+
(({pexp_desc = Pexp_ident {txt = Ldot (Lident local_module_name, local_fun_name);
218+
loc};
219+
pexp_attributes = [] ;
220+
pexp_loc = loc} : Parsetree.expression),
221+
args);
222+
pexp_attributes = [];
223+
pexp_loc = loc
224+
})
225+
226+
227+
228+
98229
let handle_raw ?ty loc e attrs =
99230
let attrs =
100231
match ty with
@@ -151,7 +282,7 @@ let uncurry_fn_type loc ty ptyp_attributes
151282
ptyp_attributes }
152283
in
153284
{ ty with ptyp_desc =
154-
Ptyp_constr ({txt = Ldot (Lident "Fn", "t") ; loc},
285+
Ptyp_constr ({txt = curry_type_id ; loc},
155286
[ fn_type]);
156287
ptyp_attributes = []
157288
}
@@ -246,44 +377,26 @@ let handle_uncurry_generation loc
246377
| v -> [v]
247378
in
248379
let len = List.length args in
249-
let mk = "mk" ^ string_of_int len in
250380
let body = mapper.expr mapper body in
251-
begin match args with
252-
| [] ->
253-
{e with pexp_desc =
254-
Pexp_apply (
255-
{pexp_desc = Pexp_ident {txt = Ldot (Lident "Fn", mk); loc};
256-
pexp_loc = loc;
257-
pexp_attributes = []
258-
},
259-
[("",
260-
{pexp_desc =
261-
Pexp_fun ("", None,
262-
{ppat_desc =
263-
Ppat_construct ({txt = Lident "()"; loc}, None);
264-
ppat_loc = loc ;
265-
ppat_attributes = []},
266-
body);
267-
pexp_loc = loc ;
268-
pexp_attributes = []})])}
269-
| _ ->
270-
let fun_ =
271-
List.fold_right (fun arg body ->
272-
let arg = mapper.pat mapper arg in
273-
{Parsetree.
274-
pexp_loc = loc ;
275-
pexp_desc = Pexp_fun ("", None, arg, body);
276-
pexp_attributes = []}) args body in
277-
{ e with
278-
pexp_desc =
279-
Pexp_apply ({pexp_desc = Pexp_ident {txt = Ldot (Lident "Fn", mk); loc};
280-
pexp_loc = loc ;
281-
pexp_attributes = []},
282-
[("",
283-
fun_)])
284-
}
285-
end
286-
381+
let fun_ =
382+
if len = 0 then
383+
{Parsetree.pexp_desc =
384+
Pexp_fun ("", None,
385+
{ppat_desc =
386+
Ppat_construct ({txt = Lident "()"; loc}, None);
387+
ppat_loc = loc ;
388+
ppat_attributes = []},
389+
body);
390+
pexp_loc = loc ;
391+
pexp_attributes = []}
392+
else
393+
List.fold_right (fun arg body ->
394+
let arg = mapper.pat mapper arg in
395+
{Parsetree.
396+
pexp_loc = loc ;
397+
pexp_desc = Pexp_fun ("", None, arg, body);
398+
pexp_attributes = []}) args body in
399+
{e with pexp_desc = gen_fn_mk loc len [("", fun_)]}
287400
let handle_uncurry_application
288401
loc fn (pat : Parsetree.expression) (e : Parsetree.expression)
289402
(self : Ast_mapper.mapper)
@@ -302,18 +415,7 @@ let handle_uncurry_application
302415
let fn = self.expr self fn in
303416
let args = List.map (self.expr self) args in
304417
let len = List.length args in
305-
let run = "run" ^ string_of_int len in
306-
{ e with
307-
Parsetree.pexp_desc =
308-
Pexp_apply (
309-
{pexp_desc =
310-
Pexp_ident {txt = Ldot (Lident "Fn", run) ;
311-
loc ; };
312-
pexp_loc = loc ;
313-
pexp_attributes = []
314-
},
315-
(("", fn) :: List.map (fun x -> "", x) args))
316-
}
418+
{ e with pexp_desc = gen_fn_run loc len (("", fn) :: List.map (fun x -> "", x) args)}
317419

318420
let handle_obj_property loc obj name e
319421
(mapper : Ast_mapper.mapper) : Parsetree.expression =
@@ -324,7 +426,7 @@ let handle_obj_property loc obj name e
324426
({pexp_desc =
325427
Pexp_apply
326428
({pexp_desc =
327-
Pexp_ident {txt = Ldot (Ldot (Lident "Js", "Unsafe"), "!");
429+
Pexp_ident {txt = js_unsafe_downgrade_id;
328430
loc};
329431
pexp_loc = loc;
330432
pexp_attributes = []},
@@ -370,39 +472,27 @@ let handle_obj_method loc (obj : Parsetree.expression)
370472
let len = List.length args in
371473
let obj = mapper.expr mapper obj in
372474
let args = List.map (mapper.expr mapper ) args in
373-
(* TODO: in the future, dynamically create the c externs,
374-
so it can handle arbitrary large number
375-
*)
376-
let run = "run" ^ string_of_int len in
377-
{ e with
378-
pexp_desc =
379-
Pexp_apply (
380-
{pexp_desc =
381-
Pexp_ident {txt = Ldot (Lident "Fn", run) ;
382-
loc ; };
383-
pexp_loc = loc ;
384-
pexp_attributes = []
385-
},
386-
(("",
387-
{pexp_desc =
388-
Pexp_send
389-
({pexp_desc =
390-
Pexp_apply
391-
({pexp_desc =
392-
Pexp_ident {
393-
txt = Ldot (Ldot (Lident "Js", "Unsafe"), "!");
394-
loc };
395-
pexp_loc = loc ;
396-
pexp_attributes = []},
397-
[("", obj)]);
398-
pexp_loc = loc ;
399-
pexp_attributes = []},
400-
name);
401-
pexp_loc = loc ;
402-
pexp_attributes = [] }) ::
403-
List.map (fun x -> "", x) args
404-
))
405-
}
475+
476+
{e with pexp_desc = gen_fn_run loc len
477+
(("",
478+
{pexp_desc =
479+
Pexp_send
480+
({pexp_desc =
481+
Pexp_apply
482+
({pexp_desc =
483+
Pexp_ident {
484+
txt = js_unsafe_downgrade_id;
485+
loc };
486+
pexp_loc = loc ;
487+
pexp_attributes = []},
488+
[("", obj)]);
489+
pexp_loc = loc ;
490+
pexp_attributes = []},
491+
name);
492+
pexp_loc = loc ;
493+
pexp_attributes = [] }) ::
494+
List.map (fun x -> "", x) args
495+
)}
406496
(** TODO:
407497
More syntax sanity check for [case__set]
408498
case__set: arity 2

jscomp/runtime/.runtimedepend

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,6 @@ caml_weak.cmj : js.cmj caml_array.cmi caml_weak.cmi
7474
caml_weak.cmx : js.cmx caml_array.cmx caml_weak.cmi
7575
curry.cmj : js.cmj caml_oo.cmi
7676
curry.cmx : js.cmx caml_oo.cmx
77-
fn.cmj :
78-
fn.cmx :
7977
js.cmj :
8078
js.cmx :
8179
js_primitive.cmj : js.cmj js_primitive.cmi
@@ -134,8 +132,6 @@ caml_weak.cmo : js.cmo caml_array.cmi caml_weak.cmi
134132
caml_weak.cmj : js.cmj caml_array.cmj caml_weak.cmi
135133
curry.cmo : js.cmo caml_oo.cmi
136134
curry.cmj : js.cmj caml_oo.cmj
137-
fn.cmo :
138-
fn.cmj :
139135
js.cmo :
140136
js.cmj :
141137
js_primitive.cmo : js.cmo js_primitive.cmi

jscomp/runtime/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ OTHERS= caml_array caml_string \
1010
caml_backtrace caml_int32 caml_gc typed_array \
1111
js_primitive caml_basic caml_oo curry
1212

13-
SOURCE_LIST= $(OTHERS) caml_builtin_exceptions block js fn
13+
SOURCE_LIST= $(OTHERS) caml_builtin_exceptions block js
1414

1515
caml_oo.cmj : caml_array.cmj
1616
caml_format.cmj caml_io.cmj: curry.cmj
1717

1818

19-
$(addsuffix .cmj, $(OTHERS)): caml_builtin_exceptions.cmj block.cmj js.cmj fn.cmj
19+
$(addsuffix .cmj, $(OTHERS)): caml_builtin_exceptions.cmj block.cmj js.cmj
2020

2121
RUNTIME := $(addsuffix .cmj, $(SOURCE_LIST))
2222

0 commit comments

Comments
 (0)