Skip to content

Commit 7a45b6a

Browse files
author
Rahul Kumar
committed
implement ppx for class type ffi
1 parent 4033a90 commit 7a45b6a

File tree

1 file changed

+207
-0
lines changed

1 file changed

+207
-0
lines changed

jscomp/tools/ppx_class.ml

+207
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
(* BuckleScript compiler
2+
* Copyright (C) 2015-2016 Bloomberg Finance L.P.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU Lesser General Public License as published by
6+
* the Free Software Foundation, with linking exception;
7+
* either version 2.1 of the License, or (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17+
*)
18+
19+
(* Author: Hongbo Zhang *)
20+
open Ast_mapper
21+
open Ast_helper
22+
open Asttypes
23+
open Parsetree
24+
open Longident
25+
26+
27+
(* Define types of indications supported by the FFI *)
28+
type indication =
29+
| Indication_read
30+
| Indication_write
31+
| Indication_unsafe
32+
33+
(* Define a comparator to sort indications with *)
34+
let compare_indication : indication -> indication -> int = compare
35+
36+
(* Given an attribute string, determine the indications *)
37+
let classify_indication name = match name with
38+
| "r" -> [Indication_read]
39+
| "w" -> [Indication_write]
40+
| "rw" -> [Indication_read;Indication_write]
41+
| "unsafe" -> [Indication_unsafe]
42+
| _ -> []
43+
44+
(* Get unique indications from an attribute list *)
45+
let indications_of_attr attrlst =
46+
List.sort_uniq compare_indication (List.concat
47+
(List.map (fun ({txt=x;_},_) -> classify_indication x) attrlst))
48+
49+
(* Define the methods generated by this ppx extension *)
50+
type methods =
51+
| Method_read
52+
| Method_read_unsafe
53+
| Method_write
54+
| Method_arity
55+
| Method_arity_unsafe
56+
57+
(* Define the formatter for printing errors *)
58+
let fmtr = Format.err_formatter
59+
60+
61+
(* Predicated print warning to stderr *)
62+
let pwarn loc predicate message =
63+
if predicate then
64+
begin
65+
let open Location in
66+
let () = Printf.fprintf stderr "\n%!" in
67+
let () = print_loc fmtr loc in
68+
Format.fprintf fmtr ": %s\n%!" message
69+
end
70+
else
71+
()
72+
73+
(* Define which methods get generated based on indications *)
74+
let methods_of_indications i loc =
75+
match i with
76+
| [] -> [Method_arity]
77+
| [Indication_read] -> [Method_read]
78+
| [Indication_read;
79+
Indication_unsafe] -> [Method_read_unsafe]
80+
| [Indication_read;
81+
Indication_write] -> [Method_read;Method_write]
82+
| [Indication_read;
83+
Indication_write;
84+
Indication_unsafe] -> [Method_read_unsafe;Method_write]
85+
| [Indication_write] -> [Method_write]
86+
| [Indication_unsafe] -> [Method_arity_unsafe]
87+
| _ -> begin
88+
pwarn loc true "Invalid combination of indications.";
89+
[]
90+
end
91+
92+
(* Filter out the attributes expanded by this ppx from an attribute list *)
93+
let filter_attributes attrlst = List.filter
94+
(fun ({txt=x;_},_) -> classify_indication x = []) attrlst
95+
96+
(* Filter out the attributes expanded by this ppx from a core_type*)
97+
let strip_attr t = {t with ptyp_attributes=filter_attributes t.ptyp_attributes}
98+
99+
(* Count the arity of a core_type *)
100+
let rec count_arity z = match z with
101+
| {ptyp_desc = Ptyp_arrow (_,_,y);_} -> 1 + (count_arity y)
102+
| _ -> 0
103+
104+
(* Determine whether a core_type is a non-nullary function *)
105+
let is_non_nullary_fn z = (count_arity z) > 0
106+
107+
(* Produce a new core_type from the given core_type, removing 'opt/option/def'
108+
from the return type*)
109+
let rec optless typ loc prefix = match typ with
110+
| {ptyp_desc = Ptyp_constr ({txt=(Lident ("opt"|"def"|"option")) |
111+
(Ldot (Lident "Js",("opt"|"def"|"option")));_},[x]);_} -> x
112+
| {ptyp_desc = Ptyp_arrow (tag,t1,t2);_} as t_ ->
113+
{t_ with ptyp_desc = Ptyp_arrow (tag,t1,optless t2 loc prefix)}
114+
| _ -> begin
115+
pwarn loc true (prefix^"indication provided for non-option type.");
116+
typ
117+
end
118+
119+
(* Generate method signature with appropriate warnings/checks *)
120+
let gen_method loc original abs vis name attr typ meth = match meth with
121+
| Method_read -> begin
122+
let () = pwarn loc (is_non_nullary_fn typ)
123+
"Read indication provided for non-property."
124+
in
125+
let t = strip_attr typ in
126+
{original with pctf_desc = Pctf_method (name^"__r",abs,vis,t)}
127+
end
128+
| Method_read_unsafe -> begin
129+
let () = pwarn loc (is_non_nullary_fn typ)
130+
"Read (unsafe) indication provided for non-property."
131+
in
132+
let t1 = optless typ loc "Read (unsafe) " in
133+
let t = strip_attr t1 in
134+
{original with pctf_desc = Pctf_method (name^"__r_unsafe",abs,vis,t)}
135+
end
136+
| Method_write -> begin
137+
let () = pwarn loc (is_non_nullary_fn typ)
138+
"Write indication provided for non-property."
139+
in
140+
let t = strip_attr typ in
141+
{original with pctf_desc = Pctf_method (name^"__w",abs,vis,t)}
142+
end
143+
| Method_arity -> begin
144+
let t = strip_attr typ in
145+
{original with pctf_desc = Pctf_method (
146+
name^"__"^(string_of_int (count_arity typ)),abs,vis,t)}
147+
end
148+
| Method_arity_unsafe -> begin
149+
let t1 = optless typ loc "Unsafe method " in
150+
let t = strip_attr t1 in
151+
{original with pctf_desc = Pctf_method (
152+
name^"__"^(string_of_int (count_arity typ))^"_unsafe",abs,vis,t)}
153+
end
154+
155+
(* Check that a string contains '__', without using the Str library *)
156+
let contains_undscr x =
157+
let cnt = ref 0 in
158+
let l = String.length x in
159+
let () = String.iteri (fun i c ->
160+
if c='_' then
161+
begin
162+
if (i < l-1) && (String.get x (i+1) = '_') then
163+
cnt := !cnt + 1
164+
else
165+
()
166+
end
167+
else
168+
()) x in
169+
(!cnt > 0)
170+
171+
(* Generate the FFI method signature given original method declaration *)
172+
let new_methods m =
173+
match m with
174+
| {pctf_desc = Pctf_method (name,_,_,_);_} as c
175+
when contains_undscr name -> [c]
176+
| {pctf_desc = Pctf_method (name,x,y,z);_} as c ->
177+
begin
178+
let loc = z.ptyp_loc in
179+
let attr = z.ptyp_attributes in
180+
let i = indications_of_attr attr in
181+
List.map (gen_method loc c x y name attr z)
182+
(methods_of_indications i loc)
183+
end
184+
| _ -> [m]
185+
186+
(* Map class_type_declaration for FFI *)
187+
let new_ctd mapper ctd = match ctd with
188+
| { pci_name=name;
189+
pci_expr =
190+
{ pcty_desc = Pcty_signature (
191+
{ pcsig_fields = methodlst;_ } as flds);
192+
_} as expr;
193+
pci_attributes = attrlst
194+
} as c ->
195+
{ c with pci_expr =
196+
{ expr with pcty_desc = Pcty_signature
197+
{ flds with pcsig_fields =
198+
(List.concat (List.map new_methods methodlst)) } };
199+
}
200+
| _ -> default_mapper.class_type_declaration mapper ctd
201+
202+
let ctd_mapper argv = {
203+
default_mapper with
204+
class_type_declaration = (fun mapper ctd -> new_ctd mapper ctd);
205+
}
206+
207+
let () = register "ppx_class" ctd_mapper

0 commit comments

Comments
 (0)