From f2c9bf2556ae3b0cd2955f3b6bcef0f150f238fc Mon Sep 17 00:00:00 2001 From: Iwan Date: Wed, 5 May 2021 09:08:50 +0200 Subject: [PATCH 1/4] ## Unicode support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR adds support for Unicode codepoints at the syntax level: ReScript source code is now unicode text encoded in UTF-8. Fixes https://github.com/rescript-lang/syntax/issues/397 ### Codepoint literals A codepoint literal represents an integer value identifying a unicode code point. It is expressed as one or more characters enclosed in single quotes. Examples are `’x’`, `’\n’` or `\u{00A9}`. Multiple UTF-8-encoded bytes may represent a single integer value. ### String literals String literals are (possibly multi-byte) UTF-8 encoded character sequences between double quotes, as in `"fox"`. ### New escape sequences Both codepoint and string literals accept the following new escape sequences: 1) Unicode escape sequences Any character with a character code lower than 65536 can be escaped using the hexadecimal value of its character code, prefixed with `\u`. Unicode escapes are six characters long. They require exactly four characters following `\u` . If the hexadecimal character code is only one, two or three characters long, you’ll need to pad it with leading zeroes. Example: `'\u2665'` (Represents ♥) 2) Unicode codepoint escape sequences Any code point or character can be escaped using the hexadecimal value of its character code, prefixed with `\u{` and suffixed with `}` . This allows for code points up to 0x10FFFF, which is the highest code point defined by Unicode. Unicode code point escapes consist of at least five characters. At least one hexadecimal character can be wrapped in `\u{…}` . There is no upper limit on the number of hex digits in use (for example '\u{000000000061}' == 'a') Example: `'\u{2318}'` (Represents ⌘) --- .depend | 26 ++-- Makefile | 3 +- benchmarks/Benchmark.ml | 39 ----- src/res_ast_debugger.ml | 7 +- src/res_cli.ml | 8 +- src/res_core.ml | 66 ++++++-- src/res_printer.ml | 32 +++- src/res_scanner.ml | 82 +++++++++- src/res_token.ml | 4 +- src/res_utf8.ml | 141 ++++++++++++++++++ src/res_utf8.mli | 9 ++ .../parsing/grammar/expressions/constants.res | 16 ++ .../expressions/expected/constants.res.txt | 16 +- tests/printer/other/char.res | 2 + tests/printer/other/expected/char.res.txt | 6 +- tests/printer/other/expected/string.res.txt | 4 + tests/printer/other/string.res | 4 + tests/res_test.ml | 1 + tests/res_utf8_test.ml | 91 +++++++++++ 19 files changed, 471 insertions(+), 86 deletions(-) create mode 100644 src/res_utf8.ml create mode 100644 src/res_utf8.mli create mode 100644 tests/res_utf8_test.ml diff --git a/.depend b/.depend index b93a69e6..3e5cc17a 100644 --- a/.depend +++ b/.depend @@ -12,9 +12,10 @@ src/res_comment.cmx : src/res_comment.cmi src/res_comment.cmi : src/res_comments_table.cmx : src/res_parsetree_viewer.cmx src/res_doc.cmx \ src/res_comment.cmx -src/res_core.cmx : src/res_token.cmx src/res_scanner.cmx src/res_printer.cmx \ - src/res_parser.cmx src/res_js_ffi.cmx src/res_grammar.cmx src/res_doc.cmx \ - src/res_diagnostics.cmx src/res_comments_table.cmx src/res_core.cmi +src/res_core.cmx : src/res_utf8.cmx src/res_token.cmx src/res_scanner.cmx \ + src/res_printer.cmx src/res_parser.cmx src/res_js_ffi.cmx \ + src/res_grammar.cmx src/res_doc.cmx src/res_diagnostics.cmx \ + src/res_comments_table.cmx src/res_core.cmi src/res_core.cmi : src/res_parser.cmi src/res_diagnostics.cmx : src/res_token.cmx src/res_grammar.cmx \ src/res_diagnostics_printing_utils.cmx src/res_diagnostics.cmi @@ -60,16 +61,19 @@ src/res_parser.cmi : src/res_token.cmx src/res_scanner.cmi \ src/res_comment.cmi src/res_parsetree_viewer.cmx : src/res_parsetree_viewer.cmi src/res_parsetree_viewer.cmi : -src/res_printer.cmx : src/res_token.cmx src/res_parsetree_viewer.cmx \ - src/res_parens.cmx src/res_doc.cmx src/res_comments_table.cmx \ - src/res_comment.cmx src/res_printer.cmi +src/res_printer.cmx : src/res_utf8.cmx src/res_token.cmx \ + src/res_parsetree_viewer.cmx src/res_parens.cmx src/res_doc.cmx \ + src/res_comments_table.cmx src/res_comment.cmx src/res_printer.cmi src/res_printer.cmi : src/res_doc.cmi src/res_comments_table.cmx \ src/res_comment.cmi src/res_reporting.cmx : src/res_token.cmx src/res_grammar.cmx -src/res_scanner.cmx : src/res_token.cmx src/res_diagnostics.cmx \ - src/res_comment.cmx src/res_scanner.cmi +src/res_scanner.cmx : src/res_utf8.cmx src/res_token.cmx \ + src/res_diagnostics.cmx src/res_comment.cmx src/res_scanner.cmi src/res_scanner.cmi : src/res_token.cmx src/res_diagnostics.cmi src/res_token.cmx : src/res_comment.cmx -tests/res_test.cmx : src/res_token.cmx src/res_parser.cmx \ - src/res_outcome_printer.cmx src/res_multi_printer.cmx src/res_io.cmx \ - src/res_driver.cmx src/res_core.cmx +src/res_utf8.cmx : src/res_utf8.cmi +src/res_utf8.cmi : +tests/res_test.cmx : tests/res_utf8_test.cmx src/res_token.cmx \ + src/res_parser.cmx src/res_outcome_printer.cmx src/res_multi_printer.cmx \ + src/res_io.cmx src/res_driver.cmx src/res_core.cmx +tests/res_utf8_test.cmx : src/res_utf8.cmx diff --git a/Makefile b/Makefile index ba0ac79c..6bad4ade 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,7 @@ API_FILES = \ src/res_parsetree_viewer.cmx\ src/res_parens.cmx\ src/res_comments_table.cmx\ + src/res_utf8.cmx\ src/res_printer.cmx\ src/res_scanner.cmx\ src/res_js_ffi.cmx\ @@ -40,7 +41,7 @@ API_FILES = \ CLI_FILES = $(API_FILES) src/res_cli.cmx -TEST_FILES = $(API_FILES) tests/res_test.cmx +TEST_FILES = $(API_FILES) tests/res_utf8_test.cmx tests/res_test.cmx .DEFAULT_GOAL := build-native diff --git a/benchmarks/Benchmark.ml b/benchmarks/Benchmark.ml index 3700403a..6727cc64 100644 --- a/benchmarks/Benchmark.ml +++ b/benchmarks/Benchmark.ml @@ -6,7 +6,6 @@ module Printer = Res_printer module IO: sig val readFile: string -> string - val readStdin: unit -> string end = struct (* random chunk size: 2^15, TODO: why do we guess randomly? *) let chunkSize = 32768 @@ -26,21 +25,6 @@ end = struct ) in loop () - - let readStdin () = - let buffer = Buffer.create chunkSize in - let chunk = (Bytes.create [@doesNotRaise]) chunkSize in - let rec loop () = - let len = try input stdin chunk 0 chunkSize with Invalid_argument _ -> 0 in - if len == 0 then ( - close_in_noerr stdin; - Buffer.contents buffer - ) else ( - Buffer.add_subbytes buffer chunk 0 len; - loop () - ) - in - loop () end module Time: sig @@ -188,29 +172,6 @@ end = struct done end -module Profile: sig - val record : name:string -> (unit -> 'a) -> 'a - val print: unit -> unit -end = struct - let state = Hashtbl.create 2 - - let record ~name f = - let startTime = Time.now() in - let result = f() in - let endTime = Time.now() in - - Hashtbl.add state name (Time.diff startTime endTime); - result - - let print () = - let report = Hashtbl.fold (fun k v acc -> - let line = Printf.sprintf "%s: %fms\n" k (Time.print v) in - acc ^ line - ) state "\n\n" - in - print_endline report -end - module Benchmarks: sig val run: unit -> unit end = struct diff --git a/src/res_ast_debugger.ml b/src/res_ast_debugger.ml index 1dbb2d42..212ad106 100644 --- a/src/res_ast_debugger.ml +++ b/src/res_ast_debugger.ml @@ -143,10 +143,13 @@ module SexpAst = struct string txt; optChar tag; ] - | Pconst_char c -> + | Pconst_char _ -> + Sexp.list [ + Sexp.atom "Pconst_char"; + ] + | Pconst_string(_, Some "INTERNAL_RES_CHAR_CONTENTS") -> Sexp.list [ Sexp.atom "Pconst_char"; - Sexp.atom (Char.escaped c); ] | Pconst_string (txt, tag) -> Sexp.list [ diff --git a/src/res_cli.ml b/src/res_cli.ml index e5917bc5..fd92ed00 100644 --- a/src/res_cli.ml +++ b/src/res_cli.ml @@ -165,6 +165,7 @@ module ResClflags: sig val file: string ref val interface: bool ref val ppx: string ref + val typechecker: bool ref val parse: unit -> unit end = struct @@ -176,6 +177,7 @@ end = struct let interface = ref false let ppx = ref "" let file = ref "" + let typechecker = ref false let usage = "\n**This command line is for the repo developer's testing purpose only. DO NOT use it in production**!\n\n" ^ "Usage:\n rescript \n\n" ^ @@ -192,6 +194,7 @@ end = struct ("-width", Arg.Int (fun w -> width := w), "Specify the line length for the printer (formatter)"); ("-interface", Arg.Unit (fun () -> interface := true), "Parse as interface"); ("-ppx", Arg.String (fun txt -> ppx := txt), "Apply a specific built-in ppx before parsing, none or jsx. Default: none"); + ("-typechecker", Arg.Unit (fun () -> typechecker := true), "Parses the ast as it would be passed to the typechecker and not the printer") ] let parse () = Arg.parse spec (fun f -> file := f) usage @@ -200,7 +203,7 @@ end module CliArgProcessor = struct type backend = Parser: ('diagnostics) Res_driver.parsingEngine -> backend [@@unboxed] - let processFile ~isInterface ~width ~recover ~origin ~target ~ppx filename = + let processFile ~isInterface ~width ~recover ~origin ~target ~ppx ~typechecker filename = let len = String.length filename in let processInterface = isInterface || len > 0 && (String.get [@doesNotRaise]) filename (len - 1) = 'i' @@ -233,7 +236,7 @@ module CliArgProcessor = struct in let forPrinter = match target with - | "res" | "sexp" -> true + | "res" | "sexp" when not typechecker -> true | _ -> false in @@ -292,5 +295,6 @@ let [@raises Invalid_argument, Failure, exit] () = ~target:!ResClflags.print ~origin:!ResClflags.origin ~ppx:!ResClflags.ppx + ~typechecker:!ResClflags.typechecker !ResClflags.file end diff --git a/src/res_core.ml b/src/res_core.ml index 94022348..dca637f6 100644 --- a/src/res_core.ml +++ b/src/res_core.ml @@ -143,6 +143,9 @@ type stringLiteralState = | HexEscape | DecimalEscape | OctalEscape + | UnicodeEscape + | UnicodeCodePointEscape + | UnicodeEscapeStart | EscapedLineBreak type typDefOrExt = @@ -482,15 +485,12 @@ let processUnderscoreApplication args = in (args, wrap) -let hexValue x = - match x with - | '0' .. '9' -> - (Char.code x) - 48 - | 'A' .. 'Z' -> - (Char.code x) - 55 - | 'a' .. 'z' -> - (Char.code x) - 97 - | _ -> 16 +let hexValue ch = + match ch with + | '0'..'9' -> (Char.code ch) - 48 + | 'a'..'f' -> (Char.code ch) - (Char.code 'a') + 10 + | 'A'..'F' -> (Char.code ch) + 32 - (Char.code 'a') + 10 + | _ -> 16 (* larger than any legal value *) let parseStringLiteral s = let len = String.length s in @@ -499,7 +499,7 @@ let parseStringLiteral s = let rec parse state i d = if i = len then (match state with - | HexEscape | DecimalEscape | OctalEscape -> false + | HexEscape | DecimalEscape | OctalEscape | UnicodeEscape | UnicodeCodePointEscape -> false | _ -> true) else let c = String.unsafe_get s i in @@ -517,6 +517,7 @@ let parseStringLiteral s = | ('\\' | ' ' | '\'' | '"') as c -> Buffer.add_char b c; parse Start (i + 1) d | 'x' -> parse HexEscape (i + 1) 0 | 'o' -> parse OctalEscape (i + 1) 0 + | 'u' -> parse UnicodeEscapeStart (i + 1) 0 | '0' .. '9' -> parse DecimalEscape i 0 | '\010' | '\013' -> parse EscapedLineBreak (i + 1) d | c -> Buffer.add_char b '\\'; Buffer.add_char b c; parse Start (i + 1) d) @@ -558,6 +559,45 @@ let parseStringLiteral s = ) else parse OctalEscape (i + 1) (d + 1) + | UnicodeEscapeStart -> + (match c with + | '{' -> parse UnicodeCodePointEscape (i + 1) 0 + | _ -> parse UnicodeEscape (i + 1) 1) + | UnicodeEscape -> + if d == 3 then + let c0 = String.unsafe_get s (i - 3) in + let c1 = String.unsafe_get s (i - 2) in + let c2 = String.unsafe_get s (i - 1) in + let c3 = String.unsafe_get s i in + let c = (4096 * (hexValue c0)) + (256 * (hexValue c1)) + (16 * (hexValue c2)) + (hexValue c3) in + if Res_utf8.isValidCodePoint c then ( + let codePoint = Res_utf8.encodeCodePoint c in + Buffer.add_string b codePoint; + parse Start (i + 1) 0 + ) else ( + false + ) + else + parse UnicodeEscape (i + 1) (d + 1) + | UnicodeCodePointEscape -> + (match c with + | '0'..'9' | 'a'..'f' | 'A'.. 'F' -> + parse UnicodeCodePointEscape (i + 1) (d + 1) + | '}' -> + let x = ref 0 in + for remaining = d downto 1 do + let ix = i - remaining in + x := (!x * 16) + (hexValue (String.unsafe_get s ix)); + done; + let c = !x in + if Res_utf8.isValidCodePoint c then ( + let codePoint = Res_utf8.encodeCodePoint !x in + Buffer.add_string b codePoint; + parse Start (i + 1) 0 + ) else ( + false + ) + | _ -> false) | EscapedLineBreak -> (match c with | ' ' | '\t' -> parse EscapedLineBreak (i + 1) d @@ -877,7 +917,11 @@ let parseConstant p = s in Pconst_string(txt, None) - | Character c -> Pconst_char c + | Character {c; original} -> + if p.mode = ParseForTypeChecker then + Pconst_char c + else + Pconst_string (original, Some "INTERNAL_RES_CHAR_CONTENTS") | token -> Parser.err p (Diagnostics.unexpected token p.breadcrumbs); Pconst_string("", None) diff --git a/src/res_printer.ml b/src/res_printer.ml index edd92d32..5685e213 100644 --- a/src/res_printer.ml +++ b/src/res_printer.ml @@ -508,14 +508,32 @@ let printConstant c = match c with Doc.text "\""; ] | Pconst_string (txt, Some prefix) -> - Doc.concat [ - if prefix = "js" then Doc.nil else Doc.text prefix; - Doc.text "`"; - printStringContents txt; - Doc.text "`"; - ] + if prefix = "INTERNAL_RES_CHAR_CONTENTS" then + Doc.concat [Doc.text "'"; Doc.text txt; Doc.text "'"] + else + Doc.concat [ + if prefix = "js" then Doc.nil else Doc.text prefix; + Doc.text "`"; + printStringContents txt; + Doc.text "`"; + ] | Pconst_float (s, _) -> Doc.text s - | Pconst_char c -> Doc.text ("'" ^ (Char.escaped c) ^ "'") + | Pconst_char c -> + let str = match c with + | '\'' -> "\\'" + | '\\' -> "\\\\" + | '\n' -> "\\n" + | '\t' -> "\\t" + | '\r' -> "\\r" + | '\b' -> "\\b" + | ' ' .. '~' as c -> + let s = (Bytes.create [@doesNotRaise]) 1 in + Bytes.unsafe_set s 0 c; + Bytes.unsafe_to_string s + | c -> + Res_utf8.encodeCodePoint (Obj.magic c) + in + Doc.text ("'" ^ str ^ "'") let rec printStructure (s : Parsetree.structure) t = match s with diff --git a/src/res_scanner.ml b/src/res_scanner.ml index b6855f6c..8357d034 100644 --- a/src/res_scanner.ml +++ b/src/res_scanner.ml @@ -319,9 +319,9 @@ let scanStringEscapeSequence ~startPos scanner = loop (n - 1) (x * base + d) in let x = loop n 0 in - if x > max then + if x > max || 0xD800 <= x && x < 0xE000 then let pos = position scanner in - let msg = "invalid escape sequence (value too high)" in + let msg = "escape sequence is invalid unicode code point" in scanner.err ~startPos ~endPos:pos (Diagnostics.message msg) in match scanner.ch with @@ -339,6 +339,24 @@ let scanStringEscapeSequence ~startPos scanner = (* hex *) next scanner; scan ~n:2 ~base:16 ~max:255 + | 'u' -> + next scanner; + (match scanner.ch with + | '{' -> + (* unicode code point escape sequence: '\u{7A}', one or more hex digits *) + next scanner; + let x = ref 0 in + while match scanner.ch with | '0'..'9' | 'a'..'f' | 'A'..'F' -> true | _ -> false do + x := (!x * 16) + (digitValue scanner.ch); + next scanner + done; + (* consume '}' in '\u{7A}' *) + (match scanner.ch with + | '}' -> next scanner + | _ -> ()); + | _ -> + scan ~n:4 ~base:16 ~max:Res_utf8.max + ) | _ -> (* unknown escape sequence * TODO: we should warn the user here. Let's not make it a hard error for now, for reason compat *) @@ -381,6 +399,8 @@ let scanString scanner = Token.String (scan ()) let scanEscape scanner = + (* '\' consumed *) + let offset = scanner.offset - 1 in let convertNumber scanner ~n ~base = let x = ref 0 in for _ = n downto 1 do @@ -388,10 +408,13 @@ let scanEscape scanner = x := (!x * base) + d; next scanner done; - (Char.chr [@doesNotRaise]) !x + let c = !x in + if Res_utf8.isValidCodePoint c then + Char.unsafe_chr c + else + Char.unsafe_chr Res_utf8.repl in - (* let offset = scanner.offset in *) - let c = match scanner.ch with + let codepoint = match scanner.ch with | '0'..'9' -> convertNumber scanner ~n:3 ~base:10 | 'b' -> next scanner; '\008' | 'n' -> next scanner; '\010' @@ -399,11 +422,36 @@ let scanEscape scanner = | 't' -> next scanner; '\009' | 'x' -> next scanner; convertNumber scanner ~n:2 ~base:16 | 'o' -> next scanner; convertNumber scanner ~n:3 ~base:8 + | 'u' -> + next scanner; + begin match scanner.ch with + | '{' -> + (* unicode code point escape sequence: '\u{7A}', one or more hex digits *) + next scanner; + let x = ref 0 in + while match scanner.ch with | '0'..'9' | 'a'..'f' | 'A'..'F' -> true | _ -> false do + x := (!x * 16) + (digitValue scanner.ch); + next scanner + done; + (* consume '}' in '\u{7A}' *) + (match scanner.ch with + | '}' -> next scanner + | _ -> ()); + let c = !x in + if Res_utf8.isValidCodePoint c then + Char.unsafe_chr c + else + Char.unsafe_chr Res_utf8.repl + | _ -> + (* unicode escape sequence: '\u007A', exactly 4 hex digits *) + convertNumber scanner ~n:4 ~base:16 + end | ch -> next scanner; ch in + let contents = (String.sub [@doesNotRaise]) scanner.src offset (scanner.offset - offset) in next scanner; (* Consume \' *) (* TODO: do we know it's \' ? *) - Token.Character c + Token.Character {c = codepoint; original = contents} let scanSingleLineComment scanner = let startOff = scanner.offset in @@ -613,8 +661,26 @@ let rec scan scanner = then relying on matching on the quote *) next scanner; SingleQuote | '\\', _ -> next2 scanner; scanEscape scanner - | ch, '\'' -> next3 scanner; Token.Character ch - | _ -> next scanner; SingleQuote) + | ch, '\'' -> + let offset = scanner.offset + 1 in + next3 scanner; + Token.Character {c = ch; original = (String.sub [@doesNotRaise]) scanner.src offset 1} + | ch, _ -> + next scanner; + let offset = scanner.offset in + let (codepoint, length) = Res_utf8.decodeCodePoint scanner.offset scanner.src (String.length scanner.src) in + for _ = 0 to length - 1 do + next scanner + done; + if scanner.ch = '\'' then ( + let contents = (String.sub [@doesNotRaise]) scanner.src offset length in + next scanner; + Token.Character {c = Obj.magic codepoint; original = contents} + ) else ( + scanner.ch <- ch; + scanner.offset <- offset; + SingleQuote + )) | '!' -> (match peek scanner, peek2 scanner with | '=', '=' -> next3 scanner; Token.BangEqualEqual diff --git a/src/res_token.ml b/src/res_token.ml index a14491dd..fed60e42 100644 --- a/src/res_token.ml +++ b/src/res_token.ml @@ -3,7 +3,7 @@ module Comment = Res_comment type t = | Open | True | False - | Character of char + | Character of {c: char; original: string} | Int of {i: string; suffix: char option} | Float of {f: string; suffix: char option} | String of string @@ -88,7 +88,7 @@ let precedence = function let toString = function | Open -> "open" | True -> "true" | False -> "false" - | Character c -> "character '" ^ (Char.escaped c) ^ "'" + | Character {original} -> "character '" ^ original ^ "'" | String s -> "string \"" ^ s ^ "\"" | Lident str -> str | Uident str -> str diff --git a/src/res_utf8.ml b/src/res_utf8.ml new file mode 100644 index 00000000..a8fd99ee --- /dev/null +++ b/src/res_utf8.ml @@ -0,0 +1,141 @@ +(* https://tools.ietf.org/html/rfc3629#section-10 *) +(* let bom = 0xFEFF *) + +let repl = 0xFFFD + +(* let min = 0x0000 *) +let max = 0x10FFFF + +let surrogateMin = 0xD800 +let surrogateMax = 0xDFFF + +(* + * Char. number range | UTF-8 octet sequence + * (hexadecimal) | (binary) + * --------------------+--------------------------------------------- + * 0000 0000-0000 007F | 0xxxxxxx + * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx + * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx + * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + *) +let h2 = 0b1100_0000 +let h3 = 0b1110_0000 +let h4 = 0b1111_0000 + +let cont_mask = 0b0011_1111 + +type category = { + low: int; + high: int; + size: int; +} + +let locb = 0b1000_0000 +let hicb = 0b1011_1111 + +let categoryTable = [| +(* 0 *) {low = -1; high= -1; size= 1}; (* invalid *) +(* 1 *) {low = 1; high= -1; size= 1}; (* ascii *) +(* 2 *) {low = locb; high= hicb; size= 2}; +(* 3 *) {low = 0xA0; high= hicb; size= 3}; +(* 4 *) {low = locb; high= hicb; size= 3}; +(* 5 *) {low = locb; high= 0x9F; size= 3}; +(* 6 *) {low = 0x90; high= hicb; size= 4}; +(* 7 *) {low = locb; high= hicb; size= 4}; +(* 8 *) {low = locb; high= 0x8F; size= 4}; + +|] + +let categories = [| + 1; 1; 1; 1; 1; 1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1; + 1; 1; 1; 1; 1; 1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1; + 1; 1; 1; 1; 1; 1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1; + 1; 1; 1; 1; 1; 1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1; + 1; 1; 1; 1; 1; 1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1; + 1; 1; 1; 1; 1; 1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1; + 1; 1; 1; 1; 1; 1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1; + 1; 1; 1; 1; 1; 1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1 ;1; + + 0; 0; 0; 0;0; 0; 0; 0;0; 0; 0; 0;0; 0; 0; 0; + 0; 0; 0; 0;0; 0; 0; 0;0; 0; 0; 0;0; 0; 0; 0; + 0; 0; 0; 0;0; 0; 0; 0;0; 0; 0; 0;0; 0; 0; 0; + 0; 0; 0; 0;0; 0; 0; 0;0; 0; 0; 0;0; 0; 0; 0; + (* surrogate range U+D800 - U+DFFFF = 55296 - 917503 *) + 0; 0; 2; 2;2; 2; 2; 2;2; 2; 2; 2;2; 2; 2; 2; + 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; + 3; 4; 4; 4; 4; 4; 4; 4; 4; 4; 4; 4; 4; 5; 4; 4; + 6; 7; 7 ;7; 8; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; +|] + +let decodeCodePoint i s len = + if len < 1 then (repl, 1) else + let first = int_of_char (String.unsafe_get s i) in + if first < 128 then (first, 1) else + let index = Array.unsafe_get categories first in + if index = 0 then + (repl, 1) + else + let cat = Array.unsafe_get categoryTable index in + if len < i + cat.size then + (repl, 1) + else if cat.size == 2 then + let c1 = int_of_char (String.unsafe_get s (i + 1)) in + if c1 < cat.low || cat.high < c1 then + (repl, 1) + else + let i1 = c1 land 0b00111111 in + let i0 = (first land 0b00011111) lsl 6 in + let uc = i0 lor i1 in + (uc, 2) + else if cat.size == 3 then + let c1 = int_of_char (String.unsafe_get s (i + 1)) in + let c2 = int_of_char (String.unsafe_get s (i + 2)) in + if c1 < cat.low || cat.high < c1 || c2 < locb || hicb < c2 then (repl, 1) + else + let i0 = (first land 0b00001111) lsl 12 in + let i1 = (c1 land 0b00111111) lsl 6 in + let i2 = (c2 land 0b00111111) in + let uc = i0 lor i1 lor i2 in + (uc, 3) + else + let c1 = int_of_char (String.unsafe_get s (i +1)) in + let c2 = int_of_char (String.unsafe_get s (i +2)) in + let c3 = int_of_char (String.unsafe_get s (i +3)) in + if c1 < cat.low || cat.high < c1 || + c2 < locb || hicb < c2 || c3 < locb || hicb < c3 + then (repl, 1) + else + let i1 = (c1 land 0x3f) lsl 12 in + let i2 = (c2 land 0x3f) lsl 6 in + let i3 = (c3 land 0x3f) in + let i0 = (first land 0x07) lsl 18 in + let uc = i0 lor i3 lor i2 lor i1 in + (uc, 4) + +let encodeCodePoint c = + if c <= 127 then ( + let bytes = (Bytes.create [@doesNotRaise]) 1 in + Bytes.unsafe_set bytes 0 (Char.unsafe_chr c); + Bytes.unsafe_to_string bytes + ) else if c <= 2047 then ( + let bytes = (Bytes.create [@doesNotRaise]) 2 in + Bytes.unsafe_set bytes 0 (Char.unsafe_chr (h2 lor (c lsr 6))); + Bytes.unsafe_set bytes 1 (Char.unsafe_chr (0b1000_0000 lor (c land cont_mask))); + Bytes.unsafe_to_string bytes + ) else if c <= 65535 then ( + let bytes = (Bytes.create [@doesNotRaise]) 3 in + Bytes.unsafe_set bytes 0 (Char.unsafe_chr (h3 lor (c lsr 12))); + Bytes.unsafe_set bytes 1 (Char.unsafe_chr (0b1000_0000 lor ((c lsr 6) land cont_mask))); + Bytes.unsafe_set bytes 2 (Char.unsafe_chr (0b1000_0000 lor (c land cont_mask))); + Bytes.unsafe_to_string bytes + ) else (* if c <= max then *) ( + let bytes = (Bytes.create [@doesNotRaise]) 4 in + Bytes.unsafe_set bytes 0 (Char.unsafe_chr (h4 lor (c lsr 18))); + Bytes.unsafe_set bytes 1 (Char.unsafe_chr (0b1000_0000 lor ((c lsr 12) land cont_mask))); + Bytes.unsafe_set bytes 2 (Char.unsafe_chr (0b1000_0000 lor ((c lsr 6) land cont_mask))); + Bytes.unsafe_set bytes 3 (Char.unsafe_chr (0b1000_0000 lor (c land cont_mask))); + Bytes.unsafe_to_string bytes + ) + +let isValidCodePoint c = + 0 <= c && c < surrogateMin || surrogateMax < c && c <= max diff --git a/src/res_utf8.mli b/src/res_utf8.mli new file mode 100644 index 00000000..4b7462a4 --- /dev/null +++ b/src/res_utf8.mli @@ -0,0 +1,9 @@ +val repl: int + +val max: int + +val decodeCodePoint: int -> string -> int -> int * int + +val encodeCodePoint: int -> string + +val isValidCodePoint: int -> bool diff --git a/tests/parsing/grammar/expressions/constants.res b/tests/parsing/grammar/expressions/constants.res index 867e4bc7..0d913de0 100644 --- a/tests/parsing/grammar/expressions/constants.res +++ b/tests/parsing/grammar/expressions/constants.res @@ -75,3 +75,19 @@ let x = "\tbar" let x = "\bbar" let x = "\rbar" let x = "\ bar" +let x = "\u00A9" +let x = "\u00a9" +let x = "\u2665" +let a = "\u{000000000061}" +let x = "\u{00A9}" +let x = "\u{00a9}" +let smile = "\u{1F600}" +let smile = "\u{1f600}" +let smile = "\240\159\152\128" + +// represent the same thing +let u = "日本語" +let u = "\u65e5\u672c\u8a9e" +let u = "\u{000065e5}\u{0000672c}\u{00008a9e}" +let u = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e" +let u = "\230\151\165\230\156\172\232\170\158" diff --git a/tests/parsing/grammar/expressions/expected/constants.res.txt b/tests/parsing/grammar/expressions/expected/constants.res.txt index ef85a46e..37de3805 100644 --- a/tests/parsing/grammar/expressions/expected/constants.res.txt +++ b/tests/parsing/grammar/expressions/expected/constants.res.txt @@ -58,4 +58,18 @@ let x = "\nbar" let x = "\tbar" let x = "\bbar" let x = "\rbar" -let x = " bar" \ No newline at end of file +let x = " bar" +let x = "\194\169" +let x = "\194\169" +let x = "\226\153\165" +let a = "a" +let x = "\194\169" +let x = "\194\169" +let smile = "\240\159\152\128" +let smile = "\240\159\152\128" +let smile = "\240\159\152\128" +let u = "\230\151\165\230\156\172\232\170\158" +let u = "\230\151\165\230\156\172\232\170\158" +let u = "\230\151\165\230\156\172\232\170\158" +let u = "\230\151\165\230\156\172\232\170\158" +let u = "\230\151\165\230\156\172\232\170\158" \ No newline at end of file diff --git a/tests/printer/other/char.res b/tests/printer/other/char.res index a2db5895..47ba3a53 100644 --- a/tests/printer/other/char.res +++ b/tests/printer/other/char.res @@ -9,3 +9,5 @@ let x = ' ' let x = '\o021' let x = '\xAA' let x = '\179' +let heart = '\u2665' +let smile2 = '\u{1F600}' diff --git a/tests/printer/other/expected/char.res.txt b/tests/printer/other/expected/char.res.txt index 86d1b1a3..47ba3a53 100644 --- a/tests/printer/other/expected/char.res.txt +++ b/tests/printer/other/expected/char.res.txt @@ -6,6 +6,8 @@ let x = '\t' let x = '\b' let x = '\r' let x = ' ' -let x = '\017' -let x = '\170' +let x = '\o021' +let x = '\xAA' let x = '\179' +let heart = '\u2665' +let smile2 = '\u{1F600}' diff --git a/tests/printer/other/expected/string.res.txt b/tests/printer/other/expected/string.res.txt index 79f8f27f..2c2b2fc9 100644 --- a/tests/printer/other/expected/string.res.txt +++ b/tests/printer/other/expected/string.res.txt @@ -9,3 +9,7 @@ let s = "a double escaped \\ test" let s = "what happens here \\n" let s = "\123 \o111 \xA0" + +let heart = "\u2665" + +let smile = "emoji: \u{1F600}" diff --git a/tests/printer/other/string.res b/tests/printer/other/string.res index 79f8f27f..2c2b2fc9 100644 --- a/tests/printer/other/string.res +++ b/tests/printer/other/string.res @@ -9,3 +9,7 @@ let s = "a double escaped \\ test" let s = "what happens here \\n" let s = "\123 \o111 \xA0" + +let heart = "\u2665" + +let smile = "emoji: \u{1F600}" diff --git a/tests/res_test.ml b/tests/res_test.ml index 7ca12bc6..37ef9bd4 100644 --- a/tests/res_test.ml +++ b/tests/res_test.ml @@ -177,3 +177,4 @@ end let () = OutcomePrinterTests.run() let () = ParserApiTest.run() +let () = Res_utf8_test.run() diff --git a/tests/res_utf8_test.ml b/tests/res_utf8_test.ml new file mode 100644 index 00000000..7d5172ce --- /dev/null +++ b/tests/res_utf8_test.ml @@ -0,0 +1,91 @@ +type utf8Test = { + codepoint: int; + str: string; + size: int; +} + +let utf8CodePointTests = [| + {codepoint = 0x00; str = "\x00"; size = 1}; + {codepoint = 0x01; str = "\x01"; size = 1}; + {codepoint = 0x7e; str = "\x7e"; size = 1}; + {codepoint = 0x7f; str = "\x7f"; size = 1}; + {codepoint = 0x0080; str = "\xc2\x80"; size = 2}; + {codepoint = 0x0081; str = "\xc2\x81"; size = 2}; + {codepoint = 0x00bf; str = "\xc2\xbf"; size = 2}; + {codepoint = 0x00c0; str = "\xc3\x80"; size = 2}; + {codepoint = 0x00c1; str = "\xc3\x81"; size = 2}; + {codepoint = 0x00c8; str = "\xc3\x88"; size = 2}; + {codepoint = 0x00d0; str = "\xc3\x90"; size = 2}; + {codepoint = 0x00e0; str = "\xc3\xa0"; size = 2}; + {codepoint = 0x00f0; str = "\xc3\xb0"; size = 2}; + {codepoint = 0x00f8; str = "\xc3\xb8"; size = 2}; + {codepoint = 0x00ff; str = "\xc3\xbf"; size = 2}; + {codepoint = 0x0100; str = "\xc4\x80"; size = 2}; + {codepoint = 0x07ff; str = "\xdf\xbf"; size = 2}; + {codepoint = 0x0400; str = "\xd0\x80"; size = 2}; + {codepoint = 0x0800; str = "\xe0\xa0\x80"; size = 3}; + {codepoint = 0x0801; str = "\xe0\xa0\x81"; size = 3}; + {codepoint = 0x1000; str = "\xe1\x80\x80"; size = 3}; + {codepoint = 0xd000; str = "\xed\x80\x80"; size = 3}; + {codepoint = 0xd7ff; str = "\xed\x9f\xbf"; size = 3}; + {codepoint = 0xe000; str = "\xee\x80\x80"; size = 3}; + {codepoint = 0xfffe; str = "\xef\xbf\xbe"; size = 3}; + {codepoint = 0xffff; str = "\xef\xbf\xbf"; size = 3}; + {codepoint = 0x10000; str = "\xf0\x90\x80\x80"; size = 4}; + {codepoint = 0x10001; str = "\xf0\x90\x80\x81"; size = 4}; + {codepoint = 0x40000; str = "\xf1\x80\x80\x80"; size = 4}; + {codepoint = 0x10fffe; str = "\xf4\x8f\xbf\xbe"; size = 4}; + {codepoint = 0x10ffff; str = "\xf4\x8f\xbf\xbf"; size = 4}; + {codepoint = 0xFFFD; str = "\xef\xbf\xbd"; size = 3} +|] + +let surrogateRange = [| + {codepoint = 0xFFFD; str = "\xed\xa0\x80"; size = 1}; + {codepoint = 0xFFFD; str = "\xed\xbf\xbf"; size = 1}; +|] + +let testDecode () = + Array.iter (fun t -> + let len = String.length t.str in + let (codepoint, size) = Res_utf8.decodeCodePoint 0 t.str len in + assert (codepoint = t.codepoint); + assert (size = t.size) +) utf8CodePointTests + +let testDecodeSurrogateRange () = + Array.iter (fun t -> + let len = String.length t.str in + let (codepoint, size) = Res_utf8.decodeCodePoint 0 t.str len in + assert (codepoint = t.codepoint); + assert (size = t.size) +) surrogateRange + +let testEncode () = + Array.iter (fun t -> + let encodedString = Res_utf8.encodeCodePoint t.codepoint in + assert (encodedString = t.str) +) utf8CodePointTests + +let validCodePointsTests = [| + (0, true); + (Char.code 'e', true); + (Res_utf8.max, true); + (0xD7FF, true); + (0xD800, false); + (0xDFFF, false); + (0xE000, true); + (Res_utf8.max + 1, false); + (-1, false); +|] + +let testIsValidCodePoint () = + Array.iter (fun (codePoint, t) -> + assert ((Res_utf8.isValidCodePoint codePoint) = t) + ) validCodePointsTests + +let run () = + testDecode(); + testDecodeSurrogateRange(); + testEncode(); + testIsValidCodePoint(); + print_endline "✅ utf8 tests" From a6a6d717cdd367ea299d5d585adb7c1d7437502f Mon Sep 17 00:00:00 2001 From: Iwan Date: Fri, 4 Jun 2021 18:38:42 +0200 Subject: [PATCH 2/4] Rename Character token to Codepoint token. Codepoint makes more sense with unicode --- src/res_core.ml | 6 +++--- src/res_grammar.ml | 10 +++++----- src/res_scanner.ml | 6 +++--- src/res_token.ml | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/res_core.ml b/src/res_core.ml index dca637f6..844a4f1c 100644 --- a/src/res_core.ml +++ b/src/res_core.ml @@ -917,7 +917,7 @@ let parseConstant p = s in Pconst_string(txt, None) - | Character {c; original} -> + | Codepoint {c; original} -> if p.mode = ParseForTypeChecker then Pconst_char c else @@ -1113,7 +1113,7 @@ let rec parsePattern ?(alias=true) ?(or_=true) p = let loc = mkLoc startPos endPos in Ast_helper.Pat.construct ~loc (Location.mkloc (Longident.Lident (Token.toString token)) loc) None - | Int _ | String _ | Float _ | Character _ | Minus | Plus -> + | Int _ | String _ | Float _ | Codepoint _ | Minus | Plus -> let c = parseConstant p in begin match p.token with | DotDot -> @@ -1858,7 +1858,7 @@ and parseAtomicExpr p = let loc = mkLoc startPos p.prevEndPos in Ast_helper.Exp.construct ~loc (Location.mkloc (Longident.Lident (Token.toString token)) loc) None - | Int _ | String _ | Float _ | Character _ -> + | Int _ | String _ | Float _ | Codepoint _ -> let c = parseConstant p in let loc = mkLoc startPos p.prevEndPos in Ast_helper.Exp.constant ~loc c diff --git a/src/res_grammar.ml b/src/res_grammar.ml index 394bdd96..ac649a1e 100644 --- a/src/res_grammar.ml +++ b/src/res_grammar.ml @@ -134,7 +134,7 @@ let isSignatureItemStart = function | _ -> false let isAtomicPatternStart = function - | Token.Int _ | String _ | Character _ | Backtick + | Token.Int _ | String _ | Codepoint _ | Backtick | Lparen | Lbracket | Lbrace | Underscore | Lident _ | Uident _ | List @@ -144,7 +144,7 @@ let isAtomicPatternStart = function let isAtomicExprStart = function | Token.True | False - | Int _ | String _ | Float _ | Character _ + | Int _ | String _ | Float _ | Codepoint _ | Backtick | Uident _ | Lident _ | Hash | Lparen @@ -165,7 +165,7 @@ let isAtomicTypExprStart = function let isExprStart = function | Token.True | False - | Int _ | String _ | Float _ | Character _ | Backtick + | Int _ | String _ | Float _ | Codepoint _ | Backtick | Underscore (* _ => doThings() *) | Uident _ | Lident _ | Hash | Lparen | List | Module | Lbracket | Lbrace @@ -194,7 +194,7 @@ let isStructureItemStart = function | _ -> false let isPatternStart = function - | Token.Int _ | Float _ | String _ | Character _ | Backtick | True | False | Minus | Plus + | Token.Int _ | Float _ | String _ | Codepoint _ | Backtick | True | False | Minus | Plus | Lparen | Lbracket | Lbrace | List | Underscore | Lident _ | Uident _ | Hash @@ -301,7 +301,7 @@ let isJsxChildStart = isAtomicExprStart let isBlockExprStart = function | Token.At | Hash | Percent | Minus | MinusDot | Plus | PlusDot | Bang - | True | False | Float _ | Int _ | String _ | Character _ | Lident _ | Uident _ + | True | False | Float _ | Int _ | String _ | Codepoint _ | Lident _ | Uident _ | Lparen | List | Lbracket | Lbrace | Forwardslash | Assert | Lazy | If | For | While | Switch | Open | Module | Exception | Let | LessThan | Backtick | Try | Underscore -> true diff --git a/src/res_scanner.ml b/src/res_scanner.ml index 8357d034..f0180517 100644 --- a/src/res_scanner.ml +++ b/src/res_scanner.ml @@ -451,7 +451,7 @@ let scanEscape scanner = let contents = (String.sub [@doesNotRaise]) scanner.src offset (scanner.offset - offset) in next scanner; (* Consume \' *) (* TODO: do we know it's \' ? *) - Token.Character {c = codepoint; original = contents} + Token.Codepoint {c = codepoint; original = contents} let scanSingleLineComment scanner = let startOff = scanner.offset in @@ -664,7 +664,7 @@ let rec scan scanner = | ch, '\'' -> let offset = scanner.offset + 1 in next3 scanner; - Token.Character {c = ch; original = (String.sub [@doesNotRaise]) scanner.src offset 1} + Token.Codepoint {c = ch; original = (String.sub [@doesNotRaise]) scanner.src offset 1} | ch, _ -> next scanner; let offset = scanner.offset in @@ -675,7 +675,7 @@ let rec scan scanner = if scanner.ch = '\'' then ( let contents = (String.sub [@doesNotRaise]) scanner.src offset length in next scanner; - Token.Character {c = Obj.magic codepoint; original = contents} + Token.Codepoint {c = Obj.magic codepoint; original = contents} ) else ( scanner.ch <- ch; scanner.offset <- offset; diff --git a/src/res_token.ml b/src/res_token.ml index fed60e42..b901276a 100644 --- a/src/res_token.ml +++ b/src/res_token.ml @@ -3,7 +3,7 @@ module Comment = Res_comment type t = | Open | True | False - | Character of {c: char; original: string} + | Codepoint of {c: char; original: string} | Int of {i: string; suffix: char option} | Float of {f: string; suffix: char option} | String of string @@ -88,7 +88,7 @@ let precedence = function let toString = function | Open -> "open" | True -> "true" | False -> "false" - | Character {original} -> "character '" ^ original ^ "'" + | Codepoint {original} -> "codepoint '" ^ original ^ "'" | String s -> "string \"" ^ s ^ "\"" | Lident str -> str | Uident str -> str From 051abc1cb080bae47e3390dd92b40a21abb8460c Mon Sep 17 00:00:00 2001 From: Iwan Date: Fri, 4 Jun 2021 18:52:57 +0200 Subject: [PATCH 3/4] Add comment about codepoint literal encoding for printer. --- src/res_core.ml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/res_core.ml b/src/res_core.ml index 844a4f1c..15cd1a26 100644 --- a/src/res_core.ml +++ b/src/res_core.ml @@ -921,6 +921,9 @@ let parseConstant p = if p.mode = ParseForTypeChecker then Pconst_char c else + (* Pconst_char char does not have enough information for formatting. + * When parsing for the printer, we encode the char contents as a string + * with a special prefix. *) Pconst_string (original, Some "INTERNAL_RES_CHAR_CONTENTS") | token -> Parser.err p (Diagnostics.unexpected token p.breadcrumbs); From f5166a0463792ba57d255f3922c70af1153ea3ec Mon Sep 17 00:00:00 2001 From: Iwan Date: Fri, 20 Aug 2021 16:38:07 +0200 Subject: [PATCH 4/4] 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 --- Makefile | 2 +- src/res_ast_conversion.ml | 6 +- src/res_core.ml | 38 ++--- src/res_parsetree_viewer.ml | 24 +-- src/res_parsetree_viewer.mli | 1 + src/res_printer.ml | 16 +- .../reason/expected/bracedJsx.re.txt | 2 +- .../expected/ambiguousArrow.res.txt | 4 +- .../expressions/expected/emptyeof.res.txt | 3 +- .../errors/expressions/expected/ifLet.res.txt | 8 +- .../expected/taggedTemplateLiterals.res.txt | 2 +- .../expected/unexpectedConstraint.res.txt | 2 +- .../other/expected/breadcrumbs170.res.txt | 2 +- .../errors/pattern/expected/missing.res.txt | 2 +- .../pattern/expected/templateLiteral.res.txt | 7 +- .../scanner/expected/escapeSequence.res.txt | 4 +- .../scanner/expected/exoticIdent.res.txt | 3 +- .../scanner/expected/unclosedString.res.txt | 3 +- .../errors/structure/expected/gh16A.res.txt | 2 +- .../errors/structure/expected/gh16B.res.txt | 18 ++- .../errors/typexpr/expected/garbage.res.txt | 2 +- .../expressions/expected/argument.res.txt | 4 +- .../expressions/expected/arrow.res.txt | 8 +- .../expressions/expected/block.res.txt | 15 +- .../expressions/expected/bsObject.res.txt | 4 +- .../expressions/expected/constants.res.txt | 57 +++---- .../expressions/expected/es6template.res.txt | 123 ++++++++++---- .../expressions/expected/extension.res.txt | 4 +- .../expected/firstClassModule.res.txt | 20 +-- .../expressions/expected/infix.res.txt | 2 +- .../grammar/expressions/expected/jsx.res.txt | 151 +++++++++--------- .../expected/parenthesized.res.txt | 14 +- .../expressions/expected/polyvariant.res.txt | 4 +- .../expressions/expected/switch.res.txt | 6 +- .../grammar/expressions/expected/try.res.txt | 5 +- .../grammar/ffi/expected/export.res.txt | 10 +- .../modtype/expected/extension.res.txt | 2 +- .../grammar/pattern/expected/constant.res.txt | 86 +++++----- .../pattern/expected/constructor.res.txt | 4 +- .../pattern/expected/extension.res.txt | 14 +- .../signature/expected/include.res.txt | 2 +- .../signature/expected/itemExtension.res.txt | 2 +- .../structure/expected/itemExtension.res.txt | 2 +- .../expected/modExprExtension.res.txt | 2 +- .../expected/moduleTypeExtension.res.txt | 2 +- .../expected/standaloneAttribute.res.txt | 2 +- .../expected/privateTypeEquation.res.txt | 2 +- .../grammar/typexpr/expected/alias.res.txt | 4 +- .../typexpr/expected/extension.res.txt | 4 +- .../expected/objectTypeSpreading.res.txt | 9 +- .../expected/jsxChildren.res.txt | 2 +- .../expected/nonRecTypes.res.txt | 38 ++--- .../parsing/other/expected/attributes.res.txt | 4 +- .../recovery/expression/expected/if.res.txt | 2 +- .../recovery/expression/expected/list.res.txt | 4 +- .../recovery/string/expected/emptyeof.res.txt | 3 +- .../recovery/string/expected/eof.res.txt | 3 +- .../string/expected/es6template.res.txt | 5 +- .../recovery/string/expected/unclosed.res.txt | 2 +- .../expr/expected/templateLiteral.res.txt | 2 +- 60 files changed, 447 insertions(+), 333 deletions(-) diff --git a/Makefile b/Makefile index 6bad4ade..5480e4a8 100644 --- a/Makefile +++ b/Makefile @@ -75,7 +75,7 @@ test: reanalyze build-native lib/test.exe ./lib/test.exe ./test.sh -roundtrip-test: reanalyze bootstrap lib/test.exe +roundtrip-test: reanalyze lib/test.exe ./lib/test.exe ROUNDTRIP_TEST=1 ./test.sh diff --git a/src/res_ast_conversion.ml b/src/res_ast_conversion.ml index 20eba5ff..39a2029e 100644 --- a/src/res_ast_conversion.ml +++ b/src/res_ast_conversion.ml @@ -323,6 +323,8 @@ let hasUncurriedAttribute attrs = List.exists (fun attr -> match attr with | _ -> false ) attrs +let templateLiteralAttr = (Location.mknoloc "res.template", Parsetree.PStr []) + let normalize = let open Ast_mapper in { default_mapper with @@ -368,7 +370,7 @@ let normalize = in let s = Parsetree.Pconst_string ((escapeTemplateLiteral txt), newTag) in {p with - ppat_attributes = mapper.attributes mapper p.ppat_attributes; + ppat_attributes = templateLiteralAttr::(mapper.attributes mapper p.ppat_attributes); ppat_desc = Ppat_constant s } | _ -> @@ -396,7 +398,7 @@ let normalize = in let s = Parsetree.Pconst_string ((escapeTemplateLiteral txt), newTag) in {expr with - pexp_attributes = mapper.attributes mapper expr.pexp_attributes; + pexp_attributes= templateLiteralAttr::(mapper.attributes mapper expr.pexp_attributes); pexp_desc = Pexp_constant s } | Pexp_apply ( diff --git a/src/res_core.ml b/src/res_core.ml index 15cd1a26..413d3115 100644 --- a/src/res_core.ml +++ b/src/res_core.ml @@ -136,6 +136,7 @@ let ternaryAttr = (Location.mknoloc "ns.ternary", Parsetree.PStr []) let ifLetAttr = (Location.mknoloc "ns.iflet", Parsetree.PStr []) let suppressFragileMatchWarningAttr = (Location.mknoloc "warning", Parsetree.PStr [Ast_helper.Str.eval (Ast_helper.Exp.constant (Pconst_string ("-4", None)))]) let makeBracesAttr loc = (Location.mkloc "ns.braces" loc, Parsetree.PStr []) +let templateLiteralAttr = (Location.mknoloc "res.template", Parsetree.PStr []) type stringLiteralState = | Start @@ -911,12 +912,10 @@ let parseConstant p = let floatTxt = if isNegative then "-" ^ f else f in Parsetree.Pconst_float (floatTxt, suffix) | String s -> - let txt = if p.mode = ParseForTypeChecker then - parseStringLiteral s + if p.mode = ParseForTypeChecker then + Pconst_string (s, Some "js") else - s - in - Pconst_string(txt, None) + Pconst_string (s, None) | Codepoint {c; original} -> if p.mode = ParseForTypeChecker then Pconst_char c @@ -1128,7 +1127,7 @@ let rec parsePattern ?(alias=true) ?(or_=true) p = end | Backtick -> let constant = parseTemplateConstant ~prefix:(Some "js") p in - Ast_helper.Pat.constant ~loc:(mkLoc startPos p.prevEndPos) constant + Ast_helper.Pat.constant ~attrs:[templateLiteralAttr] ~loc:(mkLoc startPos p.prevEndPos) constant | Lparen -> Parser.next p; begin match p.token with @@ -2228,25 +2227,19 @@ and parseTemplateExpr ?(prefix="js") p = | TemplateTail txt -> Parser.next p; let loc = mkLoc startPos p.prevEndPos in - if String.length txt > 0 then - let txt = if p.mode = ParseForTypeChecker then parseTemplateStringLiteral txt else txt in - let str = Ast_helper.Exp.constant ~loc (Pconst_string(txt, Some prefix)) in - Ast_helper.Exp.apply ~loc hiddenOperator - [Nolabel, acc; Nolabel, str] - else - acc + let txt = if p.mode = ParseForTypeChecker then parseTemplateStringLiteral txt else txt in + let str = Ast_helper.Exp.constant ~attrs:[templateLiteralAttr] ~loc (Pconst_string(txt, Some prefix)) in + Ast_helper.Exp.apply ~attrs:[templateLiteralAttr] ~loc hiddenOperator + [Nolabel, acc; Nolabel, str] | TemplatePart txt -> Parser.next p; let loc = mkLoc startPos p.prevEndPos in let expr = parseExprBlock p in let fullLoc = mkLoc startPos p.prevEndPos in let txt = if p.mode = ParseForTypeChecker then parseTemplateStringLiteral txt else txt in - let str = Ast_helper.Exp.constant ~loc (Pconst_string(txt, Some prefix)) in + let str = Ast_helper.Exp.constant ~attrs:[templateLiteralAttr] ~loc (Pconst_string(txt, Some prefix)) in let next = - let a = if String.length txt > 0 then - Ast_helper.Exp.apply ~loc:fullLoc hiddenOperator [Nolabel, acc; Nolabel, str] - else acc - in + let a = Ast_helper.Exp.apply ~attrs:[templateLiteralAttr] ~loc:fullLoc hiddenOperator [Nolabel, acc; Nolabel, str] in Ast_helper.Exp.apply ~loc:fullLoc hiddenOperator [Nolabel, a; Nolabel, expr] in @@ -2261,19 +2254,16 @@ and parseTemplateExpr ?(prefix="js") p = | TemplateTail txt -> Parser.next p; let txt = if p.mode = ParseForTypeChecker then parseTemplateStringLiteral txt else txt in - Ast_helper.Exp.constant ~loc:(mkLoc startPos p.prevEndPos) (Pconst_string(txt, Some prefix)) + Ast_helper.Exp.constant ~attrs:[templateLiteralAttr] ~loc:(mkLoc startPos p.prevEndPos) (Pconst_string(txt, Some prefix)) | TemplatePart txt -> Parser.next p; let constantLoc = mkLoc startPos p.prevEndPos in let expr = parseExprBlock p in let fullLoc = mkLoc startPos p.prevEndPos in let txt = if p.mode = ParseForTypeChecker then parseTemplateStringLiteral txt else txt in - let str = Ast_helper.Exp.constant ~loc:constantLoc (Pconst_string(txt, Some prefix)) in + let str = Ast_helper.Exp.constant ~attrs:[templateLiteralAttr] ~loc:constantLoc (Pconst_string(txt, Some prefix)) in let next = - if String.length txt > 0 then - Ast_helper.Exp.apply ~loc:fullLoc hiddenOperator [Nolabel, str; Nolabel, expr] - else - expr + Ast_helper.Exp.apply ~attrs:[templateLiteralAttr] ~loc:fullLoc hiddenOperator [Nolabel, str; Nolabel, expr] in parseParts next | token -> diff --git a/src/res_parsetree_viewer.ml b/src/res_parsetree_viewer.ml index 7c25e3aa..3bb2da3d 100644 --- a/src/res_parsetree_viewer.ml +++ b/src/res_parsetree_viewer.ml @@ -153,7 +153,7 @@ let processBracesAttr expr = let filterParsingAttrs attrs = List.filter (fun attr -> match attr with - | ({Location.txt = ("ns.ternary" | "ns.braces" | "bs" | "ns.iflet" | "ns.namedArgLoc")}, _) -> false + | ({Location.txt = ("ns.ternary" | "ns.braces" | "res.template" | "bs" | "ns.iflet" | "ns.namedArgLoc")}, _) -> false | _ -> true ) attrs @@ -292,7 +292,7 @@ let isIfLetExpr expr = match expr with let hasAttributes attrs = List.exists (fun attr -> match attr with - | ({Location.txt = "bs" | "ns.ternary" | "ns.braces" | "ns.iflet"}, _) -> false + | ({Location.txt = "bs" | "res.template" | "ns.ternary" | "ns.braces" | "ns.iflet"}, _) -> false (* Remove the fragile pattern warning for iflet expressions *) | ({Location.txt="warning"}, PStr [{ pstr_desc = Pstr_eval ({ @@ -453,13 +453,13 @@ let shouldInlineRhsBinaryExpr rhs = match rhs.pexp_desc with let filterPrinteableAttributes attrs = List.filter (fun attr -> match attr with - | ({Location.txt="bs" | "ns.ternary" | "ns.iflet" | "JSX"}, _) -> false + | ({Location.txt="bs" | "res.template" | "ns.ternary" | "ns.iflet" | "JSX"}, _) -> false | _ -> true ) attrs let partitionPrinteableAttributes attrs = List.partition (fun attr -> match attr with - | ({Location.txt="bs" | "ns.ternary" | "ns.iflet" | "JSX"}, _) -> false + | ({Location.txt="bs" | "res.template"| "ns.ternary" | "ns.iflet" | "JSX"}, _) -> false | _ -> true ) attrs @@ -511,17 +511,19 @@ let rec collectPatternsFromListConstruct acc pattern = collectPatternsFromListConstruct (pat::acc) rest | _ -> List.rev acc, pattern -(* Simple heuristic to detect template literal sugar: - * `${user.name} lastName` parses internally as user.name ++ ` lastName`. - * The thing is: the ++ operator (parsed as `^`) will always have a ghost loc. - * A ghost loc is only produced by our parser. - * Hence, if we have that ghost operator, we know for sure it's a template literal. *) + +let hasTemplateLiteralAttr attrs = List.exists (fun attr -> match attr with +| ({Location.txt = "res.template"}, _) -> true +| _ -> false) attrs + let isTemplateLiteral expr = match expr.pexp_desc with | Pexp_apply ( - {pexp_desc = Pexp_ident {txt = Longident.Lident "^"; loc}}, + {pexp_desc = Pexp_ident {txt = Longident.Lident "^"}}, [Nolabel, _; Nolabel, _] - ) when loc.loc_ghost -> true + ) when hasTemplateLiteralAttr expr.pexp_attributes -> true + | Pexp_constant (Pconst_string (_, Some "")) -> true + | Pexp_constant _ when hasTemplateLiteralAttr expr.pexp_attributes -> true | _ -> false (* Blue | Red | Green -> [Blue; Red; Green] *) diff --git a/src/res_parsetree_viewer.mli b/src/res_parsetree_viewer.mli index f83ea02f..65a67367 100644 --- a/src/res_parsetree_viewer.mli +++ b/src/res_parsetree_viewer.mli @@ -109,6 +109,7 @@ val collectPatternsFromListConstruct: val isBlockExpr : Parsetree.expression -> bool val isTemplateLiteral: Parsetree.expression -> bool +val hasTemplateLiteralAttr: Parsetree.attributes -> bool val collectOrPatternChain: Parsetree.pattern -> Parsetree.pattern list diff --git a/src/res_printer.ml b/src/res_printer.ml index 5685e213..42d2de77 100644 --- a/src/res_printer.ml +++ b/src/res_printer.ml @@ -495,7 +495,7 @@ let printStringContents txt = let lines = String.split_on_char '\n' txt in Doc.join ~sep:Doc.literalLine (List.map Doc.text lines) -let printConstant c = match c with +let printConstant ?(templateLiteral=false) c = match c with | Parsetree.Pconst_integer (s, suffix) -> begin match suffix with | Some c -> Doc.text (s ^ (Char.escaped c)) @@ -511,11 +511,14 @@ let printConstant c = match c with if prefix = "INTERNAL_RES_CHAR_CONTENTS" then Doc.concat [Doc.text "'"; Doc.text txt; Doc.text "'"] else + let (lquote, rquote) = + if templateLiteral then ("`", "`") else ("\"", "\"") + in Doc.concat [ if prefix = "js" then Doc.nil else Doc.text prefix; - Doc.text "`"; + Doc.text lquote; printStringContents txt; - Doc.text "`"; + Doc.text rquote; ] | Pconst_float (s, _) -> Doc.text s | Pconst_char c -> @@ -2097,7 +2100,9 @@ and printPattern (p : Parsetree.pattern) cmtTbl = let patternWithoutAttributes = match p.ppat_desc with | Ppat_any -> Doc.text "_" | Ppat_var var -> printIdentLike var.txt - | Ppat_constant c -> printConstant c + | Ppat_constant c -> + let templateLiteral = ParsetreeViewer.hasTemplateLiteralAttr p.ppat_attributes in + printConstant ~templateLiteral c | Ppat_tuple patterns -> Doc.group( Doc.concat([ @@ -2540,7 +2545,8 @@ and printIfChain pexp_attributes ifs elseExpr cmtTbl = and printExpression (e : Parsetree.expression) cmtTbl = let printedExpression = match e.pexp_desc with - | Parsetree.Pexp_constant c -> printConstant c + | Parsetree.Pexp_constant c -> + printConstant ~templateLiteral:(ParsetreeViewer.isTemplateLiteral e) c | Pexp_construct _ when ParsetreeViewer.hasJsxAttribute e.pexp_attributes -> printJsxFragment e cmtTbl | Pexp_construct ({txt = Longident.Lident "()"}, _) -> Doc.text "()" diff --git a/tests/conversion/reason/expected/bracedJsx.re.txt b/tests/conversion/reason/expected/bracedJsx.re.txt index 0cbb8f47..27f93d34 100644 --- a/tests/conversion/reason/expected/bracedJsx.re.txt +++ b/tests/conversion/reason/expected/bracedJsx.re.txt @@ -114,7 +114,7 @@ let make = () => { ref={containerRef->ReactDOMRe.Ref.domRef}> {state.history ->Array.mapWithIndex((index, item) => -
+
{ReasonReact.string( switch item { | User(value) => userPrefix ++ value diff --git a/tests/parsing/errors/expressions/expected/ambiguousArrow.res.txt b/tests/parsing/errors/expressions/expected/ambiguousArrow.res.txt index 4d9c2c4d..596fad0f 100644 --- a/tests/parsing/errors/expressions/expected/ambiguousArrow.res.txt +++ b/tests/parsing/errors/expressions/expected/ambiguousArrow.res.txt @@ -24,6 +24,6 @@ 1) (pattern): int => "test" 2) (pattern: int) => "test" -let a b = ("hi" : int) -let x = ((let a = 1 in let b = 2 in fun pattern -> ("test" : int)) +let a b = ({js|hi|js} : int) +let x = ((let a = 1 in let b = 2 in fun pattern -> ({js|test|js} : int)) [@ns.braces ]) \ No newline at end of file diff --git a/tests/parsing/errors/expressions/expected/emptyeof.res.txt b/tests/parsing/errors/expressions/expected/emptyeof.res.txt index 349c3643..4d299bef 100644 --- a/tests/parsing/errors/expressions/expected/emptyeof.res.txt +++ b/tests/parsing/errors/expressions/expected/emptyeof.res.txt @@ -7,4 +7,5 @@ This string is missing a double quote at the end -let x = "\n" \ No newline at end of file +let x = {js| +|js} \ No newline at end of file diff --git a/tests/parsing/errors/expressions/expected/ifLet.res.txt b/tests/parsing/errors/expressions/expected/ifLet.res.txt index 196aee23..a4a227f4 100644 --- a/tests/parsing/errors/expressions/expected/ifLet.res.txt +++ b/tests/parsing/errors/expressions/expected/ifLet.res.txt @@ -35,10 +35,12 @@ switch result { | _ => () } -;;((match result with | Some x -> Js.log "The sky is blue" | _ -> ()) +;;((match result with | Some x -> Js.log {js|The sky is blue|js} | _ -> ()) [@ns.iflet ][@warning "-4"]) ;;((match result with - | Error x -> Js.log "The sky is red" + | Error x -> Js.log {js|The sky is red|js} | _ -> - (((match result with | Ok y -> Js.log "The sky is blue" | _ -> ())) + (((match result with + | Ok y -> Js.log {js|The sky is blue|js} + | _ -> ())) [@ns.iflet ][@warning "-4"]))[@ns.iflet ][@warning "-4"]) \ No newline at end of file diff --git a/tests/parsing/errors/expressions/expected/taggedTemplateLiterals.res.txt b/tests/parsing/errors/expressions/expected/taggedTemplateLiterals.res.txt index 5b2dad26..3711a2c3 100644 --- a/tests/parsing/errors/expressions/expected/taggedTemplateLiterals.res.txt +++ b/tests/parsing/errors/expressions/expected/taggedTemplateLiterals.res.txt @@ -7,4 +7,4 @@ Tagged template literals are currently restricted to names like: json`null`. -;;{js|null|js} \ No newline at end of file +;;(({js|null|js})[@res.template ]) \ No newline at end of file diff --git a/tests/parsing/errors/expressions/expected/unexpectedConstraint.res.txt b/tests/parsing/errors/expressions/expected/unexpectedConstraint.res.txt index 35e6dcc3..cfa9f898 100644 --- a/tests/parsing/errors/expressions/expected/unexpectedConstraint.res.txt +++ b/tests/parsing/errors/expressions/expected/unexpectedConstraint.res.txt @@ -24,4 +24,4 @@ ("hi": string) let x = ((let a = 1 in let b = 2 in (a + b : int))[@ns.braces ]) -let x = ("hi" : string) \ No newline at end of file +let x = ({js|hi|js} : string) \ No newline at end of file diff --git a/tests/parsing/errors/other/expected/breadcrumbs170.res.txt b/tests/parsing/errors/other/expected/breadcrumbs170.res.txt index 8f950a16..25b22910 100644 --- a/tests/parsing/errors/other/expected/breadcrumbs170.res.txt +++ b/tests/parsing/errors/other/expected/breadcrumbs170.res.txt @@ -18,7 +18,7 @@ module M = struct ;;match l with | None -> [] | Some l -> l#prop end ;;all ;;syntax ;;is -;;"valid" +;;{js|valid|js} ;;it ;;compiles ;;will diff --git a/tests/parsing/errors/pattern/expected/missing.res.txt b/tests/parsing/errors/pattern/expected/missing.res.txt index 4ff671a0..a6a10a2c 100644 --- a/tests/parsing/errors/pattern/expected/missing.res.txt +++ b/tests/parsing/errors/pattern/expected/missing.res.txt @@ -44,5 +44,5 @@ I was expecting a pattern to match on before the `=>` let 2 = [%rescript.exprhole ] -let 4 = for [%rescript.patternhole ] = 0 to 10 do Js.log "for" done +let 4 = for [%rescript.patternhole ] = 0 to 10 do Js.log {js|for|js} done ;;match x with | () -> [%rescript.exprhole ] \ No newline at end of file diff --git a/tests/parsing/errors/pattern/expected/templateLiteral.res.txt b/tests/parsing/errors/pattern/expected/templateLiteral.res.txt index 8c92a720..1412c742 100644 --- a/tests/parsing/errors/pattern/expected/templateLiteral.res.txt +++ b/tests/parsing/errors/pattern/expected/templateLiteral.res.txt @@ -34,5 +34,8 @@ String interpolation is not supported in pattern matching. -let zeroCoord = "0.0" -;;match l with | "" -> () | "" -> () | _ -> () \ No newline at end of file +let zeroCoord = {js|0.0|js} +;;match l with + | (("")[@res.template ]) -> () + | (("")[@res.template ]) -> () + | _ -> () \ No newline at end of file diff --git a/tests/parsing/errors/scanner/expected/escapeSequence.res.txt b/tests/parsing/errors/scanner/expected/escapeSequence.res.txt index 1a0b637b..6757d586 100644 --- a/tests/parsing/errors/scanner/expected/escapeSequence.res.txt +++ b/tests/parsing/errors/scanner/expected/escapeSequence.res.txt @@ -19,5 +19,5 @@ unknown escape sequence -let x = "\\0" -let x = "\\oAAA" \ No newline at end of file +let x = {js|\0|js} +let x = {js|\oAAA|js} \ No newline at end of file diff --git a/tests/parsing/errors/scanner/expected/exoticIdent.res.txt b/tests/parsing/errors/scanner/expected/exoticIdent.res.txt index d32455c1..6e8f2747 100644 --- a/tests/parsing/errors/scanner/expected/exoticIdent.res.txt +++ b/tests/parsing/errors/scanner/expected/exoticIdent.res.txt @@ -43,4 +43,5 @@ let a = b ;;c -;;" = 1\n" \ No newline at end of file +;;{js| = 1 +|js} \ No newline at end of file diff --git a/tests/parsing/errors/scanner/expected/unclosedString.res.txt b/tests/parsing/errors/scanner/expected/unclosedString.res.txt index a3e03f68..059735ce 100644 --- a/tests/parsing/errors/scanner/expected/unclosedString.res.txt +++ b/tests/parsing/errors/scanner/expected/unclosedString.res.txt @@ -7,4 +7,5 @@ This string is missing a double quote at the end -let z = "eof\n" \ No newline at end of file +let z = {js|eof +|js} \ No newline at end of file diff --git a/tests/parsing/errors/structure/expected/gh16A.res.txt b/tests/parsing/errors/structure/expected/gh16A.res.txt index 1306427f..4e8d983a 100644 --- a/tests/parsing/errors/structure/expected/gh16A.res.txt +++ b/tests/parsing/errors/structure/expected/gh16A.res.txt @@ -11,4 +11,4 @@ I'm not sure what to parse here when looking at ")". module C = struct module T = (Fun)(struct ;;foo (a + c) (b + d) end) end -;;Js.log "test" \ No newline at end of file +;;Js.log {js|test|js} \ No newline at end of file diff --git a/tests/parsing/errors/structure/expected/gh16B.res.txt b/tests/parsing/errors/structure/expected/gh16B.res.txt index 898dde5a..23cfa8a5 100644 --- a/tests/parsing/errors/structure/expected/gh16B.res.txt +++ b/tests/parsing/errors/structure/expected/gh16B.res.txt @@ -14,13 +14,17 @@ open Ws let wss = Server.make { port = 82 } let address = wss |. Server.address -let log msg = Js.log ({js|> Server: |js} ^ msg) +let log msg = + Js.log + (((((({js|> Server: |js})[@res.template ]) ^ msg)[@res.template ]) ^ + (({js||js})[@res.template ]))[@res.template ]) ;;log - (((((({js|Running on: |js} ^ address.address) ^ {js|:|js}) ^ - (address.port |. string_of_int)) - ^ {js| (|js}) - ^ address.family) - ^ {js|)|js}) + (((((((((((({js|Running on: |js})[@res.template ]) ^ address.address) + [@res.template ]) ^ (({js|:|js})[@res.template ])) + [@res.template ]) ^ (address.port |. string_of_int)) + ^ (({js| (|js})[@res.template ])) + [@res.template ]) ^ address.family) + ^ (({js|)|js})[@res.template ]))[@res.template ]) module ClientSet = struct module T = @@ -33,4 +37,4 @@ module ClientSet = end) let empty = Belt.Set.make ~id:(((module T))[@ns.namedArgLoc ]) end -;;Js.log "test" \ No newline at end of file +;;Js.log {js|test|js} \ No newline at end of file diff --git a/tests/parsing/errors/typexpr/expected/garbage.res.txt b/tests/parsing/errors/typexpr/expected/garbage.res.txt index 5aef4d25..3c171a7c 100644 --- a/tests/parsing/errors/typexpr/expected/garbage.res.txt +++ b/tests/parsing/errors/typexpr/expected/garbage.res.txt @@ -9,4 +9,4 @@ I'm not sure what to parse here when looking at "?". external printName : name:((unit)[@ns.namedArgLoc ]) -> unit = "printName" -[@@bs.module "moduleName"] \ No newline at end of file +[@@bs.module {js|moduleName|js}] \ No newline at end of file diff --git a/tests/parsing/grammar/expressions/expected/argument.res.txt b/tests/parsing/grammar/expressions/expected/argument.res.txt index 74c8779f..7bc23ec6 100644 --- a/tests/parsing/grammar/expressions/expected/argument.res.txt +++ b/tests/parsing/grammar/expressions/expected/argument.res.txt @@ -6,8 +6,8 @@ let comparisonResult = ((compare currentNode.value ~targetValue:((targetValue)[@ns.namedArgLoc ])) [@bs ]) ;;((callback firstNode ~y:((y)[@ns.namedArgLoc ]))[@bs ]) -;;((document.createElementWithOptions "div" - (elementProps ~onClick:((fun _ -> Js.log "hello world") +;;((document.createElementWithOptions {js|div|js} + (elementProps ~onClick:((fun _ -> Js.log {js|hello world|js}) [@ns.namedArgLoc ])))[@bs ]) ;;((resolve ())[@bs ]) ;;((resolve (let __res_unit = () in __res_unit))[@bs ]) \ No newline at end of file diff --git a/tests/parsing/grammar/expressions/expected/arrow.res.txt b/tests/parsing/grammar/expressions/expected/arrow.res.txt index a2194e12..5ab739fe 100644 --- a/tests/parsing/grammar/expressions/expected/arrow.res.txt +++ b/tests/parsing/grammar/expressions/expected/arrow.res.txt @@ -1,12 +1,12 @@ let f x = x + 1 -let f _ = Js.log "test" -let f () = Js.log "unit" +let f _ = Js.log {js|test|js} +let f () = Js.log {js|unit|js} let f (Reducer (inst, comp)) = inst.render comp let f (Instance) = () let f a b = a + b let f 1 2 = () -let f "stringPattern" = () -let f "stringPattern" "stringPattern" = () +let f {js|stringPattern|js} = () +let f {js|stringPattern|js} {js|stringPattern|js} = () let f () = () let f (a : int) (b : int) = a + b let f _ _ = () diff --git a/tests/parsing/grammar/expressions/expected/block.res.txt b/tests/parsing/grammar/expressions/expected/block.res.txt index 4609a59c..09b38117 100644 --- a/tests/parsing/grammar/expressions/expected/block.res.txt +++ b/tests/parsing/grammar/expressions/expected/block.res.txt @@ -23,17 +23,17 @@ let b = [@ns.braces ]) let b = ((f (); g (); h (); (let arr = [|1;2;3|] in ()))[@ns.braces ]) let res = - ((let a = "a starts out as" in + ((let a = {js|a starts out as|js} in (((print_string a; (let a = 20 in print_int a))) [@ns.braces ]); print_string a) [@ns.braces ]) let res = - ((let a = "first its a string" in + ((let a = {js|first its a string|js} in let a = 20 in print_int a; print_int a; print_int a) [@ns.braces ]) let res = - ((let a = "a is always a string" in + ((let a = {js|a is always a string|js} in print_string a; (let b = 30 in print_int b)) [@ns.braces ]) let nestedLet = ((let _ = 1 in ())[@ns.braces ]) @@ -49,10 +49,11 @@ let reifyStyle (type a) (x : 'a) = external canvasGradient : constructor = "CanvasGradient"[@@bs.val ] external canvasPattern : constructor = "CanvasPattern"[@@bs.val ] let instanceOf = - ([%bs.raw {js|function(x,y) {return +(x instanceof y)}|js}] : - 'a -> constructor -> bool) + ([%bs.raw + (({js|function(x,y) {return +(x instanceof y)}|js}) + [@res.template ])] : 'a -> constructor -> bool) end in - ((if (Js.typeof x) = "string" + ((if (Js.typeof x) = {js|string|js} then Obj.magic String else if Internal.instanceOf x Internal.canvasGradient @@ -63,7 +64,7 @@ let reifyStyle (type a) (x : 'a) = else raise (Invalid_argument - "Unknown canvas style kind. Known values are: String, CanvasGradient, CanvasPattern")), + {js|Unknown canvas style kind. Known values are: String, CanvasGradient, CanvasPattern|js})), (Obj.magic x))) [@ns.braces ]) : (a style * a)) let calc_fps t0 t1 = ((let delta = (t1 -. t0) /. 1000. in 1. /. delta) diff --git a/tests/parsing/grammar/expressions/expected/bsObject.res.txt b/tests/parsing/grammar/expressions/expected/bsObject.res.txt index 43d5d47f..c007f765 100644 --- a/tests/parsing/grammar/expressions/expected/bsObject.res.txt +++ b/tests/parsing/grammar/expressions/expected/bsObject.res.txt @@ -1,7 +1,7 @@ let x = [%obj { age = 30 }] let y = [%obj { age = 30 }] -let y = [%obj { age = 30; name = "steve" }] -let y = [%obj { age = 30; name = "steve" }] +let y = [%obj { age = 30; name = {js|steve|js} }] +let y = [%obj { age = 30; name = {js|steve|js} }] let x = (("age")[@ns.braces ]) let x = (("age".(0))[@ns.braces ]) let x = (("age" |. Js.log)[@ns.braces ]) diff --git a/tests/parsing/grammar/expressions/expected/constants.res.txt b/tests/parsing/grammar/expressions/expected/constants.res.txt index 37de3805..896d1d31 100644 --- a/tests/parsing/grammar/expressions/expected/constants.res.txt +++ b/tests/parsing/grammar/expressions/expected/constants.res.txt @@ -1,13 +1,14 @@ let x = true let y = false -let txt = "a string" -let txtWithEscapedChar = "foo\nbar" +let txt = {js|a string|js} +let txtWithEscapedChar = {js|foo\nbar|js} let number = 1 -let template = {js|amazing +let template = (({js|amazing multine template string -|js} +|js}) + [@res.template ]) let complexNumber = 1.6 let x = 0b0000_0001 let int32 = 42l @@ -49,27 +50,27 @@ let x = '\017' let x = '\170' let x = '\179' let () = ((getResult (); (-10))[@ns.braces ]) -let x = "foo\nbar" -let x = "foo\nbar" -let x = "foo\nbar" -let x = "\\abc" -let x = "'bar" -let x = "\nbar" -let x = "\tbar" -let x = "\bbar" -let x = "\rbar" -let x = " bar" -let x = "\194\169" -let x = "\194\169" -let x = "\226\153\165" -let a = "a" -let x = "\194\169" -let x = "\194\169" -let smile = "\240\159\152\128" -let smile = "\240\159\152\128" -let smile = "\240\159\152\128" -let u = "\230\151\165\230\156\172\232\170\158" -let u = "\230\151\165\230\156\172\232\170\158" -let u = "\230\151\165\230\156\172\232\170\158" -let u = "\230\151\165\230\156\172\232\170\158" -let u = "\230\151\165\230\156\172\232\170\158" \ No newline at end of file +let x = {js|foo\010bar|js} +let x = {js|foo\x0Abar|js} +let x = {js|foo\o012bar|js} +let x = {js|\\abc|js} +let x = {js|\'bar|js} +let x = {js|\nbar|js} +let x = {js|\tbar|js} +let x = {js|\bbar|js} +let x = {js|\rbar|js} +let x = {js|\ bar|js} +let x = {js|\u00A9|js} +let x = {js|\u00a9|js} +let x = {js|\u2665|js} +let a = {js|\u{000000000061}|js} +let x = {js|\u{00A9}|js} +let x = {js|\u{00a9}|js} +let smile = {js|\u{1F600}|js} +let smile = {js|\u{1f600}|js} +let smile = {js|\240\159\152\128|js} +let u = {js|日本語|js} +let u = {js|\u65e5\u672c\u8a9e|js} +let u = {js|\u{000065e5}\u{0000672c}\u{00008a9e}|js} +let u = {js|\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e|js} +let u = {js|\230\151\165\230\156\172\232\170\158|js} \ No newline at end of file diff --git a/tests/parsing/grammar/expressions/expected/es6template.res.txt b/tests/parsing/grammar/expressions/expected/es6template.res.txt index 3776db36..a179529e 100644 --- a/tests/parsing/grammar/expressions/expected/es6template.res.txt +++ b/tests/parsing/grammar/expressions/expected/es6template.res.txt @@ -1,26 +1,81 @@ -let s = {js|foo|js} -let s = {js|multi +let s = (({js|foo|js})[@res.template ]) +let s = (({js|multi line string -|js} -let s = foo -let s = {js|before|js} ^ foo -let s = {js|before |js} ^ foo -let s = {js|before |js} ^ foo -let s = foo ^ {js|after|js} -let s = foo ^ {js| after|js} -let s = foo ^ {js| after|js} -let s = foo ^ bar -let s = (foo ^ bar) ^ baz -let s = (foo ^ {js| |js}) ^ bar -let s = (((foo ^ {js| |js}) ^ bar) ^ {js| |js}) ^ baz -let s = ((({js| before |js} ^ foo) ^ {js| |js}) ^ bar) ^ {js| after |js} -let s = - ((((({js|before |js} ^ foo) ^ {js| middle |js}) ^ bar) ^ {js| |js}) ^ baz) - ^ {js| wow |js} -let s = - {js| +|js})[@res.template ]) +let s = + (((((({js||js})[@res.template ]) ^ foo)[@res.template ]) ^ (({js||js}) + [@res.template ])) + [@res.template ]) +let s = + (((((({js|before|js})[@res.template ]) ^ foo)[@res.template ]) ^ + (({js||js})[@res.template ])) + [@res.template ]) +let s = + (((((({js|before |js})[@res.template ]) ^ foo)[@res.template ]) ^ + (({js||js})[@res.template ])) + [@res.template ]) +let s = + (((((({js|before |js})[@res.template ]) ^ foo)[@res.template ]) ^ + (({js||js})[@res.template ])) + [@res.template ]) +let s = + (((((({js||js})[@res.template ]) ^ foo)[@res.template ]) ^ (({js|after|js}) + [@res.template ])) + [@res.template ]) +let s = + (((((({js||js})[@res.template ]) ^ foo)[@res.template ]) ^ + (({js| after|js})[@res.template ])) + [@res.template ]) +let s = + (((((({js||js})[@res.template ]) ^ foo)[@res.template ]) ^ + (({js| after|js})[@res.template ])) + [@res.template ]) +let s = + ((((((((({js||js})[@res.template ]) ^ foo)[@res.template ]) ^ (({js||js}) + [@res.template ])) + [@res.template ]) ^ bar) + ^ (({js||js})[@res.template ])) + [@res.template ]) +let s = + (((((((((((({js||js})[@res.template ]) ^ foo)[@res.template ]) ^ + (({js||js})[@res.template ])) + [@res.template ]) ^ bar) + ^ (({js||js})[@res.template ])) + [@res.template ]) ^ baz) + ^ (({js||js})[@res.template ])) + [@res.template ]) +let s = + ((((((((({js||js})[@res.template ]) ^ foo)[@res.template ]) ^ (({js| |js}) + [@res.template ])) + [@res.template ]) ^ bar) + ^ (({js||js})[@res.template ])) + [@res.template ]) +let s = + (((((((((((({js||js})[@res.template ]) ^ foo)[@res.template ]) ^ + (({js| |js})[@res.template ])) + [@res.template ]) ^ bar) + ^ (({js| |js})[@res.template ])) + [@res.template ]) ^ baz) + ^ (({js||js})[@res.template ])) + [@res.template ]) +let s = + ((((((((({js| before |js})[@res.template ]) ^ foo)[@res.template ]) ^ + (({js| |js})[@res.template ])) + [@res.template ]) ^ bar) + ^ (({js| after |js})[@res.template ])) + [@res.template ]) +let s = + (((((((((((({js|before |js})[@res.template ]) ^ foo)[@res.template ]) ^ + (({js| middle |js})[@res.template ])) + [@res.template ]) ^ bar) + ^ (({js| |js})[@res.template ])) + [@res.template ]) ^ baz) + ^ (({js| wow |js})[@res.template ])) + [@res.template ]) +let s = + (({js| multiline es6 @@ -32,13 +87,21 @@ let s = so convenient :) -|js} -let s = {js|$dollar without $braces $interpolation|js} -let s = {json|null|json} -let x = {js|foo`bar$\foo|js} -let x = ((({js|foo`bar$\foo|js} ^ a) ^ {js| ` |js}) ^ b) ^ {js| ` xx|js} -let thisIsFine = {js|$something|js} -let thisIsAlsoFine = {js|fine$|js} -let isThisFine = {js|shouldBeFine$|js} -;;{js|$|js} ^ dollarAmountInt -;;{js|$|js} ^ dollarAmountInt \ No newline at end of file +|js}) + [@res.template ]) +let s = (({js|$dollar without $braces $interpolation|js})[@res.template ]) +let s = (({json|null|json})[@res.template ]) +let x = (({js|foo`bar$\foo|js})[@res.template ]) +let x = + ((((((((({js|foo`bar$\foo|js})[@res.template ]) ^ a)[@res.template ]) ^ + (({js| ` |js})[@res.template ])) + [@res.template ]) ^ b) + ^ (({js| ` xx|js})[@res.template ])) + [@res.template ]) +let thisIsFine = (({js|$something|js})[@res.template ]) +let thisIsAlsoFine = (({js|fine$|js})[@res.template ]) +let isThisFine = (({js|shouldBeFine$|js})[@res.template ]) +;;(((((({js|$|js})[@res.template ]) ^ dollarAmountInt)[@res.template ]) ^ + (({js||js})[@res.template ]))[@res.template ]) +;;(((((({js|$|js})[@res.template ]) ^ dollarAmountInt)[@res.template ]) ^ + (({js||js})[@res.template ]))[@res.template ]) \ No newline at end of file diff --git a/tests/parsing/grammar/expressions/expected/extension.res.txt b/tests/parsing/grammar/expressions/expected/extension.res.txt index f1ab923c..b97d310d 100644 --- a/tests/parsing/grammar/expressions/expected/extension.res.txt +++ b/tests/parsing/grammar/expressions/expected/extension.res.txt @@ -1,5 +1,5 @@ ;;[%expr ] ;;[%expr.extension ] -;;[%expr.extension.with.args "argument"] +;;[%expr.extension.with.args {js|argument|js}] ;;[%expr.extension.with.args fun x -> f x] -let x = ([%bs.raw "1"]) + ([%bs.raw "2"]) \ No newline at end of file +let x = ([%bs.raw {js|1|js}]) + ([%bs.raw {js|2|js}]) \ No newline at end of file diff --git a/tests/parsing/grammar/expressions/expected/firstClassModule.res.txt b/tests/parsing/grammar/expressions/expected/firstClassModule.res.txt index 9c6d5d2f..6a80380c 100644 --- a/tests/parsing/grammar/expressions/expected/firstClassModule.res.txt +++ b/tests/parsing/grammar/expressions/expected/firstClassModule.res.txt @@ -45,22 +45,22 @@ let build_dispatch_table handlers = ;;((module Teenager) |. age) |. Js.log ;;((module Teenager).(0)) |. Js.log ;;((if ((module Teenager) |. age) |. isAdult - then Js.log "has responsibilities" - else Js.log "can play in the playground")[@ns.ternary ]) + then Js.log {js|has responsibilities|js} + else Js.log {js|can play in the playground|js})[@ns.ternary ]) ;;((if ((module Streets).(0)) |. isExpensive - then Js.log "big money" - else Js.log "affordable")[@ns.ternary ]) + then Js.log {js|big money|js} + else Js.log {js|affordable|js})[@ns.ternary ]) let () = ((((module Teenager) |. age) |. Js.log)[@ns.braces ]) let () = (((module Teenager).(0))[@ns.braces ]) let () = ((if ((module Teenager) |. age) |. isAdult - then Js.log "has responsibilities" - else Js.log "can play in the playground") + then Js.log {js|has responsibilities|js} + else Js.log {js|can play in the playground|js}) [@ns.braces ][@ns.ternary ]) let () = ((if ((module Streets).(0)) |. isExpensive - then Js.log "big money" - else Js.log "affordable") + then Js.log {js|big money|js} + else Js.log {js|affordable|js}) [@ns.braces ][@ns.ternary ]) let () = ((let a = 1 in @@ -71,7 +71,7 @@ let () = let b = 2 in ((module Teenager) |. age) |. Js.log; ((if (((module Teenager).(0)) |. age) |. isAdult - then Js.log "has responsibilities" - else Js.log "can play in the playground") + then Js.log {js|has responsibilities|js} + else Js.log {js|can play in the playground|js}) [@ns.ternary ])) [@ns.braces ]) \ No newline at end of file diff --git a/tests/parsing/grammar/expressions/expected/infix.res.txt b/tests/parsing/grammar/expressions/expected/infix.res.txt index 7126c043..0d8bbd28 100644 --- a/tests/parsing/grammar/expressions/expected/infix.res.txt +++ b/tests/parsing/grammar/expressions/expected/infix.res.txt @@ -1,5 +1,5 @@ ;;a |. (f b) -;;"string1" ^ "string2" +;;{js|string1|js} ^ {js|string2|js} ;;a <> b ;;a != b ;;a = b diff --git a/tests/parsing/grammar/expressions/expected/jsx.res.txt b/tests/parsing/grammar/expressions/expected/jsx.res.txt index 0a3c9c66..035598f7 100644 --- a/tests/parsing/grammar/expressions/expected/jsx.res.txt +++ b/tests/parsing/grammar/expressions/expected/jsx.res.txt @@ -1,29 +1,29 @@ let _ = ((div ~children:[] ())[@JSX ]) let _ = ((div ~children:[] ())[@JSX ]) -let _ = ((div ~className:(("menu")[@ns.namedArgLoc ]) ~children:[] ()) +let _ = ((div ~className:(({js|menu|js})[@ns.namedArgLoc ]) ~children:[] ()) [@JSX ]) -let _ = ((div ~className:(("menu")[@ns.namedArgLoc ]) ~children:[] ()) +let _ = ((div ~className:(({js|menu|js})[@ns.namedArgLoc ]) ~children:[] ()) [@JSX ]) -let _ = ((div ~className:(("menu")[@ns.namedArgLoc ]) ~children:[] ()) +let _ = ((div ~className:(({js|menu|js})[@ns.namedArgLoc ]) ~children:[] ()) [@JSX ]) -let _ = ((div ~className:(("menu")[@ns.namedArgLoc ]) ~children:[] ()) +let _ = ((div ~className:(({js|menu|js})[@ns.namedArgLoc ]) ~children:[] ()) [@JSX ]) let _ = - ((div ~className:(("menu")[@ns.namedArgLoc ]) - ~onClick:((fun _ -> Js.log "click")[@ns.namedArgLoc ][@ns.braces ]) - ~children:[] ()) + ((div ~className:(({js|menu|js})[@ns.namedArgLoc ]) + ~onClick:((fun _ -> Js.log {js|click|js}) + [@ns.namedArgLoc ][@ns.braces ]) ~children:[] ()) [@JSX ]) let _ = - ((div ~className:(("menu")[@ns.namedArgLoc ]) - ~onClick:((fun _ -> Js.log "click")[@ns.namedArgLoc ][@ns.braces ]) - ~children:[] ()) + ((div ~className:(({js|menu|js})[@ns.namedArgLoc ]) + ~onClick:((fun _ -> Js.log {js|click|js}) + [@ns.namedArgLoc ][@ns.braces ]) ~children:[] ()) [@JSX ]) let _ = ((Navbar.createElement ~children:[] ())[@JSX ]) let _ = ((Navbar.createElement ~children:[] ())[@JSX ]) let _ = ((Navbar.createElement ~children:[] ())[@JSX ]) let _ = - ((Navbar.createElement ~className:(("menu")[@ns.namedArgLoc ]) ~children:[] - ()) + ((Navbar.createElement ~className:(({js|menu|js})[@ns.namedArgLoc ]) + ~children:[] ()) [@JSX ]) let _ = ((Dot.Up.createElement ~children:[] ())[@JSX ]) let _ = ((Dot.Up.createElement ~children:[] ())[@JSX ]) @@ -37,8 +37,8 @@ let _ = ~children:[((Dot.Up.createElement ~children:[] ())[@JSX ])] ()) [@JSX ]) let _ = - ((Dot.Up.createElement ~className:(("menu")[@ns.namedArgLoc ]) ~children:[] - ()) + ((Dot.Up.createElement ~className:(({js|menu|js})[@ns.namedArgLoc ]) + ~children:[] ()) [@JSX ]) let _ = ((Dot.low.createElement ~children:[] ())[@JSX ]) let _ = ((Dot.low.createElement ~children:[] ())[@JSX ]) @@ -52,7 +52,7 @@ let _ = ~children:[((Dot.low.createElement ~children:[] ())[@JSX ])] ()) [@JSX ]) let _ = - ((Dot.low.createElement ~className:(("menu")[@ns.namedArgLoc ]) + ((Dot.low.createElement ~className:(({js|menu|js})[@ns.namedArgLoc ]) ~children:[] ()) [@JSX ]) let _ = ((el ~punned:((punned)[@ns.namedArgLoc ]) ~children:[] ())[@JSX ]) @@ -64,20 +64,20 @@ let _ = ((el ?a:((b)[@ns.namedArgLoc ]) ~children:[] ())[@JSX ]) let _ = (([])[@JSX ]) let _ = (([])[@JSX ]) let _ = - ((div ~className:(("menu")[@ns.namedArgLoc ]) - ~children:[((div ~className:(("submenu")[@ns.namedArgLoc ]) + ((div ~className:(({js|menu|js})[@ns.namedArgLoc ]) + ~children:[((div ~className:(({js|submenu|js})[@ns.namedArgLoc ]) ~children:[sub1] ()) [@JSX ]); - ((div ~className:(("submenu")[@ns.namedArgLoc ]) + ((div ~className:(({js|submenu|js})[@ns.namedArgLoc ]) ~children:[sub2] ()) [@JSX ])] ()) [@JSX ]) let _ = - ((div ~className:(("menu")[@ns.namedArgLoc ]) - ~children:[((div ~className:(("submenu")[@ns.namedArgLoc ]) + ((div ~className:(({js|menu|js})[@ns.namedArgLoc ]) + ~children:[((div ~className:(({js|submenu|js})[@ns.namedArgLoc ]) ~children:[sub1] ()) [@JSX ]); - ((div ~className:(("submenu")[@ns.namedArgLoc ]) + ((div ~className:(({js|submenu|js})[@ns.namedArgLoc ]) ~children:[sub2] ()) [@JSX ])] ()) [@JSX ]) @@ -99,7 +99,7 @@ let _ = [@JSX ]) let _ = ((div ~onClick:((onClickHandler)[@ns.namedArgLoc ]) - ~children:[((["foobar"])[@JSX ])] ()) + ~children:[(([{js|foobar|js}])[@JSX ])] ()) [@JSX ]) let _ = ((Window.createElement @@ -212,10 +212,10 @@ let tuples = let icon = ((Icon.createElement ~name:((match state.volume with - | v when v < 0.1 -> "sound-off" - | v when v < 0.11 -> "sound-min" - | v when v < 0.51 -> "sound-med" - | _ -> "sound-max")[@ns.namedArgLoc ][@ns.braces ]) + | v when v < 0.1 -> {js|sound-off|js} + | v when v < 0.11 -> {js|sound-min|js} + | v when v < 0.51 -> {js|sound-med|js} + | _ -> {js|sound-max|js})[@ns.namedArgLoc ][@ns.braces ]) ~children:[] ()) [@JSX ]) let _ = @@ -253,19 +253,19 @@ let y = [@JSX ])|] let _ = ((Description.createElement - ~term:((Text.createElement ~text:(("Age")[@ns.namedArgLoc ]) + ~term:((Text.createElement ~text:(({js|Age|js})[@ns.namedArgLoc ]) ~children:[] ())[@ns.namedArgLoc ][@ns.braces ][@JSX ]) ~children:[child] ()) [@JSX ]) let _ = ((Description.createElement - ~term:((Text.createElement ~text:(("Age")[@ns.namedArgLoc ]) + ~term:((Text.createElement ~text:(({js|Age|js})[@ns.namedArgLoc ]) ~children:(([||])[@ns.namedArgLoc ]) ()) [@ns.namedArgLoc ][@ns.braces ]) ~children:[child] ()) [@JSX ]) let _ = ((Description.createElement - ~term:((Text.createElement ~text:(("Age")[@ns.namedArgLoc ]) ()) + ~term:((Text.createElement ~text:(({js|Age|js})[@ns.namedArgLoc ]) ()) [@ns.namedArgLoc ][@ns.braces ][@JSX ]) ~children:[child] ()) [@JSX ]) let _ = @@ -273,7 +273,7 @@ let _ = ~term:((Text.createElement ~superLongPunnedProp:((superLongPunnedProp) [@ns.namedArgLoc ]) ~anotherSuperLongOneCrazyLongThingHere:((anotherSuperLongOneCrazyLongThingHere) - [@ns.namedArgLoc ]) ~text:(("Age")[@ns.namedArgLoc ]) + [@ns.namedArgLoc ]) ~text:(({js|Age|js})[@ns.namedArgLoc ]) ~children:[] ())[@ns.namedArgLoc ][@ns.braces ][@JSX ]) ~children:[child] ()) [@JSX ]) @@ -285,27 +285,28 @@ let _ = [@ns.namedArgLoc ]) ~children:[] ()) [@ns.namedArgLoc ][@ns.braces ][@JSX ]) ~children:[] ()) [@JSX ]) -let _ = ((div ~children:[((span ~children:[str "hello"] ())[@JSX ])] ()) +let _ = + ((div ~children:[((span ~children:[str {js|hello|js}] ())[@JSX ])] ()) [@JSX ]) let _ = ((description - ~term:((text ~text:(("Age")[@ns.namedArgLoc ]) ~children:[] ()) + ~term:((text ~text:(({js|Age|js})[@ns.namedArgLoc ]) ~children:[] ()) [@ns.namedArgLoc ][@ns.braces ][@JSX ]) ~children:[child] ()) [@JSX ]) let _ = ((description - ~term:((text ~text:(("Age")[@ns.namedArgLoc ]) ~children:(([||]) - [@ns.namedArgLoc ]) ())[@ns.namedArgLoc ][@ns.braces ]) + ~term:((text ~text:(({js|Age|js})[@ns.namedArgLoc ]) ~children:(( + [||])[@ns.namedArgLoc ]) ())[@ns.namedArgLoc ][@ns.braces ]) ~children:[child] ()) [@JSX ]) let _ = ((description - ~term:((text ~text:(("Age")[@ns.namedArgLoc ]) ~children:(([||]) - [@ns.namedArgLoc ]))[@ns.namedArgLoc ][@ns.braces ][@JSX ]) - ~children:[child] ()) + ~term:((text ~text:(({js|Age|js})[@ns.namedArgLoc ]) ~children:(( + [||])[@ns.namedArgLoc ])) + [@ns.namedArgLoc ][@ns.braces ][@JSX ]) ~children:[child] ()) [@JSX ]) let _ = - ((description ~term:((text ~text:(("Age")[@ns.namedArgLoc ]) ()) + ((description ~term:((text ~text:(({js|Age|js})[@ns.namedArgLoc ]) ()) [@ns.namedArgLoc ][@ns.braces ][@JSX ]) ~children:[child] ()) [@JSX ]) let _ = @@ -313,7 +314,7 @@ let _ = ~term:((div ~superLongPunnedProp:((superLongPunnedProp) [@ns.namedArgLoc ]) ~anotherSuperLongOneCrazyLongThingHere:((anotherSuperLongOneCrazyLongThingHere) - [@ns.namedArgLoc ]) ~text:(("Age")[@ns.namedArgLoc ]) + [@ns.namedArgLoc ]) ~text:(({js|Age|js})[@ns.namedArgLoc ]) ~children:[] ())[@ns.namedArgLoc ][@ns.braces ][@JSX ]) ~children:[child] ()) [@JSX ]) @@ -396,37 +397,39 @@ let _ = let _ = ((div ~children:[(((match color with - | Black -> ReasonReact.string "black" - | Red -> ReasonReact.string "red")) + | Black -> ReasonReact.string {js|black|js} + | Red -> ReasonReact.string {js|red|js})) [@ns.braces ])] ()) [@JSX ]) let _ = ((div - ~style:((ReactDOMRe.Style.make ~width:(("20px")[@ns.namedArgLoc ]) - ~height:(("20px")[@ns.namedArgLoc ]) ~borderRadius:(("100%") - [@ns.namedArgLoc ]) ~backgroundColor:(("red") + ~style:((ReactDOMRe.Style.make ~width:(({js|20px|js}) + [@ns.namedArgLoc ]) ~height:(({js|20px|js}) + [@ns.namedArgLoc ]) ~borderRadius:(({js|100%|js}) + [@ns.namedArgLoc ]) ~backgroundColor:(({js|red|js}) [@ns.namedArgLoc ]))[@ns.namedArgLoc ][@ns.braces ][@foo ]) ~children:[] ()) [@JSX ]) let _ = ((Animated.createElement ~initialValue:((0.0)[@ns.namedArgLoc ]) ~value:((value)[@ns.namedArgLoc ]) - ~children:((ReactDOMRe.Style.make ~width:(("20px")[@ns.namedArgLoc ]) - ~height:(("20px")[@ns.namedArgLoc ]) - ~borderRadius:(("100%")[@ns.namedArgLoc ]) - ~backgroundColor:(("red")[@ns.namedArgLoc ])) - [@ns.braces ]) ()) + ~children:((ReactDOMRe.Style.make ~width:(({js|20px|js}) + [@ns.namedArgLoc ]) ~height:(({js|20px|js}) + [@ns.namedArgLoc ]) ~borderRadius:(({js|100%|js}) + [@ns.namedArgLoc ]) ~backgroundColor:(({js|red|js}) + [@ns.namedArgLoc ]))[@ns.braces ]) ()) [@JSX ]) let _ = ((Animated.createElement ~initialValue:((0.0)[@ns.namedArgLoc ]) ~value:((value)[@ns.namedArgLoc ]) ~children:((fun value -> ((div - ~style:((ReactDOMRe.Style.make ~width:(("20px") - [@ns.namedArgLoc ]) ~height:(("20px") + ~style:((ReactDOMRe.Style.make ~width:(({js|20px|js}) [@ns.namedArgLoc ]) - ~borderRadius:(("100%")[@ns.namedArgLoc ]) - ~backgroundColor:(("red") + ~height:(({js|20px|js})[@ns.namedArgLoc ]) + ~borderRadius:(({js|100%|js}) + [@ns.namedArgLoc ]) + ~backgroundColor:(({js|red|js}) [@ns.namedArgLoc ])) [@ns.namedArgLoc ][@ns.braces ]) ~children:[] ()) [@JSX ]))[@ns.braces ]) ()) @@ -436,12 +439,13 @@ let _ = ~value:((value)[@ns.namedArgLoc ]) ~children:((fun value -> (((div - ~style:((ReactDOMRe.Style.make ~width:(("20px") - [@ns.namedArgLoc ]) ~height:(("20px") + ~style:((ReactDOMRe.Style.make + ~width:(({js|20px|js})[@ns.namedArgLoc ]) + ~height:(({js|20px|js}) [@ns.namedArgLoc ]) - ~borderRadius:(("100%") + ~borderRadius:(({js|100%|js}) [@ns.namedArgLoc ]) - ~backgroundColor:(("red") + ~backgroundColor:(({js|red|js}) [@ns.namedArgLoc ])) [@ns.namedArgLoc ][@ns.braces ]) ~children:[] ()) [@JSX ]) : ReasonReact.element))[@ns.braces ]) ()) @@ -451,11 +455,12 @@ let _ = ~value:((value)[@ns.namedArgLoc ]) ~children:((fun value -> ((div - ~style:((ReactDOMRe.Style.make ~width:(("20px") - [@ns.namedArgLoc ]) ~height:(("20px") + ~style:((ReactDOMRe.Style.make ~width:(({js|20px|js}) + [@ns.namedArgLoc ]) + ~height:(({js|20px|js})[@ns.namedArgLoc ]) + ~borderRadius:(({js|100%|js}) [@ns.namedArgLoc ]) - ~borderRadius:(("100%")[@ns.namedArgLoc ]) - ~backgroundColor:(("red") + ~backgroundColor:(({js|red|js}) [@ns.namedArgLoc ])) [@ns.namedArgLoc ][@ns.braces ]) ~children:[] ()) [@ns.braces ][@JSX ]))[@ns.braces ][@foo ]) ()) @@ -464,15 +469,15 @@ let _ = ((Animated.createElement ~initialValue:((0.0)[@ns.namedArgLoc ]) ~value:((value)[@ns.namedArgLoc ]) ~children:((fun value -> - ((let width = "20px" in - let height = "20px" in + ((let width = {js|20px|js} in + let height = {js|20px|js} in ((div ~style:((ReactDOMRe.Style.make ~width:((width) [@ns.namedArgLoc ]) ~height:((height) [@ns.namedArgLoc ]) - ~borderRadius:(("100%") + ~borderRadius:(({js|100%|js}) [@ns.namedArgLoc ]) - ~backgroundColor:(("red") + ~backgroundColor:(({js|red|js}) [@ns.namedArgLoc ])) [@ns.namedArgLoc ][@ns.braces ]) ~children:[] ()) [@JSX ])) @@ -484,19 +489,19 @@ let _ = [@JSX ]) let _ = ((button ?id:((id)[@ns.namedArgLoc ]) - ~className:((Cn.make [|"button";"is-fullwidth"|]) + ~className:((Cn.make [|{js|button|js};{js|is-fullwidth|js}|]) [@ns.namedArgLoc ][@ns.braces ]) ~onClick:((onClick)[@ns.namedArgLoc ]) ~children:[(("Submit" |> ste)[@ns.braces ])] ()) [@JSX ]) let _ = ((button ?id:((id)[@ns.namedArgLoc ]) - ~className:((Cn.make ["button"; "is-fullwidth"]) + ~className:((Cn.make [{js|button|js}; {js|is-fullwidth|js}]) [@ns.namedArgLoc ][@ns.braces ]) ~onClick:((onClick)[@ns.namedArgLoc ]) ~children:[(("Submit" |> ste)[@ns.braces ])] ()) [@JSX ]) let _ = ((button ?id:((id)[@ns.namedArgLoc ]) - ~className:((Cn.make ("button", "is-fullwidth")) + ~className:((Cn.make ({js|button|js}, {js|is-fullwidth|js})) [@ns.namedArgLoc ][@ns.braces ]) ~onClick:((onClick)[@ns.namedArgLoc ]) ~children:[(("Submit" |> ste)[@ns.braces ])] ()) [@JSX ]) @@ -506,23 +511,25 @@ let _ = ~children:[(("Submit" |> ste)[@ns.braces ])] ()) [@JSX ]) let _ = - ((X.createElement ~y:((z |. (Belt.Option.getWithDefault "")) + ((X.createElement ~y:((z |. (Belt.Option.getWithDefault {js||js})) [@ns.namedArgLoc ][@ns.braces ]) ~children:[] ()) [@JSX ]) let _ = ((div ~style:((getStyle ())[@ns.namedArgLoc ][@ns.braces ]) - ~children:[((ReasonReact.string "BugTest")[@ns.braces ])] ()) + ~children:[((ReasonReact.string {js|BugTest|js})[@ns.braces ])] ()) [@JSX ]) let _ = ((div ~children:[(((let left = limit |. Int.toString in - (left ^ {js| characters left|js}) |. React.string)) + (((((({js||js})[@res.template ]) ^ left)[@res.template ]) + ^ (({js| characters left|js})[@res.template ])) + [@res.template ]) |. React.string)) [@ns.braces ])] ()) [@JSX ]) let _ = ((View.createElement ~style:((styles#backgroundImageWrapper) [@ns.namedArgLoc ]) - ~children:[(((let uri = "/images/header-background.png" in + ~children:[(((let uri = {js|/images/header-background.png|js} in ((Image.createElement ~resizeMode:((Contain) [@ns.namedArgLoc ]) ~style:((styles#backgroundImage) [@ns.namedArgLoc ]) ~uri:((uri)[@ns.namedArgLoc ]) diff --git a/tests/parsing/grammar/expressions/expected/parenthesized.res.txt b/tests/parsing/grammar/expressions/expected/parenthesized.res.txt index 59f02931..b097fb35 100644 --- a/tests/parsing/grammar/expressions/expected/parenthesized.res.txt +++ b/tests/parsing/grammar/expressions/expected/parenthesized.res.txt @@ -4,20 +4,24 @@ let truth = true let truth = false let constructor = None let longidentConstructor = Option.None -let txt = "a string" -let otherTxt = {js|foo bar |js} ^ txt +let txt = {js|a string|js} +let otherTxt = + (((((({js|foo bar |js})[@res.template ]) ^ txt)[@res.template ]) ^ + (({js||js})[@res.template ])) + [@res.template ]) let ident = myIdent let aList = [1; 2] let anArray = [|1;2|] let aTuple = (1, 2) -let aRecord = { name = "steve"; age = 30 } +let aRecord = { name = {js|steve|js}; age = 30 } let blockExpression = ((let a = 1 in let b = 2 in a + b)[@ns.braces ]) let assertSmthing = assert true let lazyThing = lazy true -let jsx = ((div ~className:(("cx")[@ns.namedArgLoc ]) ~children:[foo] ()) +let jsx = + ((div ~className:(({js|cx|js})[@ns.namedArgLoc ]) ~children:[foo] ()) [@JSX ]) let ifExpr = if true then Js.log true else Js.log false let forExpr = for p = 0 to 10 do () done let whileExpr = while true do doSomeImperativeThing () done -let switchExpr = match myVar with | Blue -> "blue" | Red -> "red" +let switchExpr = match myVar with | Blue -> {js|blue|js} | Red -> {js|red|js} let constrainedExpr = (x : int) \ No newline at end of file diff --git a/tests/parsing/grammar/expressions/expected/polyvariant.res.txt b/tests/parsing/grammar/expressions/expected/polyvariant.res.txt index 93f71610..5de14a9e 100644 --- a/tests/parsing/grammar/expressions/expected/polyvariant.res.txt +++ b/tests/parsing/grammar/expressions/expected/polyvariant.res.txt @@ -5,5 +5,5 @@ let animation = `ease-in let one = `1 let fortyTwo = `42 let long = `42444 -let oneString = `1 "payload" -let twoIntString = `2 (3, "payload") \ No newline at end of file +let oneString = `1 {js|payload|js} +let twoIntString = `2 (3, {js|payload|js}) \ No newline at end of file diff --git a/tests/parsing/grammar/expressions/expected/switch.res.txt b/tests/parsing/grammar/expressions/expected/switch.res.txt index 662922f8..520fe4fb 100644 --- a/tests/parsing/grammar/expressions/expected/switch.res.txt +++ b/tests/parsing/grammar/expressions/expected/switch.res.txt @@ -5,7 +5,7 @@ ;;match person1 with | Teacher _ -> () | Student { reportCard = { gpa } } when gpa < 0.5 -> - Js.log "What's happening" + Js.log {js|What's happening|js} | Student { reportCard = { gpa } } when gpa > 0.9 -> - Js.log "Take more free time, you study too much." - | Student _ -> Js.log "Heyo" \ No newline at end of file + Js.log {js|Take more free time, you study too much.|js} + | Student _ -> Js.log {js|Heyo|js} \ No newline at end of file diff --git a/tests/parsing/grammar/expressions/expected/try.res.txt b/tests/parsing/grammar/expressions/expected/try.res.txt index af7802e9..4d6f59ba 100644 --- a/tests/parsing/grammar/expressions/expected/try.res.txt +++ b/tests/parsing/grammar/expressions/expected/try.res.txt @@ -1,5 +1,6 @@ ;;try ((let x = 1 in let y = 2 in dangerousCall (x + y))[@ns.braces ]) - with | Foo -> Js.log "catched Foo" | Exit -> Js.log "catched exit" -;;try myDangerousFn () with | Foo -> Js.log "catched Foo"[@@attr ] + with | Foo -> Js.log {js|catched Foo|js} + | Exit -> Js.log {js|catched exit|js} +;;try myDangerousFn () with | Foo -> Js.log {js|catched Foo|js}[@@attr ] let x = ((let y = 1 in try ((apply y)[@ns.braces ]) with | _ -> 2) [@ns.braces ]) \ No newline at end of file diff --git a/tests/parsing/grammar/ffi/expected/export.res.txt b/tests/parsing/grammar/ffi/expected/export.res.txt index 1a207c99..0df452bf 100644 --- a/tests/parsing/grammar/ffi/expected/export.res.txt +++ b/tests/parsing/grammar/ffi/expected/export.res.txt @@ -5,11 +5,11 @@ type nonrec t = int[@@genType ] and s = string type nonrec t = int and s = string[@@genType ] -let callback _ = Js.log "Clicked"[@@genType ] -let callback _ = Js.log "Clicked"[@@genType ] -let x = "hello world"[@@genType ] +let callback _ = Js.log {js|Clicked|js}[@@genType ] +let callback _ = Js.log {js|Clicked|js}[@@genType ] +let x = {js|hello world|js}[@@genType ] and y = 2[@@genType ] -let x = "hello world"[@@genType ] +let x = {js|hello world|js}[@@genType ] and y = 2[@@genType ] -let x = "hello world" +let x = {js|hello world|js} and y = 2[@@genType ] \ No newline at end of file diff --git a/tests/parsing/grammar/modtype/expected/extension.res.txt b/tests/parsing/grammar/modtype/expected/extension.res.txt index 5e99081f..78109f19 100644 --- a/tests/parsing/grammar/modtype/expected/extension.res.txt +++ b/tests/parsing/grammar/modtype/expected/extension.res.txt @@ -1,3 +1,3 @@ module type T = [%ext ] -module type T = [%ext.with.args "argument"] +module type T = [%ext.with.args {js|argument|js}] module type T = (([%ext ])[@attr ]) \ No newline at end of file diff --git a/tests/parsing/grammar/pattern/expected/constant.res.txt b/tests/parsing/grammar/pattern/expected/constant.res.txt index 2eaa0a72..ab089140 100644 --- a/tests/parsing/grammar/pattern/expected/constant.res.txt +++ b/tests/parsing/grammar/pattern/expected/constant.res.txt @@ -1,22 +1,22 @@ -let "stringPattern" = () -let "stringPattern" as s = () -let ("stringPattern" : string) = () -let ("stringPattern" : string) as s = () +let {js|stringPattern|js} = () +let {js|stringPattern|js} as s = () +let ({js|stringPattern|js} : string) = () +let ({js|stringPattern|js} : string) as s = () ;;match x with - | "stringPattern" -> () - | "stringPattern" as s -> () - | ("stringPattern" : string) as s -> () -;;for "stringPattern" = 0 to 10 do () done -;;for "stringPattern" as s = 0 to 10 do () done -;;for "stringPattern" = 0 to 10 do () done -;;for "stringPattern" as s = 0 to 10 do () done -;;for "stringPattern" as s = 0 to 10 do () done -let f "stringPattern" = () -let f ("stringPattern" as s) = () -let f ("stringPattern" as s) = () -let f ("stringPattern" : string) = () -let f (("stringPattern" : string) as s) = () -let f ("stringPattern" : string) = () + | {js|stringPattern|js} -> () + | {js|stringPattern|js} as s -> () + | ({js|stringPattern|js} : string) as s -> () +;;for {js|stringPattern|js} = 0 to 10 do () done +;;for {js|stringPattern|js} as s = 0 to 10 do () done +;;for {js|stringPattern|js} = 0 to 10 do () done +;;for {js|stringPattern|js} as s = 0 to 10 do () done +;;for {js|stringPattern|js} as s = 0 to 10 do () done +let f {js|stringPattern|js} = () +let f ({js|stringPattern|js} as s) = () +let f ({js|stringPattern|js} as s) = () +let f ({js|stringPattern|js} : string) = () +let f (({js|stringPattern|js} : string) as s) = () +let f ({js|stringPattern|js} : string) = () let 1 = () let 1 as x = () let (1 : int) = () @@ -46,11 +46,11 @@ let f (1 : int) = () let _0 = 0x9A let print ppf i = match i.stamp with - | 0 -> fprintf ppf "%s!" i.name - | (-1) -> fprintf ppf "%s#" i.name - | 1 -> fprintf ppf "%s#" i.name - | (-1.) -> fprintf ppf "%s#" i.name - | 1. -> fprintf ppf "%s#" i.name + | 0 -> fprintf ppf {js|%s!|js} i.name + | (-1) -> fprintf ppf {js|%s#|js} i.name + | 1 -> fprintf ppf {js|%s#|js} i.name + | (-1.) -> fprintf ppf {js|%s#|js} i.name + | 1. -> fprintf ppf {js|%s#|js} i.name let (-1)..(-1.) = x ;;match science with | (1.12, (-3.13)) -> true @@ -66,18 +66,30 @@ let (-1)..(-1.) = x | exception 19.34 -> true | _ -> false ;;match literal with - | {js|literal|js} -> true - | ({js|literal1|js}, {js|literal2|js}) -> true - | [|{js|literal1|js};{js|literal2|js}|] -> true - | {js|literal1|js}::{js|literal2|js}::[] -> true - | { x = {js|literal1|js}; y = {js|literal2|js} } -> true - | Constructor ({js|literal1|js}, {js|literal2|js}) -> true - | `Constuctor ({js|literal1|js}, {js|literal2|js}) -> true - | {js|literal|js} as x -> true - | {js|literal|js}|{js|literal|js} -> true - | ({js|literal|js} : string) -> true - | (lazy {js|literal|js}) -> true - | exception {js|literal|js} -> true + | (({js|literal|js})[@res.template ]) -> true + | ((({js|literal1|js})[@res.template ]), + (({js|literal2|js})[@res.template ])) -> true + | [|(({js|literal1|js})[@res.template ]);(({js|literal2|js})[@res.template + ])|] + -> true + | (({js|literal1|js})[@res.template ])::(({js|literal2|js})[@res.template ])::[] + -> true + | { x = (({js|literal1|js})[@res.template ]); + y = (({js|literal2|js})[@res.template ]) } -> true + | Constructor + ((({js|literal1|js})[@res.template ]), + (({js|literal2|js})[@res.template ])) + -> true + | `Constuctor + ((({js|literal1|js})[@res.template ]), + (({js|literal2|js})[@res.template ])) + -> true + | (({js|literal|js})[@res.template ]) as x -> true + | (({js|literal|js})[@res.template ])|(({js|literal|js})[@res.template ]) + -> true + | ((({js|literal|js})[@res.template ]) : string) -> true + | (lazy (({js|literal|js})[@res.template ])) -> true + | exception (({js|literal|js})[@res.template ]) -> true | _ -> false -let {js|literal constant|js} = x -;;for {js|literal constant|js} = 0 to 10 do () done \ No newline at end of file +let (({js|literal constant|js})[@res.template ]) = x +;;for (({js|literal constant|js})[@res.template ]) = 0 to 10 do () done \ No newline at end of file diff --git a/tests/parsing/grammar/pattern/expected/constructor.res.txt b/tests/parsing/grammar/pattern/expected/constructor.res.txt index 2c59bbcc..45783c9c 100644 --- a/tests/parsing/grammar/pattern/expected/constructor.res.txt +++ b/tests/parsing/grammar/pattern/expected/constructor.res.txt @@ -69,4 +69,6 @@ let f (Instance (comp : Component.t) : React.t) = () ;;for Point { x; y; z } = x to y do () done ;;for Point { x; y; z } = x to y do () done ;;for Point { x; y; z } as p = x to y do () done -;;match truth with | true -> Js.log "true" | false -> Js.log "false" \ No newline at end of file +;;match truth with + | true -> Js.log {js|true|js} + | false -> Js.log {js|false|js} \ No newline at end of file diff --git a/tests/parsing/grammar/pattern/expected/extension.res.txt b/tests/parsing/grammar/pattern/expected/extension.res.txt index 7a627c57..1f14e535 100644 --- a/tests/parsing/grammar/pattern/expected/extension.res.txt +++ b/tests/parsing/grammar/pattern/expected/extension.res.txt @@ -1,8 +1,8 @@ let [%patternExtension ] = () let [%pattern.extension ] = () -let [%bs.raw "x"] = () -let ([%bs.raw "x"] : unit) = () -let [%bs.raw "x"] as y = () +let [%bs.raw {js|x|js}] = () +let ([%bs.raw {js|x|js}] : unit) = () +let [%bs.raw {js|x|js}] as y = () let [%patExt1 ]|[%patExt2 ] = () ;;match x with | [%patternExtension ] -> () @@ -13,10 +13,10 @@ let [%patExt1 ]|[%patExt2 ] = () | [%patExt1 ]|[%patExt2 ] -> () let f [%patternExtension ] = () let f [%pattern.extension ] = () -let f [%bs.raw "x"] = () -let f [%bs.raw "x"] [%bs.raw "y"] = () -let f ([%bs.raw "x"] as _y) = () -let f ([%bs.raw "x"] : unit) = () +let f [%bs.raw {js|x|js}] = () +let f [%bs.raw {js|x|js}] [%bs.raw {js|y|js}] = () +let f ([%bs.raw {js|x|js}] as _y) = () +let f ([%bs.raw {js|x|js}] : unit) = () let f ([%patExt1 ]|[%patExt2 ]) = () ;;for [%ext ] = x to y do () done ;;for [%ext1 ]|[%ext2 ] = x to y do () done diff --git a/tests/parsing/grammar/signature/expected/include.res.txt b/tests/parsing/grammar/signature/expected/include.res.txt index 2311b4fd..0ffe798a 100644 --- a/tests/parsing/grammar/signature/expected/include.res.txt +++ b/tests/parsing/grammar/signature/expected/include.res.txt @@ -11,7 +11,7 @@ module type Sig = include ((sig val s : string val y : int end)[@onSignature ])[@@onInclude ] include [%extension ] - include [%extension.with.args "foo"] + include [%extension.with.args {js|foo|js}] include (([%extension ])[@onExtension ])[@@onInclude ] include (Foo with type t = string) include (((Foo)[@onModType ]) with type t = string)[@@onInclude ] diff --git a/tests/parsing/grammar/signature/expected/itemExtension.res.txt b/tests/parsing/grammar/signature/expected/itemExtension.res.txt index 474fade5..db4bcef9 100644 --- a/tests/parsing/grammar/signature/expected/itemExtension.res.txt +++ b/tests/parsing/grammar/signature/expected/itemExtension.res.txt @@ -1,7 +1,7 @@ module type Ext = sig [%%item.extension ] - [%%item.extension.with.args "argument"] + [%%item.extension.with.args {js|argument|js}] [%%item.extension.with.args fun x -> f x] [%%item.extension ][@@withAttr ] end \ No newline at end of file diff --git a/tests/parsing/grammar/structure/expected/itemExtension.res.txt b/tests/parsing/grammar/structure/expected/itemExtension.res.txt index 15d48742..ae308fb4 100644 --- a/tests/parsing/grammar/structure/expected/itemExtension.res.txt +++ b/tests/parsing/grammar/structure/expected/itemExtension.res.txt @@ -1,5 +1,5 @@ [%%itemExtension ] [%%item.extension ] -[%%item.extension.with.args "argument"] +[%%item.extension.with.args {js|argument|js}] [%%item.extension.with.args fun x -> f x] [%%itemExtension ][@@attrOnExtension ] \ No newline at end of file diff --git a/tests/parsing/grammar/structure/expected/modExprExtension.res.txt b/tests/parsing/grammar/structure/expected/modExprExtension.res.txt index 09d750a4..88d34045 100644 --- a/tests/parsing/grammar/structure/expected/modExprExtension.res.txt +++ b/tests/parsing/grammar/structure/expected/modExprExtension.res.txt @@ -1,4 +1,4 @@ module A = [%modExprExtension ] module B = [%mod.expr.extension ] -module C = [%mod.expr.extension.with.args "argument"] +module C = [%mod.expr.extension.with.args {js|argument|js}] module D = [%mod.expr.extension.with.args fun x -> f x] \ No newline at end of file diff --git a/tests/parsing/grammar/structure/expected/moduleTypeExtension.res.txt b/tests/parsing/grammar/structure/expected/moduleTypeExtension.res.txt index f8b85d50..7b0932f9 100644 --- a/tests/parsing/grammar/structure/expected/moduleTypeExtension.res.txt +++ b/tests/parsing/grammar/structure/expected/moduleTypeExtension.res.txt @@ -1,4 +1,4 @@ module type A = [%modTypeExtension ] module type B = [%mod.type.extension ] -module type C = [%mod.type.extension.with.args "argument"] +module type C = [%mod.type.extension.with.args {js|argument|js}] module type D = [%mod.type.extension.with.args fun x -> f x] \ No newline at end of file diff --git a/tests/parsing/grammar/structure/expected/standaloneAttribute.res.txt b/tests/parsing/grammar/structure/expected/standaloneAttribute.res.txt index 86f72f08..53adcb3b 100644 --- a/tests/parsing/grammar/structure/expected/standaloneAttribute.res.txt +++ b/tests/parsing/grammar/structure/expected/standaloneAttribute.res.txt @@ -1,3 +1,3 @@ [@@@standaloneAttribute ] -[@@@standaloneAttribute "with payload"] +[@@@standaloneAttribute {js|with payload|js}] [@@@standaloneAttribute fun x -> x + 1] \ No newline at end of file diff --git a/tests/parsing/grammar/typedefinition/expected/privateTypeEquation.res.txt b/tests/parsing/grammar/typedefinition/expected/privateTypeEquation.res.txt index ed8a5d59..029b6a13 100644 --- a/tests/parsing/grammar/typedefinition/expected/privateTypeEquation.res.txt +++ b/tests/parsing/grammar/typedefinition/expected/privateTypeEquation.res.txt @@ -9,5 +9,5 @@ type nonrec t = private int -> x:((string)[@ns.namedArgLoc ]) -> float -> unit type nonrec t = private string as 'x type nonrec t = private [%ext ] -type nonrec t = private [%ext "console.log"] +type nonrec t = private [%ext {js|console.log|js}] type nonrec t = private React.element \ No newline at end of file diff --git a/tests/parsing/grammar/typexpr/expected/alias.res.txt b/tests/parsing/grammar/typexpr/expected/alias.res.txt index 0a6f8ff4..7cfd6941 100644 --- a/tests/parsing/grammar/typexpr/expected/alias.res.txt +++ b/tests/parsing/grammar/typexpr/expected/alias.res.txt @@ -11,7 +11,7 @@ type nonrec t = (int as 'r, int as 'g, int as 'b) color as 'rgb type nonrec t = (int as 'r, int as 'g, int as 'b) Color.t as 'rgb type nonrec t = [%t ] as 'extension type nonrec t = [%t.typ ] as 'extension -type nonrec t = [%ext.foo "raw"] as 'extension +type nonrec t = [%ext.foo {js|raw|js}] as 'extension type nonrec tup = ((int as 'x) * (int as 'y)) as 'tupleAlias let (t : string as 's) = () let (t : _ as 'underscore) = () @@ -26,5 +26,5 @@ let (t : (int as 'r, int as 'g, int as 'b) color as 'rgb) = () let (t : (int as 'r, int as 'g, int as 'b) Color.t as 'rgb) = () let (t : [%t ] as 'extension) = () let (t : [%t.typ ] as 'extension) = () -let (t : [%ext.foo "raw"] as 'extension) = () +let (t : [%ext.foo {js|raw|js}] as 'extension) = () let (t : ((int as 'x) * (int as 'y)) as 'tupleAlias) = () \ No newline at end of file diff --git a/tests/parsing/grammar/typexpr/expected/extension.res.txt b/tests/parsing/grammar/typexpr/expected/extension.res.txt index 06ee0977..2b992350 100644 --- a/tests/parsing/grammar/typexpr/expected/extension.res.txt +++ b/tests/parsing/grammar/typexpr/expected/extension.res.txt @@ -1,6 +1,6 @@ type nonrec t = [%typ ] type nonrec t = [%raw.typ ] -type nonrec t = [%raw.typ "existential"] +type nonrec t = [%raw.typ {js|existential|js}] let (t : [%typ ]) = x let (t : [%raw.typ ]) = x -let (t : [%raw.typ "ex"]) = x \ No newline at end of file +let (t : [%raw.typ {js|ex|js}]) = x \ No newline at end of file diff --git a/tests/parsing/grammar/typexpr/expected/objectTypeSpreading.res.txt b/tests/parsing/grammar/typexpr/expected/objectTypeSpreading.res.txt index 4ac552a2..4349bb0e 100644 --- a/tests/parsing/grammar/typexpr/expected/objectTypeSpreading.res.txt +++ b/tests/parsing/grammar/typexpr/expected/objectTypeSpreading.res.txt @@ -7,9 +7,12 @@ type nonrec t = < a ;u: int > -> unit type nonrec t = (< a ;u: int > as 'a) -> unit type nonrec t = < a ;u: int > -> < a ;v: int > -> unit type nonrec user = < name: string > -let (steve : < user ;age: int > ) = [%obj { name = "Steve"; age = 30 }] -let steve = ([%obj { name = "Steve"; age = 30 }] : < user ;age: int > ) -let steve = ((([%obj { name = "Steve"; age = 30 }] : < user ;age: int > )) +let (steve : < user ;age: int > ) = + [%obj { name = {js|Steve|js}; age = 30 }] +let steve = + ([%obj { name = {js|Steve|js}; age = 30 }] : < user ;age: int > ) +let steve = + ((([%obj { name = {js|Steve|js}; age = 30 }] : < user ;age: int > )) [@ns.braces ]) let printFullUser (steve : < user ;age: int > ) = Js.log steve let printFullUser ~user:(((user : < user ;age: int > ))[@ns.namedArgLoc ]) diff --git a/tests/parsing/infiniteLoops/expected/jsxChildren.res.txt b/tests/parsing/infiniteLoops/expected/jsxChildren.res.txt index 3894e259..16a1df19 100644 --- a/tests/parsing/infiniteLoops/expected/jsxChildren.res.txt +++ b/tests/parsing/infiniteLoops/expected/jsxChildren.res.txt @@ -21,6 +21,6 @@ type nonrec action = | AddUser ;;((string ~children:[] ())[@JSX ]) -let (a : action) = AddUser "test" +let (a : action) = AddUser {js|test|js} ;;etype ;;s = { x = ((list < i) > ([%rescript.exprhole ])) } \ No newline at end of file diff --git a/tests/parsing/infiniteLoops/expected/nonRecTypes.res.txt b/tests/parsing/infiniteLoops/expected/nonRecTypes.res.txt index 24cc9e83..a1595043 100644 --- a/tests/parsing/infiniteLoops/expected/nonRecTypes.res.txt +++ b/tests/parsing/infiniteLoops/expected/nonRecTypes.res.txt @@ -71,7 +71,7 @@ include mutable size: int ; mutable root: 'value node option ; compare: [ [%rescript.typehole ]] Js.Internal.fn } - ;;{js|Arity_2('value, 'value)], int), + ;;(({js|Arity_2('value, 'value)], int), }; }: { @@ -82,46 +82,48 @@ include ( ~size: int, ~root: option(node('value)), - ~compare: Js.Internal.fn([ | |js} + ~compare: Js.Internal.fn([ | |js}) + [@res.template ]) ;;Arity_2 (value, value) ;;int - ;;(t value) = "" - ;;"BS:6.0.1\132\149\166\190\000\000\000#\000\000\000\r\000\000\000&\000\000\000#\145\160\160A\160$size@\160\160A\160$root@\160\160A\160'compare@@" + ;;(t value) = {js||js} + ;;{js|BS:6.0.1\132\149\166\190\000\000\000#\000\000\000\r\000\000\000&\000\000\000#\145\160\160A\160$size@\160\160A\160$root@\160\160A\160'compare@@|js} external sizeSet : 'value t -> int -> unit = "size" - ;;"BS:6.0.1\132\149\166\190\000\000\000\021\000\000\000\t\000\000\000\026\000\000\000\025\176\160\160A\145@\160\160A\004\003@E\151\160$size@" - ;;[|(("use sizeGet instead or use {abstract = light} explicitly") + ;;{js|BS:6.0.1\132\149\166\190\000\000\000\021\000\000\000\t\000\000\000\026\000\000\000\025\176\160\160A\145@\160\160A\004\003@E\151\160$size@|js} + ;;[|(({js|use sizeGet instead or use {abstract = light} explicitly|js}) [@ocaml.deprecated ])|] ;;[|((1)[@internal.arity ])|] external size : 'value t -> int = "" - ;;"BS:6.0.1\132\149\166\190\000\000\000\016\000\000\000\007\000\000\000\020\000\000\000\019\176\160\160A\145@@A\152\160$size@" + ;;{js|BS:6.0.1\132\149\166\190\000\000\000\016\000\000\000\007\000\000\000\020\000\000\000\019\176\160\160A\145@@A\152\160$size@|js} ;;[|((1)[@internal.arity ])|] external sizeGet : 'value t -> int = "" - ;;"BS:6.0.1\132\149\166\190\000\000\000\016\000\000\000\007\000\000\000\020\000\000\000\019\176\160\160A\145@@A\152\160$size@" + ;;{js|BS:6.0.1\132\149\166\190\000\000\000\016\000\000\000\007\000\000\000\020\000\000\000\019\176\160\160A\145@@A\152\160$size@|js} external rootSet : 'value t -> 'value node option -> unit = "root" - ;;"BS:6.0.1\132\149\166\190\000\000\000\021\000\000\000\t\000\000\000\026\000\000\000\025\176\160\160A\145@\160\160A\004\003@E\151\160$root@" - ;;[|(("use rootGet instead or use {abstract = light} explicitly") + ;;{js|BS:6.0.1\132\149\166\190\000\000\000\021\000\000\000\t\000\000\000\026\000\000\000\025\176\160\160A\145@\160\160A\004\003@E\151\160$root@|js} + ;;[|(({js|use rootGet instead or use {abstract = light} explicitly|js}) [@ocaml.deprecated ])|] ;;[|((1)[@internal.arity ])|] external root : 'value t -> 'value node option = "" - ;;"BS:6.0.1\132\149\166\190\000\000\000\016\000\000\000\007\000\000\000\020\000\000\000\019\176\160\160A\145@@A\152\160$root@" + ;;{js|BS:6.0.1\132\149\166\190\000\000\000\016\000\000\000\007\000\000\000\020\000\000\000\019\176\160\160A\145@@A\152\160$root@|js} ;;[|((1)[@internal.arity ])|] external rootGet : 'value t -> 'value node option = "" - ;;"BS:6.0.1\132\149\166\190\000\000\000\016\000\000\000\007\000\000\000\020\000\000\000\019\176\160\160A\145@@A\152\160$root@" - ;;[|(("use compareGet instead or use {abstract = light} explicitly") + ;;{js|BS:6.0.1\132\149\166\190\000\000\000\016\000\000\000\007\000\000\000\020\000\000\000\019\176\160\160A\145@@A\152\160$root@|js} + ;;[|(({js|use compareGet instead or use {abstract = light} explicitly|js}) [@ocaml.deprecated ])|] ;;[|((1)[@internal.arity ])|] external compare : 'value t -> [ [%rescript.typehole ]] Js.Internal.fn - ;;{js|Arity_2('value, 'value)], int) = + ;;(({js|Arity_2('value, 'value)], int) = "" "BS:6.0.1\132\149\166\190\000\000\000\019\000\000\000\007\000\000\000\020\000\000\000\019\176\160\160A\145@@A\152\160'compare@"; [@internal.arity 1] external compareGet: - t('value) => Js.Internal.fn([ | |js} + t('value) => Js.Internal.fn([ | |js}) + [@res.template ]) ;;Arity_2 (value, value) ;;int - ;;"" - ;;"BS:6.0.1\132\149\166\190\000\000\000\019\000\000\000\007\000\000\000\020\000\000\000\019\176\160\160A\145@@A\152\160'compare@" + ;;{js||js} + ;;{js|BS:6.0.1\132\149\166\190\000\000\000\019\000\000\000\007\000\000\000\020\000\000\000\019\176\160\160A\145@@A\152\160'compare@|js} end let has rbt value = (_findNode rbt (rootGet rbt) value) != None let rec minNode node = [%rescript.exprhole ] @@ -143,7 +145,7 @@ include match successor with | None -> let leaf = - createNode ~value:((Js.Internal.raw_expr "0") + createNode ~value:((Js.Internal.raw_expr {js|0|js}) [@ns.namedArgLoc ]) ~color:((Black)[@ns.namedArgLoc ]) ~height:((0.)[@ns.namedArgLoc ]) in let isLeaf = Js.Internal.fn_mk1 (fun x -> x == leaf) in diff --git a/tests/parsing/other/expected/attributes.res.txt b/tests/parsing/other/expected/attributes.res.txt index 53884806..d4aa3e7c 100644 --- a/tests/parsing/other/expected/attributes.res.txt +++ b/tests/parsing/other/expected/attributes.res.txt @@ -4,5 +4,5 @@ let x = 1[@@attr ?var when x == 1] ;;[%ext :val x : int] ;;[%ext :val x : int val y : float] -[%%ext ;;"A" - ;;"B"] \ No newline at end of file +[%%ext ;;{js|A|js} + ;;{js|B|js}] \ No newline at end of file diff --git a/tests/parsing/recovery/expression/expected/if.res.txt b/tests/parsing/recovery/expression/expected/if.res.txt index 1b62a7ba..3e4d5bcb 100644 --- a/tests/parsing/recovery/expression/expected/if.res.txt +++ b/tests/parsing/recovery/expression/expected/if.res.txt @@ -16,4 +16,4 @@ Did you forget a `{` here? -;;if foo = bar then Js.log "if-branch" else Js.log "else-branch" \ No newline at end of file +;;if foo = bar then Js.log {js|if-branch|js} else Js.log {js|else-branch|js} \ No newline at end of file diff --git a/tests/parsing/recovery/expression/expected/list.res.txt b/tests/parsing/recovery/expression/expected/list.res.txt index c8da551e..25507bff 100644 --- a/tests/parsing/recovery/expression/expected/list.res.txt +++ b/tests/parsing/recovery/expression/expected/list.res.txt @@ -32,11 +32,11 @@ let flags = ((let parts = Utils.split_on_char ' ' flags in let rec loop items = ((match items with - | [|"-pp";_ppFlag;rest|] -> loop rest + | [|{js|-pp|js};_ppFlag;rest|] -> loop rest | [|x;rest|] -> [|x;(loop rest)|] | [||] -> [||]) [@ns.braces ]) in - (loop parts) |> (String.concat " ")) + (loop parts) |> (String.concat {js| |js})) [@ns.braces ]) else flags) [@ns.ternary ]) \ No newline at end of file diff --git a/tests/parsing/recovery/string/expected/emptyeof.res.txt b/tests/parsing/recovery/string/expected/emptyeof.res.txt index 9e857f15..62f60c7d 100644 --- a/tests/parsing/recovery/string/expected/emptyeof.res.txt +++ b/tests/parsing/recovery/string/expected/emptyeof.res.txt @@ -7,4 +7,5 @@ This string is missing a double quote at the end -let x = "\n" \ No newline at end of file +let x = {js| +|js} \ No newline at end of file diff --git a/tests/parsing/recovery/string/expected/eof.res.txt b/tests/parsing/recovery/string/expected/eof.res.txt index 1d495390..3faafc64 100644 --- a/tests/parsing/recovery/string/expected/eof.res.txt +++ b/tests/parsing/recovery/string/expected/eof.res.txt @@ -7,4 +7,5 @@ This string is missing a double quote at the end -let x = "eof here\n" \ No newline at end of file +let x = {js|eof here +|js} \ No newline at end of file diff --git a/tests/parsing/recovery/string/expected/es6template.res.txt b/tests/parsing/recovery/string/expected/es6template.res.txt index 53be3098..858d46e6 100644 --- a/tests/parsing/recovery/string/expected/es6template.res.txt +++ b/tests/parsing/recovery/string/expected/es6template.res.txt @@ -7,4 +7,7 @@ Did you forget to close this template expression with a backtick? -let x = ({js|this contains |js} ^ foo) ^ {js|, missing closing|js} \ No newline at end of file +let x = + (((((({js|this contains |js})[@res.template ]) ^ foo)[@res.template ]) ^ + (({js|, missing closing|js})[@res.template ])) + [@res.template ]) \ No newline at end of file diff --git a/tests/parsing/recovery/string/expected/unclosed.res.txt b/tests/parsing/recovery/string/expected/unclosed.res.txt index 308116b1..793509c1 100644 --- a/tests/parsing/recovery/string/expected/unclosed.res.txt +++ b/tests/parsing/recovery/string/expected/unclosed.res.txt @@ -1,2 +1,2 @@ -let x = "unclosed" +let x = {js|unclosed|js} let y = 1 \ No newline at end of file diff --git a/tests/printer/expr/expected/templateLiteral.res.txt b/tests/printer/expr/expected/templateLiteral.res.txt index 445c7767..740691ac 100644 --- a/tests/printer/expr/expected/templateLiteral.res.txt +++ b/tests/printer/expr/expected/templateLiteral.res.txt @@ -9,7 +9,7 @@ let s = `multi string ` -let s = foo +let s = `${foo}` let s = `before${foo}` let s = `before ${foo}`