Skip to content
This repository was archived by the owner on Jun 15, 2023. It is now read-only.

Commit f27e107

Browse files
Add support for parsing template literals in patterns. (#326)
* Add support for parsing template literals in patterns. * Add extra conversion test for template literals in pattern matching
1 parent f7227ac commit f27e107

File tree

11 files changed

+193
-4
lines changed

11 files changed

+193
-4
lines changed

src/res_ast_conversion.ml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,17 @@ let normalize =
355355
| Ppat_open ({txt = longidentOpen}, pattern) ->
356356
let p = rewritePpatOpen longidentOpen pattern in
357357
default_mapper.pat mapper p
358+
| Ppat_constant (Pconst_string (txt, tag)) ->
359+
let newTag = match tag with
360+
(* transform {|abc|} into {js|abc|js}, because `template string` is interpreted as {js||js} *)
361+
| Some "" -> Some "js"
362+
| tag -> tag
363+
in
364+
let s = Parsetree.Pconst_string ((escapeTemplateLiteral txt), newTag) in
365+
{p with
366+
ppat_attributes = mapper.attributes mapper p.ppat_attributes;
367+
ppat_desc = Ppat_constant s
368+
}
358369
| _ ->
359370
default_mapper.pat mapper p
360371
end;

src/res_core.ml

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ Solution: directly use `concat`."
109109
"A labeled parameter starts with a `~`."
110110
else
111111
("A labeled parameter starts with a `~`. Did you mean: `~" ^ name ^ "`?")
112+
113+
let stringInterpolationInPattern =
114+
"String interpolation is not supported in pattern matching."
112115
end
113116

114117

@@ -903,6 +906,27 @@ let parseConstant p =
903906
Parser.next p;
904907
constant
905908

909+
let parseTemplateConstant ~prefix (p : Parser.t) =
910+
(* Arrived at the ` char *)
911+
let startPos = p.startPos in
912+
Parser.nextTemplateLiteralToken p;
913+
match p.token with
914+
| TemplateTail txt ->
915+
Parser.next p;
916+
let txt = if p.mode = ParseForTypeChecker then parseTemplateStringLiteral txt else txt in
917+
Parsetree.Pconst_string (txt, prefix)
918+
| _ ->
919+
let rec skipTokens () =
920+
Parser.next p;
921+
match p.token with
922+
| Backtick -> Parser.next p; ()
923+
| _ -> skipTokens ()
924+
in
925+
skipTokens ();
926+
Parser.err ~startPos ~endPos:p.prevEndPos p
927+
(Diagnostics.message ErrorMessages.stringInterpolationInPattern);
928+
Pconst_string ("", None)
929+
906930
let parseCommaDelimitedRegion p ~grammar ~closing ~f =
907931
Parser.leaveBreadcrumb p grammar;
908932
let rec loop nodes =
@@ -1076,6 +1100,9 @@ let rec parsePattern ?(alias=true) ?(or_=true) p =
10761100
| _ ->
10771101
Ast_helper.Pat.constant ~loc:(mkLoc startPos p.prevEndPos) c
10781102
end
1103+
| Backtick ->
1104+
let constant = parseTemplateConstant ~prefix:(Some "js") p in
1105+
Ast_helper.Pat.constant ~loc:(mkLoc startPos p.prevEndPos) constant
10791106
| Lparen ->
10801107
Parser.next p;
10811108
begin match p.token with
@@ -1109,7 +1136,13 @@ let rec parsePattern ?(alias=true) ?(or_=true) p =
11091136
let endPos = p.endPos in
11101137
let loc = mkLoc startPos endPos in
11111138
Parser.next p;
1112-
Ast_helper.Pat.var ~loc ~attrs (Location.mkloc ident loc)
1139+
begin match p.token with
1140+
| Backtick ->
1141+
let constant = parseTemplateConstant ~prefix:(Some ident) p in
1142+
Ast_helper.Pat.constant ~loc:(mkLoc startPos p.prevEndPos) constant
1143+
| _ ->
1144+
Ast_helper.Pat.var ~loc ~attrs (Location.mkloc ident loc)
1145+
end
11131146
| Uident _ ->
11141147
let constr = parseModuleLongIdent ~lowercase:false p in
11151148
begin match p.Parser.token with

src/res_grammar.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ let isSignatureItemStart = function
136136
| _ -> false
137137

138138
let isAtomicPatternStart = function
139-
| Token.Int _ | String _ | Character _
139+
| Token.Int _ | String _ | Character _ | Backtick
140140
| Lparen | Lbracket | Lbrace
141141
| Underscore
142142
| Lident _ | Uident _ | List
@@ -196,7 +196,7 @@ let isStructureItemStart = function
196196
| _ -> false
197197

198198
let isPatternStart = function
199-
| Token.Int _ | Float _ | String _ | Character _ | True | False | Minus | Plus
199+
| Token.Int _ | Float _ | String _ | Character _ | Backtick | True | False | Minus | Plus
200200
| Lparen | Lbracket | Lbrace | List
201201
| Underscore
202202
| Lident _ | Uident _ | Hash

tests/conversion/reason/__snapshots__/render.spec.js.snap

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1583,6 +1583,15 @@ let x = \`/*https://www.apple.com*/\`
15831583
let x = sql\`/*https://www.apple.com*/\`
15841584
15851585
let x = \`\\\\\`https://\\\\\${appleWebsite}\\\\\`\`
1586+
1587+
let var1 = \\"three\\"
1588+
let var2 = \\"a string\\"
1589+
1590+
switch (var1, var2) {
1591+
| (\`3\`, \`a string\`) => Js.log(\\"worked\\")
1592+
| (\` test with \\\\\` \\\\\${here} \\\\\`\`, _) => Js.log(\\"escapes \` and \${\\")
1593+
| _ => Js.log(\\"didn't match\\")
1594+
}
15861595
"
15871596
`;
15881597

tests/conversion/reason/string.re

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,12 @@ let x = {|/*https://www.apple.com*/|};
2727
let x = {sql|/*https://www.apple.com*/|sql};
2828

2929
let x = {js|`https://${appleWebsite}`|js};
30+
31+
let var1 = "three"
32+
let var2 = "a string";
33+
34+
switch (var1, var2) {
35+
| ({|3|}, {|a string|}) => Js.log("worked")
36+
| ({| test with ` ${here} `|}, _) => Js.log("escapes ` and ${")
37+
| _ => Js.log("didn't match")
38+
};

tests/parsing/errors/pattern/__snapshots__/parse.spec.js.snap

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,45 @@ let 4 = for [%rescript.patternhole ] = 0 to 10 do Js.log \\"for\\" done
5050
5151
========================================================"
5252
`;
53+
54+
exports[`templateLiteral.res 1`] = `
55+
"=====Parsetree==========================================
56+
let zeroCoord = \\"0.0\\"
57+
;;match l with | \\"\\" -> () | \\"\\" -> () | _ -> ()
58+
=====Errors=============================================
59+
60+
Syntax error!
61+
parsing/errors/pattern/templateLiteral.res:4:3-27
62+
2 │
63+
3 │ switch l {
64+
4| \`zero coord \${zeroCoord}\` => ()
65+
5 │ | \`first coord \${zeroCoord} snd \${zeroCoord} \` => ()
66+
6 │ | _ => ()
67+
68+
String interpolation is not supported in pattern matching.
69+
70+
71+
Syntax error!
72+
parsing/errors/pattern/templateLiteral.res:5:33
73+
3 │ switch l {
74+
4| \`zero coord \${zeroCoord}\` => ()
75+
5 │ | \`first coord \${zeroCoord} snd \${zeroCoord} \` => ()
76+
6 │ | _ => ()
77+
7 │ }
78+
79+
Not sure what to do with this character.
80+
81+
82+
Syntax error!
83+
parsing/errors/pattern/templateLiteral.res:5:3-46
84+
3 │ switch l {
85+
4| \`zero coord \${zeroCoord}\` => ()
86+
5 │ | \`first coord \${zeroCoord} snd \${zeroCoord} \` => ()
87+
6 │ | _ => ()
88+
7 │ }
89+
90+
String interpolation is not supported in pattern matching.
91+
92+
93+
========================================================"
94+
`;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
let zeroCoord = "0.0"
2+
3+
switch l {
4+
| `zero coord ${zeroCoord}` => ()
5+
| `first coord ${zeroCoord} snd ${zeroCoord} ` => ()
6+
| _ => ()
7+
}

tests/parsing/grammar/pattern/__snapshots__/parse.spec.js.snap

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,23 @@ let (-1)..(-1.) = x
131131
| ((-3.14) : float) -> true
132132
| (lazy 5.678) -> true
133133
| exception 19.34 -> true
134-
| _ -> false"
134+
| _ -> false
135+
;;match literal with
136+
| {js|literal|js} -> true
137+
| ({js|literal1|js}, {js|literal2|js}) -> true
138+
| [|{js|literal1|js};{js|literal2|js}|] -> true
139+
| {js|literal1|js}::{js|literal2|js}::[] -> true
140+
| { x = {js|literal1|js}; y = {js|literal2|js} } -> true
141+
| Constructor ({js|literal1|js}, {js|literal2|js}) -> true
142+
| \`Constuctor ({js|literal1|js}, {js|literal2|js}) -> true
143+
| {js|literal|js} as x -> true
144+
| {js|literal|js}|{js|literal|js} -> true
145+
| ({js|literal|js} : string) -> true
146+
| (lazy {js|literal|js}) -> true
147+
| exception {js|literal|js} -> true
148+
| _ -> false
149+
let {js|literal constant|js} = x
150+
;;for {js|literal constant|js} = 0 to 10 do () done"
135151
`;
136152

137153
exports[`constraint.js 1`] = `

tests/parsing/grammar/pattern/constant.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,23 @@ switch science {
8383
| exception 19.34 => true
8484
| _ => false
8585
}
86+
87+
switch literal {
88+
| `literal` => true
89+
| (`literal1`, `literal2`) => true
90+
| [`literal1`, `literal2`] => true
91+
| list{`literal1`, `literal2`} => true
92+
| {x: `literal1`, y: `literal2`} => true
93+
| Constructor(`literal1`, `literal2`) => true
94+
| #Constuctor(`literal1`, `literal2`) => true
95+
| `literal` as x => true
96+
| `literal` | `literal` => true
97+
| (`literal` : string) => true
98+
| lazy `literal` => true
99+
| exception `literal` => true
100+
| _ => false
101+
}
102+
103+
let `literal constant` = x
104+
105+
for `literal constant` in 0 to 10 { () }

tests/printer/pattern/__snapshots__/render.spec.js.snap

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,28 @@ switch science {
6464
</div>
6565
}}
6666
</div>
67+
68+
switch literal {
69+
| \`literal\` => true
70+
| (\`literal1\`, \`literal2\`) => true
71+
| [\`literal1\`, \`literal2\`] => true
72+
| list{\`literal1\`, \`literal2\`} => true
73+
| {x: \`literal1\`, y: \`literal2\`} => true
74+
| Constructor(\`literal1\`, \`literal2\`) => true
75+
| #Constuctor(\`literal1\`, \`literal2\`) => true
76+
| \`literal\` as x => true
77+
| \`literal\` | \`literal\` => true
78+
| (\`literal\`: string) => true
79+
| lazy \`literal\` => true
80+
| exception \`literal\` => true
81+
| _ => false
82+
}
83+
84+
let \`literal constant\` = x
85+
86+
for \`literal constant\` in 0 to 10 {
87+
()
88+
}
6789
"
6890
`;
6991

0 commit comments

Comments
 (0)