Skip to content

Commit 45ee7d6

Browse files
committed
start experimenting with extracting and rewriting embeds
1 parent fcdf7a9 commit 45ee7d6

File tree

8 files changed

+206
-0
lines changed

8 files changed

+206
-0
lines changed

jscomp/bsc/rescript_compiler_main.ml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,9 @@ let buckle_script_flags : (string * Bsc_args.spec * string) array =
264264
"-I", string_list_add Clflags.include_dirs ,
265265
"*internal* <dir> Add <dir> to the list of include directories" ;
266266

267+
"-embed", string_list_add Js_config.embeds ,
268+
"TODO: Explain." ;
269+
267270
"-w", string_call (Warnings.parse_options false),
268271
"<list> Enable or disable warnings according to <list>:\n\
269272
+<spec> enable warnings in <spec>\n\

jscomp/common/js_config.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ type jsx_mode = Classic | Automatic
3131
let no_version_header = ref false
3232

3333
let directives = ref []
34+
35+
let embeds = ref []
3436
let cross_module_inline = ref false
3537
let diagnose = ref false
3638

jscomp/common/js_config.mli

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ type jsx_mode = Classic | Automatic
3232
val no_version_header : bool ref
3333
(** set/get header *)
3434

35+
val embeds : string list ref
36+
(** embeds *)
37+
3538
val directives : string list ref
3639
(** directives printed verbatims just after the version header *)
3740

jscomp/core/js_embeds.ml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
let write_embeds ~extension_points ~output ast =
2+
let content = ref [] in
3+
let append item = content := item :: !content in
4+
let extension (iterator : Ast_iterator.iterator) (ext : Parsetree.extension) =
5+
(match ext with
6+
| ( {txt},
7+
PStr
8+
[
9+
{
10+
pstr_desc =
11+
Pstr_eval
12+
( {
13+
pexp_loc;
14+
pexp_desc = Pexp_constant (Pconst_string (contents, _));
15+
},
16+
_ );
17+
};
18+
] )
19+
when extension_points |> List.mem txt ->
20+
append (pexp_loc, txt, contents)
21+
| _ -> ());
22+
Ast_iterator.default_iterator.extension iterator ext
23+
in
24+
let iterator = {Ast_iterator.default_iterator with extension} in
25+
iterator.structure iterator ast;
26+
match !content with
27+
| [] -> ()
28+
| content ->
29+
let text =
30+
content
31+
|> List.map (fun (loc, extensionName, contents) ->
32+
Printf.sprintf "<<- item begin ->>\n%s\n%s\n%i:%i-%i:%i"
33+
extensionName contents loc.Location.loc_start.pos_lnum
34+
loc.loc_start.pos_cnum loc.loc_end.pos_lnum loc.loc_end.pos_cnum)
35+
|> List.rev |> String.concat "\n\n"
36+
in
37+
let oc = open_out_bin output in
38+
output_string oc text;
39+
close_out oc

jscomp/core/js_implementation.ml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,15 @@ let no_export (rest : Parsetree.structure) : Parsetree.structure =
127127
]
128128
| _ -> rest
129129

130+
let write_embeds outputprefix (ast : Parsetree.structure) =
131+
if !Clflags.only_parse = false && !Js_config.binary_ast then (
132+
match !Js_config.embeds with
133+
| [] -> ()
134+
| embeds -> Js_embeds.write_embeds ~extension_points:embeds
135+
~output:(outputprefix ^ Literals.suffix_embeds)
136+
ast);
137+
ast
138+
130139
let after_parsing_impl ppf outputprefix (ast : Parsetree.structure) =
131140
if !Clflags.only_parse = false then (
132141
Js_config.all_module_aliases :=
@@ -180,6 +189,7 @@ let implementation ~parser ppf ?outputprefix fname =
180189
in
181190
Res_compmisc.init_path ();
182191
parser fname
192+
|> write_embeds outputprefix
183193
|> Cmd_ppx_apply.apply_rewriters ~restore:false ~tool_name:Js_config.tool_name
184194
Ml
185195
|> Ppx_entry.rewrite_implementation

jscomp/ext/literals.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ let suffix_cmti = ".cmti"
115115

116116
let suffix_ast = ".ast"
117117

118+
let suffix_embeds = ".embeds"
119+
118120
let suffix_iast = ".iast"
119121

120122
let suffix_d = ".d"

jscomp/frontend/bs_builtin_ppx.ml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,11 @@ let signature_item_mapper (self : mapper) (sigi : Parsetree.signature_item) :
377377

378378
let structure_item_mapper (self : mapper) (str : Parsetree.structure_item) :
379379
Parsetree.structure_item =
380+
let str =
381+
match !Js_config.embeds with
382+
| [] -> str
383+
| _ -> Bs_embed_lang.structure_item str
384+
in
380385
match str.pstr_desc with
381386
| Pstr_type (rf, tdcls) (* [ {ptype_attributes} as tdcl ] *) ->
382387
Ast_tdcls.handle_tdcls_in_stru self str rf tdcls

jscomp/frontend/bs_embed_lang.ml

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
let should_transform name = !Js_config.embeds |> List.mem name
2+
3+
let extract_extension str =
4+
match String.split_on_char '.' str with
5+
| ["generated"; tag] -> Some (tag, None)
6+
| ["generated"; tag; fn_name] -> Some (tag, Some fn_name)
7+
| [tag] -> Some (tag, None)
8+
| [tag; fn_name] -> Some (tag, Some fn_name)
9+
| _ -> None
10+
11+
let transformed_count = Hashtbl.create 10
12+
13+
let increment_transformed_count (ext_name : string) =
14+
match Hashtbl.find_opt transformed_count ext_name with
15+
| None -> Hashtbl.add transformed_count ext_name 1
16+
| Some count -> Hashtbl.replace transformed_count ext_name (count + 1)
17+
18+
let get_transformed_count ext_name =
19+
match Hashtbl.find_opt transformed_count ext_name with
20+
| None -> 0
21+
| Some count -> count
22+
23+
type transformMode = LetBinding | ModuleBinding
24+
25+
let make_lident ?fn_name ~extension_name ~transform_mode filename =
26+
Longident.parse
27+
(Printf.sprintf "%s__%s.M%i%s"
28+
(if String.ends_with filename ~suffix:".res" then
29+
Filename.(chop_suffix (basename filename) ".res")
30+
else Filename.(chop_suffix (basename filename) ".resi"))
31+
extension_name
32+
(get_transformed_count extension_name)
33+
(match (transform_mode, fn_name) with
34+
| LetBinding, Some fn_name -> "." ^ fn_name
35+
| LetBinding, None -> ".default"
36+
| ModuleBinding, _ -> ""))
37+
38+
let transform_expr expr =
39+
match expr.Parsetree.pexp_desc with
40+
| Pexp_extension
41+
( {txt = ext_name},
42+
PStr
43+
[
44+
{
45+
pstr_desc =
46+
Pstr_eval ({pexp_desc = Pexp_constant (Pconst_string (_, _))}, _);
47+
};
48+
] )
49+
when should_transform ext_name -> (
50+
match extract_extension ext_name with
51+
| None -> expr
52+
| Some (extension_name, fn_name) ->
53+
increment_transformed_count extension_name;
54+
let loc = expr.pexp_loc in
55+
let filename = loc.loc_start.pos_fname in
56+
let lid =
57+
make_lident ?fn_name ~extension_name ~transform_mode:LetBinding filename
58+
in
59+
Ast_helper.Exp.ident ~loc {txt = lid; loc})
60+
| _ -> expr
61+
62+
let structure_item structure_item =
63+
match structure_item.Parsetree.pstr_desc with
64+
| Pstr_value
65+
( recFlag,
66+
[
67+
({
68+
pvb_expr =
69+
{pexp_desc = Pexp_extension ({txt = ext_name}, _)} as expr;
70+
} as valueBinding);
71+
] )
72+
when should_transform ext_name -> (
73+
match extract_extension ext_name with
74+
| None -> structure_item
75+
| Some _ ->
76+
{
77+
structure_item with
78+
pstr_desc =
79+
Pstr_value
80+
(recFlag, [{valueBinding with pvb_expr = transform_expr expr}]);
81+
})
82+
| Pstr_include
83+
({
84+
pincl_mod =
85+
{pmod_desc = Pmod_extension ({txt = ext_name; loc}, _)} as pmod;
86+
} as pincl)
87+
when ext_name |> should_transform -> (
88+
match extract_extension ext_name with
89+
| None -> structure_item
90+
| Some (extension_name, _fn_name) ->
91+
increment_transformed_count extension_name;
92+
{
93+
structure_item with
94+
pstr_desc =
95+
Pstr_include
96+
{
97+
pincl with
98+
pincl_mod =
99+
{
100+
pmod with
101+
pmod_desc =
102+
Pmod_ident
103+
{
104+
txt =
105+
make_lident loc.loc_start.pos_fname ~extension_name
106+
~transform_mode:ModuleBinding;
107+
loc;
108+
};
109+
};
110+
};
111+
})
112+
| Pstr_module
113+
({
114+
pmb_expr =
115+
{pmod_desc = Pmod_extension ({txt = ext_name; loc}, _)} as pmod;
116+
} as pmb)
117+
when ext_name |> should_transform -> (
118+
match extract_extension ext_name with
119+
| None -> structure_item
120+
| Some (extension_name, _fn_name) ->
121+
increment_transformed_count extension_name;
122+
{
123+
structure_item with
124+
pstr_desc =
125+
Pstr_module
126+
{
127+
pmb with
128+
pmb_expr =
129+
{
130+
pmod with
131+
pmod_desc =
132+
Pmod_ident
133+
{
134+
txt =
135+
make_lident loc.loc_start.pos_fname ~extension_name
136+
~transform_mode:ModuleBinding;
137+
loc;
138+
};
139+
};
140+
};
141+
})
142+
| _ -> structure_item

0 commit comments

Comments
 (0)