Skip to content

Commit 8ea4775

Browse files
committed
Represent regexp literals with a dedicated AST node
Signed-off-by: Christoph Knittel <ck@cca.io> ^ Conflicts: ^ tests/ounit_tests/ounit_lambda_constant_tests.ml
1 parent d2e5fc5 commit 8ea4775

35 files changed

Lines changed: 819 additions & 443 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080

8181
#### :house: Internal
8282

83+
- Give regexp literals a dedicated AST node. https://github.com/rescript-lang/rescript/pull/8610
8384
- Developer playground: Make panes resizable with wrapping text. https://github.com/rescript-lang/rescript/pull/8628
8485
- Normalize Lambda terms where they are built: a match guard stays structured data until its fallthrough is known, and `apply` and `mk_builtin` go through the folding constructors. https://github.com/rescript-lang/rescript/pull/8615
8586
- Replace non-escaping local mutable blocks with scalar bindings when all uses are direct field accesses, generalizing reference unboxing to multi-field records and references captured by JavaScript closures. https://github.com/rescript-lang/rescript/pull/8617

analysis/src/dump_ast.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ and print_expr_item expr ~pos ~indentation =
267267
^ ")"
268268
| Pexp_extension (({txt} as loc), _) ->
269269
"Pexp_extension(%" ^ (loc |> print_loc_denominator_loc ~pos) ^ txt ^ ")"
270+
| Pexp_regexp {pattern; flags} ->
271+
"Pexp_regexp(/" ^ pattern ^ "/" ^ flags ^ ")"
270272
| Pexp_template {source_segments; values} ->
271273
"Pexp_template(source_segments=["
272274
^ String.concat ", " (List.map (fun {Asttypes.txt} -> txt) source_segments)

analysis/src/utils.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ let identify_pexp pexp =
118118
| Pexp_open _ -> "Pexp_open"
119119
| Pexp_await _ -> "Pexp_await"
120120
| Pexp_jsx_element _ -> "Pexp_jsx_element"
121+
| Pexp_regexp _ -> "Pexp_regexp"
121122
| Pexp_template _ -> "Pexp_template"
122123
| Pexp_tagged_template _ -> "Pexp_tagged_template"
123124

compiler/ext/config.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ let cmi_magic_number = "Caml1999I034"
22

33
(* Magic numbers for marshaled values of the *current* parsetree, whose layout
44
changes across compiler versions. *)
5-
and ast_impl_magic_number = "ResImpl01307"
5+
and ast_impl_magic_number = "ResImpl01308"
66

7-
and ast_intf_magic_number = "ResIntf01307"
7+
and ast_intf_magic_number = "ResIntf01308"
88

99
(* Magic numbers of the frozen Parsetree0 (OCaml 4.06) layout used on the
1010
external-PPX wire. They must never be written in front of a

compiler/frontend/bs_builtin_ppx.ml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,23 @@ let expr_mapper ~async_context ~in_function_def (self : mapper)
112112
in_function_def := false;
113113
match e.pexp_desc with
114114
(* Its output should not be rewritten anymore *)
115+
| Pexp_regexp {pattern; flags} ->
116+
let loc = e.pexp_loc in
117+
let source = "/" ^ pattern ^ "/" ^ flags in
118+
Ast_payload.validate_raw_source ~kind:Raw_re ~loc ~offset:0 source;
119+
let raw =
120+
Ast_external_mk.local_external_apply loc
121+
~pval_prim:(Prim_name "#raw_expr")
122+
~pval_type:
123+
(Ast_helper.Typ.arrow
124+
[{attrs = []; lbl = Nolabel; typ = Ast_helper.Typ.any ()}]
125+
(Ast_helper.Typ.any ()))
126+
[Ast_helper.Exp.constant ~loc (Pconst_raw_source source)]
127+
in
128+
Ast_helper.Exp.constraint_ ~loc
129+
~attrs:(self.attributes self e.pexp_attributes)
130+
{e with pexp_desc = raw; pexp_attributes = []}
131+
(Ast_comb.to_regexp_type loc)
115132
| Pexp_extension extension ->
116133
Ast_exp_extension.handle_extension e self extension
117134
| Pexp_constant (Pconst_integer (s, Some 'l')) ->

compiler/ml/ast_helper.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ module Exp = struct
205205
let pack ?loc ?attrs a = mk ?loc ?attrs (Pexp_pack a)
206206
let open_ ?loc ?attrs a b c = mk ?loc ?attrs (Pexp_open (a, b, c))
207207
let extension ?loc ?attrs a = mk ?loc ?attrs (Pexp_extension a)
208+
let regexp ?loc ?attrs pattern flags =
209+
mk ?loc ?attrs (Pexp_regexp {pattern; flags})
208210
let template ?loc ?attrs source_segments values =
209211
mk ?loc ?attrs (Pexp_template {source_segments; values})
210212
let tagged_template ?loc ?attrs tag raw_sources values =

compiler/ml/ast_helper.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ module Exp : sig
226226
val object_literal :
227227
?loc:loc -> ?attrs:attrs -> (str * expression) list -> expression
228228

229+
val regexp : ?loc:loc -> ?attrs:attrs -> string -> string -> expression
230+
229231
val template :
230232
?loc:loc -> ?attrs:attrs -> str list -> expression list -> expression
231233
val letmodule :

compiler/ml/ast_iterator.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ module E = struct
390390
iter_loc sub lid;
391391
sub.expr sub e
392392
| Pexp_extension x -> sub.extension sub x
393+
| Pexp_regexp _ -> ()
393394
| Pexp_template {values} -> List.iter (sub.expr sub) values
394395
| Pexp_tagged_template {tag; values} ->
395396
sub.expr sub tag;

compiler/ml/ast_mapper.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ module E = struct
352352
| Pexp_for_await_of (p, e1, e2) ->
353353
Exp.mk ~loc ~attrs
354354
(Pexp_for_await_of (sub.pat sub p, sub.expr sub e1, sub.expr sub e2))
355+
| Pexp_regexp {pattern; flags} -> regexp ~loc ~attrs pattern flags
355356
| Pexp_template {source_segments; values} ->
356357
Exp.template ~loc ~attrs
357358
(List.map (map_loc sub) source_segments)

compiler/ml/ast_mapper_from0.ml

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ let map_pattern_constant ~loc = function
123123
| constant -> map_constant ~loc constant
124124

125125
let is_raw_source_extension = function
126-
| "raw" | "ffi" | "re" -> true
126+
| "raw" | "ffi" -> true
127127
| _ -> false
128128

129129
let map_raw_source_payload sub = function
@@ -1132,6 +1132,49 @@ module E = struct
11321132
| Pexp_pack me -> pack ~loc ~attrs (sub.module_expr sub me)
11331133
| Pexp_open (ovf, lid, e) ->
11341134
open_ ~loc ~attrs ovf (map_loc sub lid) (sub.expr sub e)
1135+
| Pexp_extension ({txt = "re"}, payload) -> (
1136+
let malformed ~loc =
1137+
Location.raise_errorf ~loc
1138+
"A PPX returned a malformed regexp payload. Expected a string \
1139+
containing one regexp literal."
1140+
in
1141+
match payload with
1142+
| PStr
1143+
[
1144+
{
1145+
pstr_desc =
1146+
Pstr_eval
1147+
( {
1148+
pexp_desc = Pexp_constant (Pconst_string (source, _));
1149+
pexp_loc = source_loc;
1150+
pexp_attributes = source_attrs;
1151+
},
1152+
eval_attrs );
1153+
};
1154+
] -> (
1155+
let env = Parser_env.init_env None source in
1156+
let (_, expression), errors =
1157+
Parser_flow.do_parse env Parser_flow.Parse.expression false
1158+
in
1159+
match expression with
1160+
| Flow_ast.Expression.RegExpLiteral {pattern; raw}
1161+
when errors = [] && Parser_env.Peek.token env = Token.T_EOF ->
1162+
(* Flow filters unknown flags in its [flags] field. Keep the raw
1163+
spelling so the bridge never silently changes a PPX's regexp. *)
1164+
let flags_start = String.length pattern + 2 in
1165+
let flags =
1166+
String.sub raw flags_start (String.length raw - flags_start)
1167+
in
1168+
(* Payload wrappers disappear at this boundary. Keep the expression's
1169+
location and transfer both levels of payload attributes to it. *)
1170+
regexp ~loc
1171+
~attrs:
1172+
(attrs
1173+
@ sub.attributes sub eval_attrs
1174+
@ sub.attributes sub source_attrs)
1175+
pattern flags
1176+
| _ -> malformed ~loc:(sub.location sub source_loc))
1177+
| _ -> malformed ~loc)
11351178
| Pexp_extension x -> extension ~loc ~attrs (sub.extension sub x)
11361179
| Pexp_unreachable -> assert false
11371180
end
@@ -1369,6 +1412,9 @@ let default_mapper =
13691412
location = (fun _this l -> l);
13701413
extension =
13711414
(fun this (s, payload) ->
1415+
if s.txt = "re" then
1416+
Location.raise_errorf ~loc:(this.location this s.loc)
1417+
"A PPX returned a regexp extension outside an expression.";
13721418
let payload =
13731419
if is_raw_source_extension s.txt then
13741420
match map_raw_source_payload this payload with

0 commit comments

Comments
 (0)