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

Commit c5f808f

Browse files
author
Iwan
committed
Parse all normal strings as {js||js} strings.
The compiler processes these strings with js semantics. Previously {js||js} where interpreted as template literal strings. The internal encoding has been changed to use an attribute (@res.template) to detect template literal strings
1 parent 87b36d0 commit c5f808f

File tree

60 files changed

+425
-334
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+425
-334
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ test: reanalyze build-native lib/test.exe
7575
./lib/test.exe
7676
./test.sh
7777

78-
roundtrip-test: reanalyze bootstrap lib/test.exe
78+
roundtrip-test: reanalyze lib/test.exe
7979
./lib/test.exe
8080
ROUNDTRIP_TEST=1 ./test.sh
8181

src/res_ast_conversion.ml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,8 @@ let hasUncurriedAttribute attrs = List.exists (fun attr -> match attr with
323323
| _ -> false
324324
) attrs
325325

326+
let templateLiteralAttr = (Location.mknoloc "res.template", Parsetree.PStr [])
327+
326328
let normalize =
327329
let open Ast_mapper in
328330
{ default_mapper with
@@ -368,7 +370,7 @@ let normalize =
368370
in
369371
let s = Parsetree.Pconst_string ((escapeTemplateLiteral txt), newTag) in
370372
{p with
371-
ppat_attributes = mapper.attributes mapper p.ppat_attributes;
373+
ppat_attributes = templateLiteralAttr::(mapper.attributes mapper p.ppat_attributes);
372374
ppat_desc = Ppat_constant s
373375
}
374376
| _ ->
@@ -396,7 +398,7 @@ let normalize =
396398
in
397399
let s = Parsetree.Pconst_string ((escapeTemplateLiteral txt), newTag) in
398400
{expr with
399-
pexp_attributes = mapper.attributes mapper expr.pexp_attributes;
401+
pexp_attributes= templateLiteralAttr::(mapper.attributes mapper expr.pexp_attributes);
400402
pexp_desc = Pexp_constant s
401403
}
402404
| Pexp_apply (

src/res_core.ml

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ let ternaryAttr = (Location.mknoloc "ns.ternary", Parsetree.PStr [])
136136
let ifLetAttr = (Location.mknoloc "ns.iflet", Parsetree.PStr [])
137137
let suppressFragileMatchWarningAttr = (Location.mknoloc "warning", Parsetree.PStr [Ast_helper.Str.eval (Ast_helper.Exp.constant (Pconst_string ("-4", None)))])
138138
let makeBracesAttr loc = (Location.mkloc "ns.braces" loc, Parsetree.PStr [])
139+
let templateLiteralAttr = (Location.mknoloc "res.template", Parsetree.PStr [])
139140

140141
type stringLiteralState =
141142
| Start
@@ -911,12 +912,10 @@ let parseConstant p =
911912
let floatTxt = if isNegative then "-" ^ f else f in
912913
Parsetree.Pconst_float (floatTxt, suffix)
913914
| String s ->
914-
let txt = if p.mode = ParseForTypeChecker then
915-
parseStringLiteral s
915+
if p.mode = ParseForTypeChecker then
916+
Pconst_string (s, Some "js")
916917
else
917-
s
918-
in
919-
Pconst_string(txt, None)
918+
Pconst_string (s, None)
920919
| Codepoint {c; original} ->
921920
if p.mode = ParseForTypeChecker then
922921
Pconst_char c
@@ -1128,7 +1127,7 @@ let rec parsePattern ?(alias=true) ?(or_=true) p =
11281127
end
11291128
| Backtick ->
11301129
let constant = parseTemplateConstant ~prefix:(Some "js") p in
1131-
Ast_helper.Pat.constant ~loc:(mkLoc startPos p.prevEndPos) constant
1130+
Ast_helper.Pat.constant ~attrs:[templateLiteralAttr] ~loc:(mkLoc startPos p.prevEndPos) constant
11321131
| Lparen ->
11331132
Parser.next p;
11341133
begin match p.token with
@@ -2228,25 +2227,19 @@ and parseTemplateExpr ?(prefix="js") p =
22282227
| TemplateTail txt ->
22292228
Parser.next p;
22302229
let loc = mkLoc startPos p.prevEndPos in
2231-
if String.length txt > 0 then
2232-
let txt = if p.mode = ParseForTypeChecker then parseTemplateStringLiteral txt else txt in
2233-
let str = Ast_helper.Exp.constant ~loc (Pconst_string(txt, Some prefix)) in
2234-
Ast_helper.Exp.apply ~loc hiddenOperator
2235-
[Nolabel, acc; Nolabel, str]
2236-
else
2237-
acc
2230+
let txt = if p.mode = ParseForTypeChecker then parseTemplateStringLiteral txt else txt in
2231+
let str = Ast_helper.Exp.constant ~attrs:[templateLiteralAttr] ~loc (Pconst_string(txt, Some prefix)) in
2232+
Ast_helper.Exp.apply ~loc hiddenOperator
2233+
[Nolabel, acc; Nolabel, str]
22382234
| TemplatePart txt ->
22392235
Parser.next p;
22402236
let loc = mkLoc startPos p.prevEndPos in
22412237
let expr = parseExprBlock p in
22422238
let fullLoc = mkLoc startPos p.prevEndPos in
22432239
let txt = if p.mode = ParseForTypeChecker then parseTemplateStringLiteral txt else txt in
2244-
let str = Ast_helper.Exp.constant ~loc (Pconst_string(txt, Some prefix)) in
2240+
let str = Ast_helper.Exp.constant ~attrs:[templateLiteralAttr] ~loc (Pconst_string(txt, Some prefix)) in
22452241
let next =
2246-
let a = if String.length txt > 0 then
2247-
Ast_helper.Exp.apply ~loc:fullLoc hiddenOperator [Nolabel, acc; Nolabel, str]
2248-
else acc
2249-
in
2242+
let a = Ast_helper.Exp.apply ~loc:fullLoc hiddenOperator [Nolabel, acc; Nolabel, str] in
22502243
Ast_helper.Exp.apply ~loc:fullLoc hiddenOperator
22512244
[Nolabel, a; Nolabel, expr]
22522245
in
@@ -2261,19 +2254,16 @@ and parseTemplateExpr ?(prefix="js") p =
22612254
| TemplateTail txt ->
22622255
Parser.next p;
22632256
let txt = if p.mode = ParseForTypeChecker then parseTemplateStringLiteral txt else txt in
2264-
Ast_helper.Exp.constant ~loc:(mkLoc startPos p.prevEndPos) (Pconst_string(txt, Some prefix))
2257+
Ast_helper.Exp.constant ~attrs:[templateLiteralAttr] ~loc:(mkLoc startPos p.prevEndPos) (Pconst_string(txt, Some prefix))
22652258
| TemplatePart txt ->
22662259
Parser.next p;
22672260
let constantLoc = mkLoc startPos p.prevEndPos in
22682261
let expr = parseExprBlock p in
22692262
let fullLoc = mkLoc startPos p.prevEndPos in
22702263
let txt = if p.mode = ParseForTypeChecker then parseTemplateStringLiteral txt else txt in
2271-
let str = Ast_helper.Exp.constant ~loc:constantLoc (Pconst_string(txt, Some prefix)) in
2264+
let str = Ast_helper.Exp.constant ~attrs:[templateLiteralAttr] ~loc:constantLoc (Pconst_string(txt, Some prefix)) in
22722265
let next =
2273-
if String.length txt > 0 then
2274-
Ast_helper.Exp.apply ~loc:fullLoc hiddenOperator [Nolabel, str; Nolabel, expr]
2275-
else
2276-
expr
2266+
Ast_helper.Exp.apply ~loc:fullLoc hiddenOperator [Nolabel, str; Nolabel, expr]
22772267
in
22782268
parseParts next
22792269
| token ->

src/res_parsetree_viewer.ml

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ let processBracesAttr expr =
153153
let filterParsingAttrs attrs =
154154
List.filter (fun attr ->
155155
match attr with
156-
| ({Location.txt = ("ns.ternary" | "ns.braces" | "bs" | "ns.iflet" | "ns.namedArgLoc")}, _) -> false
156+
| ({Location.txt = ("ns.ternary" | "ns.braces" | "res.template" | "bs" | "ns.iflet" | "ns.namedArgLoc")}, _) -> false
157157
| _ -> true
158158
) attrs
159159

@@ -292,7 +292,7 @@ let isIfLetExpr expr = match expr with
292292

293293
let hasAttributes attrs =
294294
List.exists (fun attr -> match attr with
295-
| ({Location.txt = "bs" | "ns.ternary" | "ns.braces" | "ns.iflet"}, _) -> false
295+
| ({Location.txt = "bs" | "res.template" | "ns.ternary" | "ns.braces" | "ns.iflet"}, _) -> false
296296
(* Remove the fragile pattern warning for iflet expressions *)
297297
| ({Location.txt="warning"}, PStr [{
298298
pstr_desc = Pstr_eval ({
@@ -453,13 +453,13 @@ let shouldInlineRhsBinaryExpr rhs = match rhs.pexp_desc with
453453

454454
let filterPrinteableAttributes attrs =
455455
List.filter (fun attr -> match attr with
456-
| ({Location.txt="bs" | "ns.ternary" | "ns.iflet" | "JSX"}, _) -> false
456+
| ({Location.txt="bs" | "res.template" | "ns.ternary" | "ns.iflet" | "JSX"}, _) -> false
457457
| _ -> true
458458
) attrs
459459

460460
let partitionPrinteableAttributes attrs =
461461
List.partition (fun attr -> match attr with
462-
| ({Location.txt="bs" | "ns.ternary" | "ns.iflet" | "JSX"}, _) -> false
462+
| ({Location.txt="bs" | "res.template"| "ns.ternary" | "ns.iflet" | "JSX"}, _) -> false
463463
| _ -> true
464464
) attrs
465465

@@ -511,17 +511,25 @@ let rec collectPatternsFromListConstruct acc pattern =
511511
collectPatternsFromListConstruct (pat::acc) rest
512512
| _ -> List.rev acc, pattern
513513

514-
(* Simple heuristic to detect template literal sugar:
515-
* `${user.name} lastName` parses internally as user.name ++ ` lastName`.
516-
* The thing is: the ++ operator (parsed as `^`) will always have a ghost loc.
517-
* A ghost loc is only produced by our parser.
518-
* Hence, if we have that ghost operator, we know for sure it's a template literal. *)
514+
515+
let hasTemplateLiteralAttr attrs = List.exists (fun attr -> match attr with
516+
| ({Location.txt = "res.template"}, _) -> true
517+
| _ -> false) attrs
518+
519519
let isTemplateLiteral expr =
520520
match expr.pexp_desc with
521521
| Pexp_apply (
522-
{pexp_desc = Pexp_ident {txt = Longident.Lident "^"; loc}},
523-
[Nolabel, _; Nolabel, _]
524-
) when loc.loc_ghost -> true
522+
{pexp_desc = Pexp_ident {txt = Longident.Lident "^"}},
523+
[Nolabel, expr1; Nolabel, expr2]
524+
) ->
525+
let firstIsTemplate = hasTemplateLiteralAttr expr1.pexp_attributes in
526+
let sndIsTemplate = hasTemplateLiteralAttr expr2.pexp_attributes in
527+
begin match (firstIsTemplate, sndIsTemplate) with
528+
| (true, true) | (false, false) -> false
529+
| _ -> true
530+
end
531+
| Pexp_constant (Pconst_string (_, Some "")) -> true
532+
| Pexp_constant _ when hasTemplateLiteralAttr expr.pexp_attributes -> true
525533
| _ -> false
526534

527535
(* Blue | Red | Green -> [Blue; Red; Green] *)

src/res_parsetree_viewer.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ val collectPatternsFromListConstruct:
109109
val isBlockExpr : Parsetree.expression -> bool
110110

111111
val isTemplateLiteral: Parsetree.expression -> bool
112+
val hasTemplateLiteralAttr: Parsetree.attributes -> bool
112113

113114
val collectOrPatternChain:
114115
Parsetree.pattern -> Parsetree.pattern list

src/res_printer.ml

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ let printStringContents txt =
495495
let lines = String.split_on_char '\n' txt in
496496
Doc.join ~sep:Doc.literalLine (List.map Doc.text lines)
497497

498-
let printConstant c = match c with
498+
let printConstant ?(templateLiteral=false) c = match c with
499499
| Parsetree.Pconst_integer (s, suffix) ->
500500
begin match suffix with
501501
| Some c -> Doc.text (s ^ (Char.escaped c))
@@ -511,11 +511,14 @@ let printConstant c = match c with
511511
if prefix = "INTERNAL_RES_CHAR_CONTENTS" then
512512
Doc.concat [Doc.text "'"; Doc.text txt; Doc.text "'"]
513513
else
514+
let (lquote, rquote) =
515+
if templateLiteral then ("`", "`") else ("\"", "\"")
516+
in
514517
Doc.concat [
515518
if prefix = "js" then Doc.nil else Doc.text prefix;
516-
Doc.text "`";
519+
Doc.text lquote;
517520
printStringContents txt;
518-
Doc.text "`";
521+
Doc.text rquote;
519522
]
520523
| Pconst_float (s, _) -> Doc.text s
521524
| Pconst_char c ->
@@ -2097,7 +2100,9 @@ and printPattern (p : Parsetree.pattern) cmtTbl =
20972100
let patternWithoutAttributes = match p.ppat_desc with
20982101
| Ppat_any -> Doc.text "_"
20992102
| Ppat_var var -> printIdentLike var.txt
2100-
| Ppat_constant c -> printConstant c
2103+
| Ppat_constant c ->
2104+
let templateLiteral = ParsetreeViewer.hasTemplateLiteralAttr p.ppat_attributes in
2105+
printConstant ~templateLiteral c
21012106
| Ppat_tuple patterns ->
21022107
Doc.group(
21032108
Doc.concat([
@@ -2540,7 +2545,8 @@ and printIfChain pexp_attributes ifs elseExpr cmtTbl =
25402545

25412546
and printExpression (e : Parsetree.expression) cmtTbl =
25422547
let printedExpression = match e.pexp_desc with
2543-
| Parsetree.Pexp_constant c -> printConstant c
2548+
| Parsetree.Pexp_constant c ->
2549+
printConstant ~templateLiteral:(ParsetreeViewer.isTemplateLiteral e) c
25442550
| Pexp_construct _ when ParsetreeViewer.hasJsxAttribute e.pexp_attributes ->
25452551
printJsxFragment e cmtTbl
25462552
| Pexp_construct ({txt = Longident.Lident "()"}, _) -> Doc.text "()"

tests/conversion/reason/expected/bracedJsx.re.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ let make = () => {
114114
ref={containerRef->ReactDOMRe.Ref.domRef}>
115115
{state.history
116116
->Array.mapWithIndex((index, item) =>
117-
<div key=j`$index` className=Styles.line>
117+
<div key={j`$index`} className=Styles.line>
118118
{ReasonReact.string(
119119
switch item {
120120
| User(value) => userPrefix ++ value

tests/parsing/errors/expressions/expected/ambiguousArrow.res.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@
2424
1) (pattern): int => "test"
2525
2) (pattern: int) => "test"
2626

27-
let a b = ("hi" : int)
28-
let x = ((let a = 1 in let b = 2 in fun pattern -> ("test" : int))
27+
let a b = ({js|hi|js} : int)
28+
let x = ((let a = 1 in let b = 2 in fun pattern -> ({js|test|js} : int))
2929
[@ns.braces ])

tests/parsing/errors/expressions/expected/emptyeof.res.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77

88
This string is missing a double quote at the end
99

10-
let x = "\n"
10+
let x = {js|
11+
|js}

tests/parsing/errors/expressions/expected/ifLet.res.txt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,12 @@ switch result {
3535
| _ => ()
3636
}
3737

38-
;;((match result with | Some x -> Js.log "The sky is blue" | _ -> ())
38+
;;((match result with | Some x -> Js.log {js|The sky is blue|js} | _ -> ())
3939
[@ns.iflet ][@warning "-4"])
4040
;;((match result with
41-
| Error x -> Js.log "The sky is red"
41+
| Error x -> Js.log {js|The sky is red|js}
4242
| _ ->
43-
(((match result with | Ok y -> Js.log "The sky is blue" | _ -> ()))
43+
(((match result with
44+
| Ok y -> Js.log {js|The sky is blue|js}
45+
| _ -> ()))
4446
[@ns.iflet ][@warning "-4"]))[@ns.iflet ][@warning "-4"])

tests/parsing/errors/expressions/expected/taggedTemplateLiterals.res.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77

88
Tagged template literals are currently restricted to names like: json`null`.
99

10-
;;{js|null|js}
10+
;;(({js|null|js})[@res.template ])

tests/parsing/errors/expressions/expected/unexpectedConstraint.res.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@
2424
("hi": string)
2525

2626
let x = ((let a = 1 in let b = 2 in (a + b : int))[@ns.braces ])
27-
let x = ("hi" : string)
27+
let x = ({js|hi|js} : string)

tests/parsing/errors/other/expected/breadcrumbs170.res.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ module M = struct ;;match l with | None -> [] | Some l -> l#prop end
1818
;;all
1919
;;syntax
2020
;;is
21-
;;"valid"
21+
;;{js|valid|js}
2222
;;it
2323
;;compiles
2424
;;will

tests/parsing/errors/pattern/expected/missing.res.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,5 @@
4444
I was expecting a pattern to match on before the `=>`
4545

4646
let 2 = [%rescript.exprhole ]
47-
let 4 = for [%rescript.patternhole ] = 0 to 10 do Js.log "for" done
47+
let 4 = for [%rescript.patternhole ] = 0 to 10 do Js.log {js|for|js} done
4848
;;match x with | () -> [%rescript.exprhole ]

tests/parsing/errors/pattern/expected/templateLiteral.res.txt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,8 @@
3434

3535
String interpolation is not supported in pattern matching.
3636

37-
let zeroCoord = "0.0"
38-
;;match l with | "" -> () | "" -> () | _ -> ()
37+
let zeroCoord = {js|0.0|js}
38+
;;match l with
39+
| (("")[@res.template ]) -> ()
40+
| (("")[@res.template ]) -> ()
41+
| _ -> ()

tests/parsing/errors/scanner/expected/escapeSequence.res.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@
1919

2020
unknown escape sequence
2121

22-
let x = "\\0"
23-
let x = "\\oAAA"
22+
let x = {js|\0|js}
23+
let x = {js|\oAAA|js}

tests/parsing/errors/scanner/expected/exoticIdent.res.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,5 @@
4343

4444
let a = b
4545
;;c
46-
;;" = 1\n"
46+
;;{js| = 1
47+
|js}

tests/parsing/errors/scanner/expected/unclosedString.res.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77

88
This string is missing a double quote at the end
99

10-
let z = "eof\n"
10+
let z = {js|eof
11+
|js}

tests/parsing/errors/structure/expected/gh16A.res.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@
1111
I'm not sure what to parse here when looking at ")".
1212

1313
module C = struct module T = (Fun)(struct ;;foo (a + c) (b + d) end) end
14-
;;Js.log "test"
14+
;;Js.log {js|test|js}

0 commit comments

Comments
 (0)