Skip to content

Commit 6fa3289

Browse files
authored
support tagged templates (#6250)
* support tagged template in rescript and allow to bind to JS tagged templates using @taggedTemplate annotation * Set version to 11.1.0 * add tagged templates to changelog This work was originally based on kevinbarabash#2
1 parent 9f81707 commit 6fa3289

29 files changed

+390
-63
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010
> - :house: [Internal]
1111
> - :nail_care: [Polish]
1212
13-
# 11.0.2 (Unreleased)
13+
# 11.1.0 (Unreleased)
14+
15+
#### :rocket: New Feature
16+
17+
- experimental support of tagged template literals, eg ```sql`select * from ${table}```. https://github.com/rescript-lang/rescript-compiler/pull/6250
1418

1519
# 11.0.1
1620

jscomp/common/bs_version.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@
2121
* You should have received a copy of the GNU Lesser General Public License
2222
* along with this program; if not, write to the Free Software
2323
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
24-
let version = "11.0.2"
24+
let version = "11.1.0"
2525
let header = "// Generated by ReScript, PLEASE EDIT WITH CARE"
2626
let package_name = ref "rescript"

jscomp/core/j.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ and expression_desc =
115115
This can be constructed either in a static way [E.array_index_by_int] or a dynamic way
116116
[E.array_index]
117117
*)
118+
| Tagged_template of expression * expression list * expression list
118119
| Static_index of expression * string * int32 option
119120
(* The third argument bool indicates whether we should
120121
print it as

jscomp/core/js_analyzer.ml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ let rec no_side_effect_expression_desc (x : J.expression_desc) =
103103
| String_append (a, b) | Seq (a, b) -> no_side_effect a && no_side_effect b
104104
| Length (e, _) | Caml_block_tag (e, _) | Typeof e -> no_side_effect e
105105
| Bin (op, a, b) -> op <> Eq && no_side_effect a && no_side_effect b
106+
| Tagged_template (call_expr, strings, values) -> no_side_effect call_expr &&
107+
Ext_list.for_all strings no_side_effect && Ext_list.for_all values no_side_effect
106108
| Js_not _ | Cond _ | FlatCall _ | Call _ | New _ | Raw_js_code _
107109
(* actually true? *) ->
108110
false
@@ -204,7 +206,7 @@ let rec eq_expression ({ expression_desc = x0 } : J.expression)
204206
| _ -> false)
205207
| Length _ | Is_null_or_undefined _ | String_append _ | Typeof _ | Js_not _
206208
| Cond _ | FlatCall _ | New _ | Fun _ | Raw_js_code _ | Array _
207-
| Caml_block_tag _ | Object _
209+
| Caml_block_tag _ | Object _ | Tagged_template _
208210
| Number (Uint _) ->
209211
false
210212
| Await _ -> false

jscomp/core/js_dump.ml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ let exp_need_paren (e : J.expression) =
165165
| Js_not _ | Bool _ | New _ ->
166166
false
167167
| Await _ -> false
168+
| Tagged_template _ -> false
168169

169170
let comma_idents (cxt : cxt) f ls = iter_lst cxt f ls Ext_pp_scope.ident comma
170171

@@ -596,6 +597,24 @@ and expression_desc cxt ~(level : int) f x : cxt =
596597
P.string f L.null;
597598
comma_sp f;
598599
expression ~level:1 cxt f el))
600+
| Tagged_template (callExpr, stringArgs, valueArgs) ->
601+
let cxt = expression cxt ~level f callExpr in
602+
P.string f "`";
603+
let rec aux cxt xs ys = match xs, ys with
604+
| [], [] -> ()
605+
| [{J.expression_desc = Str { txt; _ }}], [] ->
606+
P.string f txt
607+
| {J.expression_desc = Str { txt; _ }} :: x_rest, y :: y_rest ->
608+
P.string f txt;
609+
P.string f "${";
610+
let cxt = expression cxt ~level f y in
611+
P.string f "}";
612+
aux cxt x_rest y_rest
613+
| _ -> assert false
614+
in
615+
aux cxt stringArgs valueArgs;
616+
P.string f "`";
617+
cxt
599618
| String_index (a, b) ->
600619
P.group f 1 (fun _ ->
601620
let cxt = expression ~level:15 cxt f a in

jscomp/core/js_exp_make.ml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ let call ?comment ~info e0 args : t =
7272
let flat_call ?comment e0 es : t =
7373
{ expression_desc = FlatCall (e0, es); comment }
7474

75+
let tagged_template ?comment callExpr stringArgs valueArgs : t =
76+
{ expression_desc = Tagged_template (callExpr, stringArgs, valueArgs); comment }
77+
7578
let runtime_var_dot ?comment (x : string) (e1 : string) : J.expression =
7679
{
7780
expression_desc =

jscomp/core/js_exp_make.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@ val call : ?comment:string -> info:Js_call_info.t -> t -> t list -> t
279279

280280
val flat_call : ?comment:string -> t -> t -> t
281281

282+
val tagged_template : ?comment:string -> t -> t list -> t list -> t
283+
282284
val new_ : ?comment:string -> J.expression -> J.expression list -> t
283285

284286
val array : ?comment:string -> J.mutable_flag -> J.expression list -> t

jscomp/core/js_fold.ml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ class fold =
124124
let _self = _self#expression _x0 in
125125
let _self = list (fun _self -> _self#expression) _self _x1 in
126126
_self
127+
| Tagged_template (_x0, _x1, _x2) ->
128+
let _self = _self#expression _x0 in
129+
let _self = list (fun _self -> _self#expression) _self _x1 in
130+
let _self = list (fun _self -> _self#expression) _self _x2 in
131+
_self
127132
| String_index (_x0, _x1) ->
128133
let _self = _self#expression _x0 in
129134
let _self = _self#expression _x1 in

jscomp/core/js_record_fold.ml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ let expression_desc : 'a. ('a, expression_desc) fn =
130130
let st = _self.expression _self st _x0 in
131131
let st = list _self.expression _self st _x1 in
132132
st
133+
| Tagged_template (_xo, _x1, _x2) ->
134+
let st = _self.expression _self st _xo in
135+
let st = list _self.expression _self st _x1 in
136+
let st = list _self.expression _self st _x2 in
137+
st
133138
| String_index (_x0, _x1) ->
134139
let st = _self.expression _self st _x0 in
135140
let st = _self.expression _self st _x1 in

jscomp/core/js_record_iter.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ let expression_desc : expression_desc fn =
107107
| Call (_x0, _x1, _x2) ->
108108
_self.expression _self _x0;
109109
list _self.expression _self _x1
110+
| Tagged_template (_x0, _x1, _x2) ->
111+
_self.expression _self _x0;
112+
list _self.expression _self _x1;
113+
list _self.expression _self _x2
110114
| String_index (_x0, _x1) ->
111115
_self.expression _self _x0;
112116
_self.expression _self _x1

jscomp/core/js_record_map.ml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ let expression_desc : expression_desc fn =
130130
let _x0 = _self.expression _self _x0 in
131131
let _x1 = list _self.expression _self _x1 in
132132
Call (_x0, _x1, _x2)
133+
| Tagged_template (_x0, _x1, _x2) ->
134+
let _x0 = _self.expression _self _x0 in
135+
let _x1 = list _self.expression _self _x1 in
136+
let _x2 = list _self.expression _self _x2 in
137+
Tagged_template (_x0, _x1, _x2)
133138
| String_index (_x0, _x1) ->
134139
let _x0 = _self.expression _self _x0 in
135140
let _x1 = _self.expression _self _x1 in

jscomp/core/lam_compile_external_call.ml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,17 @@ let translate_scoped_access scopes obj =
252252
let translate_ffi (cxt : Lam_compile_context.t) arg_types
253253
(ffi : External_ffi_types.external_spec) (args : J.expression list) =
254254
match ffi with
255-
| Js_call { external_module_name = module_name; name = fn; splice; scopes } ->
255+
| Js_call { external_module_name; name; splice; scopes; tagged_template = true } ->
256+
let fn = translate_scoped_module_val external_module_name name scopes in
257+
(match args with
258+
| [ stringArgs; valueArgs ] -> (
259+
match (stringArgs, valueArgs) with
260+
| ({expression_desc = Array (strings, _); _}, {expression_desc = Array (values, _); _}) ->
261+
E.tagged_template fn strings values
262+
| _ -> assert false
263+
)
264+
| _ -> assert false)
265+
| Js_call { external_module_name = module_name; name = fn; splice; scopes; tagged_template = false } ->
256266
let fn = translate_scoped_module_val module_name fn scopes in
257267
if splice then
258268
let args, eff, dynamic = assemble_args_has_splice arg_types args in

jscomp/frontend/ast_external_process.ml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ type external_desc = {
184184
get_name: bundle_source option;
185185
mk_obj: bool;
186186
return_wrapper: External_ffi_types.return_wrapper;
187+
tagged_template: bool;
187188
}
188189

189190
let init_st =
@@ -202,6 +203,7 @@ let init_st =
202203
get_name = None;
203204
mk_obj = false;
204205
return_wrapper = Return_unset;
206+
tagged_template = false;
205207
}
206208

207209
let return_wrapper loc (txt : string) : External_ffi_types.return_wrapper =
@@ -291,6 +293,7 @@ let parse_external_attributes (no_arguments : bool) (prim_name_check : string)
291293
between unset/set
292294
*)
293295
| scopes -> {st with scopes})
296+
| "taggedTemplate" -> {st with splice = true; tagged_template = true}
294297
| "bs.splice" | "bs.variadic" | "variadic" -> {st with splice = true}
295298
| "bs.send" | "send" ->
296299
{st with val_send = Some (name_from_payload_or_prim ~loc payload)}
@@ -366,6 +369,7 @@ let process_obj (loc : Location.t) (st : external_desc) (prim_name : string)
366369
get_name = None;
367370
get_index = false;
368371
return_wrapper = Return_unset;
372+
tagged_template = _;
369373
set_index = false;
370374
mk_obj = _;
371375
scopes =
@@ -564,6 +568,7 @@ let external_desc_of_non_obj (loc : Location.t) (st : external_desc)
564568
get_name = None;
565569
return_wrapper = _;
566570
mk_obj = _;
571+
tagged_template = _;
567572
} ->
568573
if arg_type_specs_length = 3 then
569574
Js_set_index {js_set_index_scopes = scopes}
@@ -588,6 +593,7 @@ let external_desc_of_non_obj (loc : Location.t) (st : external_desc)
588593
set_index = false;
589594
mk_obj = _;
590595
return_wrapper = _;
596+
tagged_template = _;
591597
} ->
592598
if arg_type_specs_length = 2 then
593599
Js_get_index {js_get_index_scopes = scopes}
@@ -614,6 +620,7 @@ let external_desc_of_non_obj (loc : Location.t) (st : external_desc)
614620
set_index = false;
615621
return_wrapper = _;
616622
mk_obj = _;
623+
tagged_template = _;
617624
} -> (
618625
match (arg_types_ty, new_name, val_name) with
619626
| [], None, _ -> Js_module_as_var external_module_name
@@ -655,6 +662,7 @@ let external_desc_of_non_obj (loc : Location.t) (st : external_desc)
655662
mk_obj = _;
656663
(* mk_obj is always false *)
657664
return_wrapper = _;
665+
tagged_template;
658666
} ->
659667
let name = prim_name_or_pval_prim.name in
660668
if arg_type_specs_length = 0 then
@@ -665,7 +673,9 @@ let external_desc_of_non_obj (loc : Location.t) (st : external_desc)
665673
FIXME: splice is not supported here
666674
*)
667675
Js_var {name; external_module_name = None; scopes}
668-
else Js_call {splice; name; external_module_name = None; scopes}
676+
else
677+
Js_call
678+
{splice; name; external_module_name = None; scopes; tagged_template}
669679
| {
670680
call_name = Some {name; source = _};
671681
splice;
@@ -681,6 +691,7 @@ let external_desc_of_non_obj (loc : Location.t) (st : external_desc)
681691
get_name = None;
682692
mk_obj = _;
683693
return_wrapper = _;
694+
tagged_template;
684695
} ->
685696
if arg_type_specs_length = 0 then
686697
(*
@@ -690,7 +701,7 @@ let external_desc_of_non_obj (loc : Location.t) (st : external_desc)
690701
*)
691702
Js_var {name; external_module_name; scopes}
692703
(*FIXME: splice is not supported here *)
693-
else Js_call {splice; name; external_module_name; scopes}
704+
else Js_call {splice; name; external_module_name; scopes; tagged_template}
694705
| {call_name = Some _; _} ->
695706
Bs_syntaxerr.err loc
696707
(Conflict_ffi_attribute "Attribute found that conflicts with %@val")
@@ -709,6 +720,7 @@ let external_desc_of_non_obj (loc : Location.t) (st : external_desc)
709720
return_wrapper = _;
710721
splice = false;
711722
scopes;
723+
tagged_template = _;
712724
} ->
713725
(*
714726
if no_arguments -->
@@ -735,6 +747,7 @@ let external_desc_of_non_obj (loc : Location.t) (st : external_desc)
735747
get_name = None;
736748
mk_obj = _;
737749
return_wrapper = _;
750+
tagged_template;
738751
} ->
739752
let name = prim_name_or_pval_prim.name in
740753
if arg_type_specs_length = 0 then
@@ -744,7 +757,7 @@ let external_desc_of_non_obj (loc : Location.t) (st : external_desc)
744757
]}
745758
*)
746759
Js_var {name; external_module_name; scopes}
747-
else Js_call {splice; name; external_module_name; scopes}
760+
else Js_call {splice; name; external_module_name; scopes; tagged_template}
748761
| {
749762
val_send = Some {name; source = _};
750763
splice;
@@ -760,6 +773,7 @@ let external_desc_of_non_obj (loc : Location.t) (st : external_desc)
760773
external_module_name = None;
761774
mk_obj = _;
762775
return_wrapper = _;
776+
tagged_template = _;
763777
} -> (
764778
(* PR #2162 - since when we assemble arguments the first argument in
765779
[@@send] is ignored
@@ -791,6 +805,7 @@ let external_desc_of_non_obj (loc : Location.t) (st : external_desc)
791805
scopes;
792806
mk_obj = _;
793807
return_wrapper = _;
808+
tagged_template = _;
794809
} ->
795810
Js_new {name; external_module_name; splice; scopes}
796811
| {new_name = Some _; _} ->
@@ -811,6 +826,7 @@ let external_desc_of_non_obj (loc : Location.t) (st : external_desc)
811826
mk_obj = _;
812827
return_wrapper = _;
813828
scopes;
829+
tagged_template = _;
814830
} ->
815831
if arg_type_specs_length = 2 then
816832
Js_set {js_set_scopes = scopes; js_set_name = name}
@@ -834,6 +850,7 @@ let external_desc_of_non_obj (loc : Location.t) (st : external_desc)
834850
mk_obj = _;
835851
return_wrapper = _;
836852
scopes;
853+
tagged_template = _;
837854
} ->
838855
if arg_type_specs_length = 1 then
839856
Js_get {js_get_name = name; js_get_scopes = scopes}

jscomp/frontend/external_ffi_types.ml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ type external_spec =
5353
external_module_name: external_module_name option;
5454
splice: bool;
5555
scopes: string list;
56+
tagged_template: bool;
5657
}
5758
| Js_send of {name: string; splice: bool; js_send_scopes: string list}
5859
(* we know it is a js send, but what will happen if you pass an ocaml objct *)
@@ -188,7 +189,9 @@ let check_ffi ?loc ffi : bool =
188189
upgrade (is_package_relative_path external_module_name.bundle);
189190
check_external_module_name external_module_name
190191
| Js_new {external_module_name; name; splice = _; scopes = _}
191-
| Js_call {external_module_name; name; splice = _; scopes = _} ->
192+
| Js_call
193+
{external_module_name; name; splice = _; scopes = _; tagged_template = _}
194+
->
192195
Ext_option.iter external_module_name (fun external_module_name ->
193196
upgrade (is_package_relative_path external_module_name.bundle));
194197
Ext_option.iter external_module_name (fun name ->

jscomp/frontend/external_ffi_types.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ type external_spec =
5050
external_module_name: external_module_name option;
5151
splice: bool;
5252
scopes: string list;
53+
tagged_template: bool;
5354
}
5455
| Js_send of {name: string; splice: bool; js_send_scopes: string list}
5556
(* we know it is a js send, but what will happen if you pass an ocaml objct *)

0 commit comments

Comments
 (0)