diff --git a/jscomp/test/hello_res.res b/jscomp/test/hello_res.res index df77c24d46..08b2cec5d5 100644 --- a/jscomp/test/hello_res.res +++ b/jscomp/test/hello_res.res @@ -1,4 +1,17 @@ +@@config({ + flags : [ + "-dparsetree" + ] +}) let b = List.length(list{1,2,3}) let a = b - 1 -Js.log ("hello, res") \ No newline at end of file +Js.log ("hello, res") + +type t = { "x" : int } + + + +let u : t = {"x" : 3 } + +let h = u["x"] \ No newline at end of file diff --git a/lib/4.06.1/unstable/js_compiler.ml b/lib/4.06.1/unstable/js_compiler.ml index c86eb701a3..6d17e3fd1f 100644 --- a/lib/4.06.1/unstable/js_compiler.ml +++ b/lib/4.06.1/unstable/js_compiler.ml @@ -406616,23 +406616,14 @@ let makePropsExternalSig fnName loc namedArgListWithKeyAndRef propsType = { psig_loc = loc; psig_desc = Psig_value (makePropsValue fnName loc namedArgListWithKeyAndRef propsType) } [@@raises Invalid_argument] -(* Build an AST node for the props name when converted to a Js.t inside the function signature *) +(* Build an AST node for the props name when converted to an object inside the function signature *) let makePropsName ~loc name = { ppat_desc = Ppat_var { txt = name; loc }; ppat_loc = loc; ppat_attributes = [] } let makeObjectField loc (str, attrs, type_) = Otag ({ loc; txt = str }, attrs, type_) -(* Build an AST node representing a "closed" Js.t object representing a component's props *) +(* Build an AST node representing a "closed" object representing a component's props *) let makePropsType ~loc namedTypeList = - Typ.mk ~loc - (Ptyp_constr - ( { txt = Ldot (Lident "Js", "t"); loc }, - [ - { - ptyp_desc = Ptyp_object (List.map (makeObjectField loc) namedTypeList, Closed); - ptyp_loc = loc; - ptyp_attributes = []; - }; - ] )) + Typ.mk ~loc (Ptyp_object (List.map (makeObjectField loc) namedTypeList, Closed)) (* Builds an AST node for the entire `external` definition of props *) let makeExternalDecl fnName loc namedArgListWithKeyAndRef namedTypeList = diff --git a/lib/4.06.1/whole_compiler.ml b/lib/4.06.1/whole_compiler.ml index 74fb727f21..b5dead8aed 100644 --- a/lib/4.06.1/whole_compiler.ml +++ b/lib/4.06.1/whole_compiler.ml @@ -409658,23 +409658,14 @@ let makePropsExternalSig fnName loc namedArgListWithKeyAndRef propsType = { psig_loc = loc; psig_desc = Psig_value (makePropsValue fnName loc namedArgListWithKeyAndRef propsType) } [@@raises Invalid_argument] -(* Build an AST node for the props name when converted to a Js.t inside the function signature *) +(* Build an AST node for the props name when converted to an object inside the function signature *) let makePropsName ~loc name = { ppat_desc = Ppat_var { txt = name; loc }; ppat_loc = loc; ppat_attributes = [] } let makeObjectField loc (str, attrs, type_) = Otag ({ loc; txt = str }, attrs, type_) -(* Build an AST node representing a "closed" Js.t object representing a component's props *) +(* Build an AST node representing a "closed" object representing a component's props *) let makePropsType ~loc namedTypeList = - Typ.mk ~loc - (Ptyp_constr - ( { txt = Ldot (Lident "Js", "t"); loc }, - [ - { - ptyp_desc = Ptyp_object (List.map (makeObjectField loc) namedTypeList, Closed); - ptyp_loc = loc; - ptyp_attributes = []; - }; - ] )) + Typ.mk ~loc (Ptyp_object (List.map (makeObjectField loc) namedTypeList, Closed)) (* Builds an AST node for the entire `external` definition of props *) let makeExternalDecl fnName loc namedArgListWithKeyAndRef namedTypeList = @@ -414074,186 +414065,20 @@ let trimSpaces s = let len = String.length s in if len = 0 then s else if String.unsafe_get s 0 = ' ' || String.unsafe_get s (len - 1) = ' ' then ( - let b = Bytes.of_string s in let i = ref 0 in - while !i < len && (Bytes.unsafe_get b !i) = ' ' do + while !i < len && (String.unsafe_get s !i) = ' ' do incr i done; let j = ref (len - 1) in - while !j >= !i && (Bytes.unsafe_get b !j) = ' ' do + while !j >= !i && (String.unsafe_get s !j) = ' ' do decr j done; if !j >= !i then - (Bytes.sub [@doesNotRaise]) b !i (!j - !i + 1) |> Bytes.to_string + (String.sub [@doesNotRaise]) s !i (!j - !i + 1) else "" ) else s end -module Res_character_codes -= struct -#1 "res_character_codes.ml" -let eof = -1 - -let space = 0x0020 -let newline = 0x0A (* \n *) [@@live] -let lineFeed = 0x0A (* \n *) -let carriageReturn = 0x0D (* \r *) -let lineSeparator = 0x2028 -let paragraphSeparator = 0x2029 - -let tab = 0x09 - -let bang = 0x21 -let dot = 0x2E -let colon = 0x3A -let comma = 0x2C -let backtick = 0x60 -(* let question = 0x3F *) -let semicolon = 0x3B -let underscore = 0x5F -let singleQuote = 0x27 -let doubleQuote = 0x22 -let equal = 0x3D -let bar = 0x7C -let tilde = 0x7E -let question = 0x3F -let ampersand = 0x26 -let at = 0x40 -let dollar = 0x24 -let percent = 0x25 - -let lparen = 0x28 -let rparen = 0x29 -let lbracket = 0x5B -let rbracket = 0x5D -let lbrace = 0x7B -let rbrace = 0x7D - -let forwardslash = 0x2F (* / *) -let backslash = 0x5C (* \ *) - -let greaterThan = 0x3E -let hash = 0x23 -let lessThan = 0x3C - -let minus = 0x2D -let plus = 0x2B -let asterisk = 0x2A - -let _0 = 0x30 -let _1 = 0x31 [@@live] -let _2 = 0x32 [@@live] -let _3 = 0x33 [@@live] -let _4 = 0x34 [@@live] -let _5 = 0x35 [@@live] -let _6 = 0x36 [@@live] -let _7 = 0x37 [@@live] -let _8 = 0x38 [@@live] -let _9 = 0x39 - -module Lower = struct - let a = 0x61 - let b = 0x62 - let c = 0x63 [@@live] - let d = 0x64 [@@live] - let e = 0x65 - let f = 0x66 - let g = 0x67 - let h = 0x68 [@@live] - let i = 0x69 [@@live] - let j = 0x6A [@@live] - let k = 0x6B [@@live] - let l = 0x6C [@@live] - let m = 0x6D [@@live] - let n = 0x6E - let o = 0x6F - let p = 0x70 - let q = 0x71 [@@live] - let r = 0x72 - let s = 0x73 [@@live] - let t = 0x74 - let u = 0x75 [@@live] - let v = 0x76 [@@live] - let w = 0x77 [@@live] - let x = 0x78 - let y = 0x79 [@@live] - let z = 0x7A -end - -module Upper = struct - let a = 0x41 - (* let b = 0x42 *) - let c = 0x43 [@@live] - let d = 0x44 [@@live] - let e = 0x45 [@@live] - let f = 0x46 [@@live] - let g = 0x47 - let h = 0x48 [@@live] - let i = 0x49 [@@live] - let j = 0x4A [@@live] - let k = 0x4B [@@live] - let l = 0x4C [@@live] - let m = 0x4D [@@live] - let b = 0x4E [@@live] - let o = 0x4F [@@live] - let p = 0x50 [@@live] - let q = 0x51 [@@live] - let r = 0x52 [@@live] - let s = 0x53 [@@live] - let t = 0x54 [@@live] - let u = 0x55 [@@live] - let v = 0x56 [@@live] - let w = 0x57 [@@live] - let x = 0x58 [@@live] - let y = 0x59 [@@live] - let z = 0x5a -end - -(* returns lower-case ch, ch should be ascii *) -let lower ch = - (* if ch >= Lower.a && ch <= Lower.z then ch else ch + 32 *) - 32 lor ch - -let isLetter ch = - Lower.a <= ch && ch <= Lower.z || - Upper.a <= ch && ch <= Upper.z - -let isUpperCase ch = - Upper.a <= ch && ch <= Upper.z - -let isDigit ch = _0 <= ch && ch <= _9 - -let isHex ch = - (_0 <= ch && ch <= _9) || - (Lower.a <= (lower ch) && (lower ch) <= Lower.f) - - (* - // ES5 7.3: - // The ECMAScript line terminator characters are listed in Table 3. - // Table 3: Line Terminator Characters - // Code Unit Value Name Formal Name - // \u000A Line Feed - // \u000D Carriage Return - // \u2028 Line separator - // \u2029 Paragraph separator - // Only the characters in Table 3 are treated as line terminators. Other new line or line - // breaking characters are treated as white space but not as line terminators. -*) -let isLineBreak ch = - ch == lineFeed - || ch == carriageReturn - || ch == lineSeparator - || ch == paragraphSeparator - -let digitValue ch = - if _0 <= ch && ch <= _9 then - ch - 48 - else if Lower.a <= (lower ch) && (lower ch) <= Lower.f then - (lower ch) - Lower.a + 10 - else - 16 (* larger than any legal value *) - -end module Res_minibuffer : sig #1 "res_minibuffer.mli" type t @@ -414276,7 +414101,7 @@ let create n = let s = (Bytes.create [@doesNotRaise]) n in {buffer = s; position = 0; length = n} -let contents b = Bytes.sub_string b.buffer 0 b.position +let contents b = (Bytes.sub_string [@doesNotRaise]) b.buffer 0 b.position (* Can't be called directly, don't add to the interface *) let resize_internal b more = @@ -417592,7 +417417,6 @@ module Res_token = struct #1 "res_token.ml" module Comment = Res_comment -module CharacterCodes = Res_character_codes type t = | Open @@ -417747,7 +417571,7 @@ let toString = function | ColonEqual -> ":=" | At -> "@" | AtAt -> "@@" | Percent -> "%" | PercentPercent -> "%%" - | Comment c -> "Comment(" ^ (Comment.toString c) ^ ")" + | Comment c -> "Comment" ^ (Comment.toString c) | List -> "list{" | TemplatePart text -> text ^ "${" | TemplateTail text -> "TemplateTail(" ^ text ^ ")" @@ -417805,9 +417629,9 @@ let isKeyword = function let lookupKeyword str = try keywordTable str with | Not_found -> - if CharacterCodes.isUpperCase (int_of_char (str.[0] [@doesNotRaise])) then - Uident str - else Lident str + match str.[0] [@doesNotRaise] with + | 'A'..'Z' -> Uident str + | _ -> Lident str let isKeywordTxt str = try let _ = keywordTable str in true with @@ -417879,6 +417703,7 @@ type t = | ListExpr | JsFfiImport | Pattern + | AttributePayload let toString = function | OpenDescription -> "an open description" @@ -417939,6 +417764,7 @@ let toString = function | JsxChild -> "jsx child" | Pattern -> "pattern" | ExprFor -> "a for expression" + | AttributePayload -> "an attribute payload" let isSignatureItemStart = function | Token.At @@ -418157,6 +417983,7 @@ let isListElement grammar token = | Primitive -> begin match token with Token.String _ -> true | _ -> false end | JsxAttribute -> isJsxAttributeStart token | JsFfiImport -> isJsFfiImportStart token + | AttributePayload -> token = Lparen | _ -> false let isListTerminator grammar token = @@ -418183,6 +418010,7 @@ let isListTerminator grammar token = | ConstructorDeclaration, token when token <> Bar -> true | Primitive, Semicolon -> true | Primitive, token when isStructureItemStart token -> true + | AttributePayload, Rparen -> true | _ -> false @@ -418211,7 +418039,7 @@ val lident: Token.t -> category val unclosedString: category val unclosedTemplate: category val unclosedComment: category -val unknownUchar: int -> category +val unknownUchar: Char.t -> category val message: string -> category val make: @@ -418236,7 +418064,7 @@ type category = | UnclosedString | UnclosedTemplate | UnclosedComment - | UnknownUchar of int + | UnknownUchar of Char.t type t = { startPos: Lexing.position; @@ -418291,10 +418119,12 @@ let explain t = "This comment seems to be missing a closing `*/`" | UnknownUchar uchar -> begin match uchar with - | 94 (* ^ *) -> - "Hmm, not sure what I should do here with this character.\nIf you're trying to deref an expression, use `foo.contents` instead." + | '^' -> + "Not sure what to do with this character.\n" ^ + " If you're trying to dereference a mutable value, use `myValue.contents` instead.\n" ^ + " To concatenate strings, use `\"a\" ++ \"b\"` instead." | _ -> - "Hmm, I have no idea what this character means…" + "Not sure what to do with this character." end | Expected {context; token = t} -> let hint = match context with @@ -418545,28 +418375,29 @@ module Res_scanner : sig #1 "res_scanner.mli" type mode = Jsx | Diamond +type charEncoding + type t = { filename: string; - src: bytes; + src: string; mutable err: startPos: Lexing.position -> endPos: Lexing.position -> Res_diagnostics.category -> unit; - mutable ch: int; (* current character *) + mutable ch: charEncoding; (* current character *) mutable offset: int; (* character offset *) - mutable rdOffset: int; (* reading offset (position after current character) *) mutable lineOffset: int; (* current line offset *) mutable lnum: int; (* current line number *) mutable mode: mode list; } -val make: ?line:int -> filename:string -> bytes -> t +val make: filename:string -> string -> t (* TODO: make this a record *) val scan: t -> (Lexing.position * Lexing.position * Res_token.t) -val isBinaryOp: bytes -> int -> int -> bool +val isBinaryOp: string -> int -> int -> bool val setJsxMode: t -> unit val setDiamondMode: t -> unit @@ -418580,23 +418411,28 @@ val tryAdvanceQuotedString: t -> unit end = struct #1 "res_scanner.ml" -module CharacterCodes = Res_character_codes module Diagnostics = Res_diagnostics module Token = Res_token module Comment = Res_comment + type mode = Jsx | Diamond +(* We hide the implementation detail of the scanner reading character. Our char +will also contain the special -1 value to indicate end-of-file. This isn't +ideal; we should clean this up *) +let hackyEOFChar = Char.unsafe_chr (-1) +type charEncoding = Char.t + type t = { filename: string; - src: bytes; + src: string; mutable err: startPos: Lexing.position -> endPos: Lexing.position -> Diagnostics.category -> unit; - mutable ch: int; (* current character *) + mutable ch: charEncoding; (* current character *) mutable offset: int; (* character offset *) - mutable rdOffset: int; (* reading offset (position after current character) *) mutable lineOffset: int; (* current line offset *) mutable lnum: int; (* current line number *) mutable mode: mode list; @@ -418635,158 +418471,201 @@ let position scanner = Lexing.{ pos_cnum = scanner.offset; } +(* Small debugging util +❯ ./lib/rescript.exe baah/test.res +let a = 1 +^ let +let a = 1 + ^ a +let a = 1 + ^ = +let a = 1 + ^ int 1 +let a = 1 + ^ eof +let a = 1 +*) +let _printDebug ~startPos scanner token = + let open Lexing in + print_endline scanner.src; + print_string ((String.make [@doesNotRaise]) startPos.pos_cnum ' '); + print_char '^'; + print_char ' '; + print_string (Res_token.toString token); + print_char ' '; + print_int startPos.pos_cnum; + print_endline "" +[@@live] + let next scanner = - if scanner.rdOffset < (Bytes.length scanner.src) then ( - scanner.offset <- scanner.rdOffset; - let ch = (Bytes.get [@doesNotRaise]) scanner.src scanner.rdOffset in - scanner.rdOffset <- scanner.rdOffset + 1; - scanner.ch <- int_of_char ch + let nextOffset = scanner.offset + 1 in + (match scanner.ch with + | '\n' | '\r' -> + scanner.lineOffset <- nextOffset; + scanner.lnum <- scanner.lnum + 1; + | _ -> ()); + if nextOffset < String.length scanner.src then ( + scanner.offset <- nextOffset; + scanner.ch <- String.unsafe_get scanner.src scanner.offset; ) else ( - scanner.offset <- Bytes.length scanner.src; - scanner.ch <- -1 + scanner.offset <- String.length scanner.src; + scanner.ch <- hackyEOFChar ) +let next2 scanner = + next scanner; + next scanner + +let next3 scanner = + next scanner; + next scanner; + next scanner + let peek scanner = - if scanner.rdOffset < (Bytes.length scanner.src) then - int_of_char (Bytes.unsafe_get scanner.src scanner.rdOffset) + if scanner.offset + 1 < String.length scanner.src then + String.unsafe_get scanner.src (scanner.offset + 1) else - -1 + hackyEOFChar + +let peek2 scanner = + if scanner.offset + 2 < String.length scanner.src then + String.unsafe_get scanner.src (scanner.offset + 2) + else + hackyEOFChar -let make ?(line=1) ~filename b = - let scanner = { +let make ~filename src = + { filename; - src = b; + src = src; err = (fun ~startPos:_ ~endPos:_ _ -> ()); - ch = CharacterCodes.space; + ch = if src = "" then hackyEOFChar else String.unsafe_get src 0; offset = 0; - rdOffset = 0; lineOffset = 0; - lnum = line; + lnum = 1; mode = []; - } in - next scanner; - scanner + } -let skipWhitespace scanner = - let rec scan () = - if scanner.ch == CharacterCodes.space || scanner.ch == CharacterCodes.tab then ( - next scanner; - scan() - ) else if CharacterCodes.isLineBreak scanner.ch then ( - scanner.lineOffset <- scanner.offset + 1; - scanner.lnum <- scanner.lnum + 1; - next scanner; - scan() - ) else ( - () - ) - in - scan() + +(* generic helpers *) + +let isWhitespace ch = + match ch with + | ' ' | '\t' | '\n' | '\r' -> true + | _ -> false + +let rec skipWhitespace scanner = + if isWhitespace scanner.ch then ( + next scanner; + skipWhitespace scanner + ) + +let digitValue 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 rec skipLowerCaseChars scanner = + match scanner.ch with + | 'a'..'z' -> next scanner; skipLowerCaseChars scanner + | _ -> () + + +(* scanning helpers *) let scanIdentifier scanner = let startOff = scanner.offset in - while ( - CharacterCodes.isLetter scanner.ch || - CharacterCodes.isDigit scanner.ch || - CharacterCodes.underscore == scanner.ch || - CharacterCodes.singleQuote == scanner.ch - ) do - next scanner - done; - let str = Bytes.sub_string scanner.src startOff (scanner.offset - startOff) in - if CharacterCodes.lbrace == scanner.ch && str = "list" - then begin + let rec skipGoodChars scanner = + match scanner.ch with + | 'A'..'Z' | 'a'..'z' | '0'..'9' | '_' | '\'' -> + next scanner; + skipGoodChars scanner + | _ -> () + in + skipGoodChars scanner; + let str = (String.sub [@doesNotRaise]) scanner.src startOff (scanner.offset - startOff) in + if '{' == scanner.ch && str = "list" then begin next scanner; + (* TODO: this isn't great *) Token.lookupKeyword "list{" end else Token.lookupKeyword str let scanDigits scanner ~base = - if base <= 10 then ( - while CharacterCodes.isDigit scanner.ch || scanner.ch == CharacterCodes.underscore do - next scanner - done; - ) else ( - while CharacterCodes.isHex scanner.ch || scanner.ch == CharacterCodes.underscore do - next scanner - done; - ) + if base <= 10 then + let rec loop scanner = + match scanner.ch with + | '0'..'9' | '_' -> next scanner; loop scanner + | _ -> () + in loop scanner + else + let rec loop scanner = + match scanner.ch with + (* hex *) + | '0'..'9' | 'a'..'f' | 'A'..'F' | '_' -> next scanner; loop scanner + | _ -> () + in loop scanner (* float: (0…9) { 0…9∣ _ } [. { 0…9∣ _ }] [(e∣ E) [+∣ -] (0…9) { 0…9∣ _ }] *) let scanNumber scanner = let startOff = scanner.offset in (* integer part *) - let base, _prefix = if scanner.ch != CharacterCodes.dot then ( - if scanner.ch == CharacterCodes._0 then ( - next scanner; - let ch = CharacterCodes.lower scanner.ch in - if ch == CharacterCodes.Lower.x then ( - next scanner; - 16, 'x' - ) else if ch == CharacterCodes.Lower.o then ( - next scanner; - 8, 'o' - ) else if ch == CharacterCodes.Lower.b then ( - next scanner; - 2, 'b' - ) else ( - 8, '0' - ) - ) else ( - 10, ' ' - ) - ) else (10, ' ') + let base = match scanner.ch with + | '0' -> + (match peek scanner with + | 'x' | 'X' -> next2 scanner; 16 + | 'o' | 'O' -> next2 scanner; 8 + | 'b' | 'B' -> next2 scanner; 2 + | _ -> next scanner; 8) + | _ -> 10 in scanDigits scanner ~base; (* *) - let isFloat = if CharacterCodes.dot == scanner.ch then ( + let isFloat = if '.' == scanner.ch then ( next scanner; scanDigits scanner ~base; true - ) else ( + ) else false - ) in + in (* exponent part *) let isFloat = - if let exp = CharacterCodes.lower scanner.ch in - exp == CharacterCodes.Lower.e || exp == CharacterCodes.Lower.p - then ( - next scanner; - if scanner.ch == CharacterCodes.plus || scanner.ch == CharacterCodes.minus then - next scanner; + match scanner.ch with + | 'e' | 'E' | 'p' | 'P' -> + (match peek scanner with + | '+' | '-' -> next2 scanner + | _ -> next scanner); scanDigits scanner ~base; true - ) else - isFloat + | _ -> isFloat in let literal = - Bytes.sub_string scanner.src startOff (scanner.offset - startOff) + (String.sub [@doesNotRaise]) scanner.src startOff (scanner.offset - startOff) in (* suffix *) let suffix = - if scanner.ch >= CharacterCodes.Lower.g && scanner.ch <= CharacterCodes.Lower.z - || scanner.ch >= CharacterCodes.Upper.g && scanner.ch <= CharacterCodes.Upper.z - then ( - let ch = scanner.ch in - if CharacterCodes.Lower.n = ch then ( - let msg = - "Unsupported number type (nativeint). Did you mean `" - ^ literal - ^ "`?" - in - let pos = position scanner in - scanner.err - ~startPos:pos - ~endPos:pos - (Diagnostics.message msg) - ); + match scanner.ch with + | 'n' -> + let msg = + "Unsupported number type (nativeint). Did you mean `" + ^ literal + ^ "`?" + in + let pos = position scanner in + scanner.err ~startPos:pos ~endPos:pos (Diagnostics.message msg); next scanner; - Some (Char.unsafe_chr ch) - ) else + Some 'n' + | 'g'..'z' | 'G'..'Z' as ch -> + next scanner; + Some ch + | _ -> None in if isFloat then @@ -418795,489 +418674,370 @@ let scanNumber scanner = Token.Int {i = literal; suffix} let scanExoticIdentifier scanner = + (* TODO: are we disregarding the current char...? Should be a quote *) next scanner; let buffer = Buffer.create 20 in let startPos = position scanner in let rec scan () = - if scanner.ch == CharacterCodes.eof then + match scanner.ch with + | '"' -> next scanner + | '\n' | '\r' -> + (* line break *) let endPos = position scanner in - scanner.err ~startPos ~endPos (Diagnostics.message "Did you forget a \" here?") - else if scanner.ch == CharacterCodes.doubleQuote then ( + scanner.err ~startPos ~endPos (Diagnostics.message "A quoted identifier can't contain line breaks."); next scanner - ) else if CharacterCodes.isLineBreak scanner.ch then ( - scanner.lineOffset <- scanner.offset + 1; - scanner.lnum <- scanner.lnum + 1; + | ch when ch == hackyEOFChar -> let endPos = position scanner in - scanner.err ~startPos ~endPos (Diagnostics.message "Did you forget a \" here?"); - next scanner - ) else ( - Buffer.add_char buffer ((Char.chr [@doesNotRaise]) scanner.ch); + scanner.err ~startPos ~endPos (Diagnostics.message "Did you forget a \" here?") + | ch -> + Buffer.add_char buffer ch; next scanner; - scan() - ) + scan () in - scan(); + scan (); + (* TODO: do we really need to create a new buffer instead of substring once? *) Token.Lident (Buffer.contents buffer) let scanStringEscapeSequence ~startPos scanner = - (* \ already consumed *) - if CharacterCodes.Lower.n == scanner.ch - || CharacterCodes.Lower.t == scanner.ch - || CharacterCodes.Lower.b == scanner.ch - || CharacterCodes.Lower.r == scanner.ch - || CharacterCodes.backslash == scanner.ch - || CharacterCodes.space == scanner.ch - || CharacterCodes.singleQuote == scanner.ch - || CharacterCodes.doubleQuote == scanner.ch - then - next scanner - else - let (n, base, max) = - if CharacterCodes.isDigit scanner.ch then - (* decimal *) - (3, 10, 255) - else if scanner.ch == CharacterCodes.Lower.o then - (* octal *) - let () = next scanner in - (3, 8, 255) - else if scanner.ch == CharacterCodes.Lower.x then - (* hex *) - let () = next scanner in - (2, 16, 255) - else - (* unknown escape sequence - * TODO: we should warn the user here. Let's not make it a hard error for now, for reason compat *) - (* let pos = position scanner in *) - (* let () = *) - (* let msg = if scanner.ch == -1 then *) - (* "unclosed escape sequence" *) - (* else "unknown escape sequence" *) - (* in *) - (* scanner.err ~startPos ~endPos:pos (Diagnostics.message msg) *) - (* in *) - (-1, -1, -1) - in - if n < 0 then () + let scan ~n ~base ~max = + let rec loop n x = + if n == 0 then x else - let rec while_ n x = - if n == 0 then x - else - let d = CharacterCodes.digitValue scanner.ch in - if d >= base then - let pos = position scanner in - let msg = if scanner.ch == -1 then - "unclosed escape sequence" - else "unknown escape sequence" - in - scanner.err ~startPos ~endPos:pos (Diagnostics.message msg); - -1 - else - let () = next scanner in - while_ (n - 1) (x * base + d) - in - let x = while_ n 0 in - if x > max then + let d = digitValue scanner.ch in + if d >= base then let pos = position scanner in - let msg = "invalid escape sequence (value too high)" in + let msg = + if scanner.ch == hackyEOFChar then "unclosed escape sequence" + else "unknown escape sequence" + in scanner.err ~startPos ~endPos:pos (Diagnostics.message msg); - () + -1 + else + let () = next scanner in + loop (n - 1) (x * base + d) + in + let x = loop n 0 in + if x > max then + let pos = position scanner in + let msg = "invalid escape sequence (value too high)" in + scanner.err ~startPos ~endPos:pos (Diagnostics.message msg) + in + match scanner.ch with + (* \ already consumed *) + | 'n' | 't' | 'b' | 'r' | '\\' | ' ' | '\'' | '"' -> + next scanner + | '0'..'9' -> + (* decimal *) + scan ~n:3 ~base:10 ~max:255 + | 'o' -> + (* octal *) + next scanner; + scan ~n:3 ~base:8 ~max:255 + | 'x' -> + (* hex *) + next scanner; + scan ~n:2 ~base:16 ~max:255 + | _ -> + (* unknown escape sequence + * TODO: we should warn the user here. Let's not make it a hard error for now, for reason compat *) + (* + let pos = position scanner in + let msg = + if ch == -1 then "unclosed escape sequence" + else "unknown escape sequence" + in + scanner.err ~startPos ~endPos:pos (Diagnostics.message msg) + *) + () let scanString scanner = let offs = scanner.offset in let startPos = position scanner in let rec scan () = - if scanner.ch == CharacterCodes.eof then - let endPos = position scanner in - scanner.err ~startPos ~endPos Diagnostics.unclosedString - else if scanner.ch == CharacterCodes.doubleQuote then ( - next scanner; - ) else if scanner.ch == CharacterCodes.backslash then ( + match scanner.ch with + | '"' -> next scanner + | '\\' -> let startPos = position scanner in next scanner; scanStringEscapeSequence ~startPos scanner; scan () - ) else if CharacterCodes.isLineBreak scanner.ch then ( - scanner.lineOffset <- scanner.offset + 1; - scanner.lnum <- scanner.lnum + 1; - next scanner; - scan () - ) else ( + | ch when ch == hackyEOFChar -> + let endPos = position scanner in + scanner.err ~startPos ~endPos Diagnostics.unclosedString + | _ -> next scanner; scan () - ) in scan (); - Token.String (Bytes.sub_string scanner.src offs (scanner.offset - offs - 1)) - -(* I wonder if this gets inlined *) -let convertNumber scanner ~n ~base = - let x = ref 0 in - for _ = n downto 1 do - let d = CharacterCodes.digitValue scanner.ch in - x := (!x * base) + d; - next scanner - done; - !x + Token.String ((String.sub [@doesNotRaise]) scanner.src offs (scanner.offset - offs - 1)) let scanEscape scanner = + let convertNumber scanner ~n ~base = + let x = ref 0 in + for _ = n downto 1 do + let d = digitValue scanner.ch in + x := (!x * base) + d; + next scanner + done; + (Char.chr [@doesNotRaise]) !x + in (* let offset = scanner.offset in *) let c = match scanner.ch with - | 98 (* b *) -> next scanner; '\008' - | 110 (* n *) -> next scanner; '\010' - | 114 (* r *) -> next scanner; '\013' - | 116 (* t *) -> next scanner; '\009' - | ch when CharacterCodes.isDigit ch -> - let x = convertNumber scanner ~n:3 ~base:10 in - (Char.chr [@doesNotRaise]) x - | ch when ch == CharacterCodes.Lower.x -> - next scanner; - let x = convertNumber scanner ~n:2 ~base:16 in - (Char.chr [@doesNotRaise]) x - | ch when ch == CharacterCodes.Lower.o -> - next scanner; - let x = convertNumber scanner ~n:3 ~base:8 in - (Char.chr [@doesNotRaise]) x - | ch -> - next scanner; - (Char.chr [@doesNotRaise]) ch + | '0'..'9' -> convertNumber scanner ~n:3 ~base:10 + | 'b' -> next scanner; '\008' + | 'n' -> next scanner; '\010' + | 'r' -> next scanner; '\013' + | 't' -> next scanner; '\009' + | 'x' -> next scanner; convertNumber scanner ~n:2 ~base:16 + | 'o' -> next scanner; convertNumber scanner ~n:3 ~base:8 + | ch -> next scanner; ch in next scanner; (* Consume \' *) + (* TODO: do we know it's \' ? *) Token.Character c let scanSingleLineComment scanner = let startOff = scanner.offset in let startPos = position scanner in - while not (CharacterCodes.isLineBreak scanner.ch) && scanner.ch >= 0 do - next scanner - done; + let rec skip scanner = + match scanner.ch with + | '\n' | '\r' -> () + | ch when ch == hackyEOFChar -> () + | _ -> + next scanner; + skip scanner + in + skip scanner; let endPos = position scanner in Token.Comment ( Comment.makeSingleLineComment ~loc:(Location.{loc_start = startPos; loc_end = endPos; loc_ghost = false}) - (Bytes.sub_string scanner.src startOff (scanner.offset - startOff)) + ((String.sub [@doesNotRaise]) scanner.src startOff (scanner.offset - startOff)) ) let scanMultiLineComment scanner = - let startOff = scanner.offset in + (* assumption: we're only ever using this helper in `scan` after detecting a comment *) + let contentStartOff = scanner.offset + 2 in let startPos = position scanner in - let rec scan ~depth () = - if scanner.ch == CharacterCodes.asterisk && - peek scanner == CharacterCodes.forwardslash then ( - next scanner; - next scanner; - if depth > 0 then scan ~depth:(depth - 1) () else () - ) else if scanner.ch == CharacterCodes.eof then ( + let rec scan ~depth = + (* invariant: depth > 0 right after this match. See assumption *) + match scanner.ch, peek scanner with + | '/', '*' -> + next2 scanner; + scan ~depth:(depth + 1) + | '*', '/' -> + next2 scanner; + if depth > 1 then scan ~depth:(depth - 1) + | ch, _ when ch == hackyEOFChar -> let endPos = position scanner in scanner.err ~startPos ~endPos Diagnostics.unclosedComment - ) else if scanner.ch == CharacterCodes.forwardslash - && peek scanner == CharacterCodes. asterisk then ( - next scanner; - next scanner; - scan ~depth:(depth + 1) () - ) else ( - if CharacterCodes.isLineBreak scanner.ch then ( - scanner.lineOffset <- scanner.offset + 1; - scanner.lnum <- scanner.lnum + 1; - ); + | _ -> next scanner; - scan ~depth () - ) + scan ~depth in - scan ~depth:0 (); + scan ~depth:0; Token.Comment ( Comment.makeMultiLineComment ~loc:(Location.{loc_start = startPos; loc_end = (position scanner); loc_ghost = false}) - (Bytes.sub_string scanner.src startOff (scanner.offset - 2 - startOff)) + ((String.sub [@doesNotRaise]) scanner.src contentStartOff (scanner.offset - 2 - contentStartOff)) ) let scanTemplateLiteralToken scanner = let startOff = scanner.offset in (* if starting } here, consume it *) - if scanner.ch == CharacterCodes.rbrace then ( - next scanner - ); + if scanner.ch == '}' then next scanner; + let startPos = position scanner in let rec scan () = - if scanner.ch == CharacterCodes.eof then ( - let endPos = position scanner in - scanner.err ~startPos ~endPos Diagnostics.unclosedTemplate; + match scanner.ch with + | '`' -> + next scanner; Token.TemplateTail( - Bytes.sub_string scanner.src startOff (scanner.offset - 1 - startOff) + (String.sub [@doesNotRaise]) scanner.src startOff (scanner.offset - 1 - startOff) ) - ) else if scanner.ch == CharacterCodes.backtick then ( - next scanner; + | '$' -> + (match peek scanner with + | '{' -> + next2 scanner; + let contents = + (String.sub [@doesNotRaise]) scanner.src startOff (scanner.offset - 2 - startOff) + in + Token.TemplatePart contents + | _ -> + next2 scanner; + scan()) + | '\\' -> + (match peek scanner with + | '`' | '\\' | '$' + | '\n' | '\r' -> + (* line break *) + next2 scanner; + scan () + | _ -> + next scanner; + scan ()) + | ch when ch == hackyEOFChar -> + let endPos = position scanner in + scanner.err ~startPos ~endPos Diagnostics.unclosedTemplate; Token.TemplateTail( - Bytes.sub_string scanner.src startOff (scanner.offset - 1 - startOff) + (String.sub [@doesNotRaise]) scanner.src startOff (scanner.offset - 1 - startOff) ) - ) else if scanner.ch == CharacterCodes.dollar && - (peek scanner) == CharacterCodes.lbrace then ( - next scanner; (* consume $ *) - next scanner; (* consume { *) - let contents = - Bytes.sub_string scanner.src startOff (scanner.offset - 2 - startOff) - in - Token.TemplatePart contents - ) else if scanner.ch == CharacterCodes.backslash then ( - next scanner; - if scanner.ch == CharacterCodes.backtick - || scanner.ch == CharacterCodes.backslash - || scanner.ch == CharacterCodes.dollar - || CharacterCodes.isLineBreak scanner.ch - then next scanner; - scan() - ) else ( - if CharacterCodes.isLineBreak scanner.ch then ( - scanner.lineOffset <- scanner.offset + 1; - scanner.lnum <- scanner.lnum + 1; - ); + | _ -> next scanner; - scan() - ) + scan () in - let token = scan() in + let token = scan () in let endPos = position scanner in (startPos, endPos, token) let rec scan scanner = skipWhitespace scanner; let startPos = position scanner in - let ch = scanner.ch in - let token = if ch == CharacterCodes.underscore then ( - let nextCh = peek scanner in - if nextCh == CharacterCodes.underscore || CharacterCodes.isDigit nextCh || CharacterCodes.isLetter nextCh then - scanIdentifier scanner - else ( - next scanner; - Token.Underscore - ) - ) else if CharacterCodes.isLetter ch then - scanIdentifier scanner - else if CharacterCodes.isDigit ch then - scanNumber scanner - else begin + + let token = match scanner.ch with + (* peeking 0 char *) + | 'A'..'Z' | 'a'..'z' -> scanIdentifier scanner + | '0'..'9' -> scanNumber scanner + | '`' -> next scanner; Token.Backtick + | '~' -> next scanner; Token.Tilde + | '?' -> next scanner; Token.Question + | ';' -> next scanner; Token.Semicolon + | '(' -> next scanner; Token.Lparen + | ')' -> next scanner; Token.Rparen + | '[' -> next scanner; Token.Lbracket + | ']' -> next scanner; Token.Rbracket + | '{' -> next scanner; Token.Lbrace + | '}' -> next scanner; Token.Rbrace + | ',' -> next scanner; Token.Comma + | '"' -> next scanner; scanString scanner + + (* peeking 1 char *) + | '_' -> + (match peek scanner with + | 'A'..'Z' | 'a'..'z' | '0'..'9' | '_' -> scanIdentifier scanner + | _ -> next scanner; Token.Underscore) + | '#' -> + (match peek scanner with + | '=' -> next2 scanner; Token.HashEqual + | _ -> next scanner; Token.Hash) + | '*' -> + (match peek scanner with + | '*' -> next2 scanner; Token.Exponentiation + | '.' -> next2 scanner; Token.AsteriskDot + | _ -> next scanner; Token.Asterisk) + | '@' -> + (match peek scanner with + | '@' -> next2 scanner; Token.AtAt + | _ -> next scanner; Token.At) + | '%' -> + (match peek scanner with + | '%' -> next2 scanner; Token.PercentPercent + | _ -> next scanner; Token.Percent) + | '|' -> + (match peek scanner with + | '|' -> next2 scanner; Token.Lor + | '>' -> next2 scanner; Token.BarGreater + | _ -> next scanner; Token.Bar) + | '&' -> + (match peek scanner with + | '&' -> next2 scanner; Token.Land + | _ -> next scanner; Token.Band) + | ':' -> + (match peek scanner with + | '=' -> next2 scanner; Token.ColonEqual + | '>' -> next2 scanner; Token.ColonGreaterThan + | _ -> next scanner; Token.Colon) + | '\\' -> next scanner; scanExoticIdentifier scanner + | '/' -> + (match peek scanner with + | '/' -> next2 scanner; scanSingleLineComment scanner + | '*' -> scanMultiLineComment scanner + | '.' -> next2 scanner; Token.ForwardslashDot + | _ -> next scanner; Token.Forwardslash) + | '-' -> + (match peek scanner with + | '.' -> next2 scanner; Token.MinusDot + | '>' -> next2 scanner; Token.MinusGreater + | _ -> next scanner; Token.Minus) + | '+' -> + (match peek scanner with + | '.' -> next2 scanner; Token.PlusDot + | '+' -> next2 scanner; Token.PlusPlus + | '=' -> next2 scanner; Token.PlusEqual + | _ -> next scanner; Token.Plus) + | '>' -> + (match peek scanner with + | '=' when not (inDiamondMode scanner) -> next2 scanner; Token.GreaterEqual + | _ -> next scanner; Token.GreaterThan) + | '<' when not (inJsxMode scanner) -> + (match peek scanner with + | '=' -> next2 scanner; Token.LessEqual + | _ -> next scanner; Token.LessThan) + (* special handling for JSX < *) + | '<' -> + (* Imagine the following:
< + * < indicates the start of a new jsx-element, the parser expects + * the name of a new element after the < + * Example:
+ * This signals a closing element. To simulate the two-token lookahead, + * the < - * < indicates the start of a new jsx-element, the parser expects - * the name of a new element after the < - * Example:
- * This signals a closing element. To simulate the two-token lookahead, - * the next scanner; Token.LessThanSlash + | '=' -> next scanner; Token.LessEqual + | _ -> Token.LessThan) + + (* peeking 2 chars *) + | '.' -> + (match peek scanner, peek2 scanner with + | '.', '.' -> next3 scanner; Token.DotDotDot + | '.', _ -> next2 scanner; Token.DotDot + | _ -> next scanner; Token.Dot) + | '\'' -> + (match peek scanner, peek2 scanner with + | '\\', '"' -> + (* careful with this one! We're next-ing _once_ (not twice), + then relying on matching on the quote *) + next scanner; SingleQuote + | '\\', _ -> next2 scanner; scanEscape scanner + | ch, '\'' -> next3 scanner; Token.Character ch + | _ -> next scanner; SingleQuote) + | '!' -> + (match peek scanner, peek2 scanner with + | '=', '=' -> next3 scanner; Token.BangEqualEqual + | '=', _ -> next2 scanner; Token.BangEqual + | _ -> next scanner; Token.Bang) + | '=' -> + (match peek scanner, peek2 scanner with + | '=', '=' -> next3 scanner; Token.EqualEqualEqual + | '=', _ -> next2 scanner; Token.EqualEqual + | '>', _ -> next2 scanner; Token.EqualGreater + | _ -> next scanner; Token.Equal) + + (* special cases *) + | ch when ch == hackyEOFChar -> next scanner; Token.Eof + | ch -> + (* if we arrive here, we're dealing with an unknown character, + * report the error and continue scanning… *) + next scanner; + let endPos = position scanner in + scanner.err ~startPos ~endPos (Diagnostics.unknownUchar ch); + let (_, _, token) = scan scanner in + token + in let endPos = position scanner in + (* _printDebug ~startPos scanner token; *) (startPos, endPos, token) + +(* misc helpers used elsewhere *) + (* Imagine:
< * is `<` the start of a jsx-child?
@@ -419286,87 +419046,67 @@ let rec scan scanner = let reconsiderLessThan scanner = (* < consumed *) skipWhitespace scanner; - if scanner.ch == CharacterCodes.forwardslash then + if scanner.ch == '/' then let () = next scanner in Token.LessThanSlash else Token.LessThan (* If an operator has whitespace around both sides, it's a binary operator *) +(* TODO: this helper seems out of place *) let isBinaryOp src startCnum endCnum = if startCnum == 0 then false - else - let leftOk = - let c = - (startCnum - 1) - |> (Bytes.get [@doesNotRaise]) src - |> Char.code - in - c == CharacterCodes.space || - c == CharacterCodes.tab || - CharacterCodes.isLineBreak c - in - let rightOk = - let c = - if endCnum == Bytes.length src then -1 - else endCnum |> (Bytes.get [@doesNotRaise]) src |> Char.code - in - c == CharacterCodes.space || - c == CharacterCodes.tab || - CharacterCodes.isLineBreak c || - c == CharacterCodes.eof - in + else begin + (* we're gonna put some assertions and invariant checks here because this is + used outside of the scanner's normal invariant assumptions *) + assert (endCnum >= 0); + assert (startCnum > 0 && startCnum < String.length src); + let leftOk = isWhitespace (String.unsafe_get src (startCnum - 1)) in + (* we need some stronger confidence that endCnum is ok *) + let rightOk = endCnum >= String.length src || isWhitespace (String.unsafe_get src endCnum) in leftOk && rightOk + end (* Assume `{` consumed, advances the scanner towards the ends of Reason quoted strings. (for conversion) * In {| foo bar |} the scanner will be advanced until after the `|}` *) let tryAdvanceQuotedString scanner = - let rec scanContents tag () = - if scanner.ch == CharacterCodes.eof then ( - () - ) else if scanner.ch == CharacterCodes.bar then ( + let rec scanContents tag = + match scanner.ch with + | '|' -> next scanner; - if CharacterCodes.Lower.a <= scanner.ch && scanner.ch <= CharacterCodes.Lower.z then ( + (match scanner.ch with + | 'a'..'z' -> let startOff = scanner.offset in - while CharacterCodes.Lower.a <= scanner.ch && scanner.ch <= CharacterCodes.Lower.z do - next scanner - done; - let suffix = Bytes.sub_string scanner.src startOff (scanner.offset - startOff) in + skipLowerCaseChars scanner; + let suffix = + (String.sub [@doesNotRaise]) scanner.src startOff (scanner.offset - startOff) + in begin if tag = suffix then ( - if scanner.ch = CharacterCodes.rbrace then + if scanner.ch = '}' then next scanner else - scanContents tag () + scanContents tag ) else - scanContents tag () - ) else if CharacterCodes.rbrace = scanner.ch then ( - next scanner - ) else ( - scanContents tag () - ) - ) else ( - if CharacterCodes.isLineBreak scanner.ch then ( - scanner.lineOffset <- scanner.offset + 1; - scanner.lnum <- scanner.lnum + 1; - ); + scanContents tag + end + | '}' -> next scanner + | _ -> scanContents tag) + | ch when ch == hackyEOFChar -> + (* TODO: why is this place checking EOF and not others? *) + () + | _ -> next scanner; - scanContents tag () - ) + scanContents tag in - if CharacterCodes.Lower.a <= scanner.ch && scanner.ch <= CharacterCodes.Lower.z then ( + match scanner.ch with + | 'a'..'z' -> let startOff = scanner.offset in - while CharacterCodes.Lower.a <= scanner.ch && scanner.ch <= CharacterCodes.Lower.z do - next scanner - done; - let tag = Bytes.sub_string scanner.src startOff (scanner.offset - startOff) in - if scanner.ch = CharacterCodes.bar then - scanContents tag () - else - () - ) else if scanner.ch = CharacterCodes.bar then - scanContents "" () - else - () + skipLowerCaseChars scanner; + let tag = (String.sub [@doesNotRaise]) scanner.src startOff (scanner.offset - startOff) in + if scanner.ch = '|' then scanContents tag + | '|' -> + scanContents "" + | _ -> () end module Res_parser : sig @@ -419396,8 +419136,7 @@ type t = { mutable regions: regionStatus ref list; } -(* `line` seeds the parser's state with an initial line number. *) -val make: ?mode:mode -> ?line:int -> string -> string -> t +val make: ?mode:mode -> string -> string -> t val expect: ?grammar:Grammar.t -> Token.t -> t -> unit val optional: t -> Token.t -> bool @@ -419450,22 +419189,25 @@ type t = { } let err ?startPos ?endPos p error = - let d = Diagnostics.make - ~startPos:(match startPos with | Some pos -> pos | None -> p.startPos) - ~endPos:(match endPos with | Some pos -> pos | None -> p.endPos) - error - in - try - if (!(List.hd p.regions) = Report) then ( + match p.regions with + | {contents = Report} as region::_ -> + let d = + Diagnostics.make + ~startPos:(match startPos with | Some pos -> pos | None -> p.startPos) + ~endPos:(match endPos with | Some pos -> pos | None -> p.endPos) + error + in ( p.diagnostics <- d::p.diagnostics; - List.hd p.regions := Silent + region := Silent ) - with Failure _ -> () + | _ -> () let beginRegion p = p.regions <- ref Report :: p.regions let endRegion p = - try p.regions <- List.tl p.regions with Failure _ -> () + match p.regions with + | [] -> () + | _::rest -> p.regions <- rest (* Advance to the next non-comment token and store any encountered comment * in the parser's state. Every comment contains the end position of its @@ -419499,8 +419241,8 @@ let checkProgress ~prevEndPos ~result p = then None else Some result -let make ?(mode=ParseForTypeChecker) ?line src filename = - let scanner = Scanner.make ~filename ?line (Bytes.of_string src) in +let make ?(mode=ParseForTypeChecker) src filename = + let scanner = Scanner.make ~filename src in let parserState = { mode; scanner; @@ -419553,7 +419295,6 @@ let lookahead p callback = let err = p.scanner.err in let ch = p.scanner.ch in let offset = p.scanner.offset in - let rdOffset = p.scanner.rdOffset in let lineOffset = p.scanner.lineOffset in let lnum = p.scanner.lnum in let mode = p.scanner.mode in @@ -419571,7 +419312,6 @@ let lookahead p callback = p.scanner.err <- err; p.scanner.ch <- ch; p.scanner.offset <- offset; - p.scanner.rdOffset <- rdOffset; p.scanner.lineOffset <- lineOffset; p.scanner.lnum <- lnum; p.scanner.mode <- mode; @@ -420016,7 +419756,7 @@ type kind = Parenthesized | Braced of Location.t | Nothing module type Functor = (SetLike => Set) with type t = A.t *) let modTypeWithOperand modType = match modType with - | {Parsetree.pmty_desc = Pmty_functor _} -> true + | {Parsetree.pmty_desc = Pmty_functor _ | Pmty_with _} -> true | _ -> false let modExprFunctorConstraint modType = match modType with @@ -420048,6 +419788,8 @@ let patternRecordRowRhs (pattern : Parsetree.pattern) = end module Res_printer : sig #1 "res_printer.mli" +val convertBsExternalAttribute : string -> string +val convertBsExtension : string -> string val printTypeParams : (Parsetree.core_type * Asttypes.variance) list -> Res_comments_table.t -> Res_doc.t @@ -420087,6 +419829,49 @@ type callbackStyle = *) | ArgumentsFitOnOneLine +(* Since compiler version 8.3, the bs. prefix is no longer needed *) +(* Synced from + https://github.com/rescript-lang/rescript-compiler/blob/29174de1a5fde3b16cf05d10f5ac109cfac5c4ca/jscomp/frontend/ast_external_process.ml#L291-L367 *) +let convertBsExternalAttribute = function + | "bs.as" -> "as" + | "bs.deriving" -> "deriving" + | "bs.get" -> "get" + | "bs.get_index" -> "get_index" + | "bs.ignore" -> "ignore" + | "bs.inline" -> "inline" + | "bs.int" -> "int" + | "bs.meth" -> "meth" + | "bs.module" -> "module" + | "bs.new" -> "new" + | "bs.obj" -> "obj" + | "bs.optional" -> "optional" + | "bs.return" -> "return" + | "bs.send" -> "send" + | "bs.scope" -> "scope" + | "bs.set" -> "set" + | "bs.set_index" -> "set_index" + | "bs.splice" | "bs.variadic" -> "variadic" + | "bs.string" -> "string" + | "bs.this" -> "this" + | "bs.uncurry" -> "uncurry" + | "bs.unwrap" -> "unwrap" + | "bs.val" -> "val" + (* bs.send.pipe shouldn't be transformed *) + | txt -> txt + +(* These haven't been needed for a long time now *) +(* Synced from + https://github.com/rescript-lang/rescript-compiler/blob/29174de1a5fde3b16cf05d10f5ac109cfac5c4ca/jscomp/frontend/ast_exp_extension.ml *) +let convertBsExtension = function + | "bs.debugger" -> "debugger" + | "bs.external" -> "raw" + (* We should never see this one since we use the sugared object form, but still *) + | "bs.obj" -> "obj" + | "bs.raw" -> "raw" + | "bs.re" -> "re" + (* TODO: what about bs.time and bs.node? *) + | txt -> txt + let addParens doc = Doc.group ( Doc.concat [ @@ -420426,30 +420211,23 @@ type identifierStyle = | NormalIdent let classifyIdentContent ?(allowUident=false) txt = - let len = String.length txt in - let rec go i = - if i == len then NormalIdent - else - let c = String.unsafe_get txt i in - if i == 0 && not ( - (allowUident && (c >= 'A' && c <= 'Z')) || - (c >= 'a' && c <= 'z') || c = '_' ) then - ExoticIdent - else if not ( - (c >= 'a' && c <= 'z') - || (c >= 'A' && c <= 'Z') - || c = '\'' - || c = '_' - || (c >= '0' && c <= '9')) - then - ExoticIdent - else - go (i + 1) - in if Token.isKeywordTxt txt then ExoticIdent else - go 0 + let len = String.length txt in + let rec loop i = + if i == len then NormalIdent + else if i == 0 then + match String.unsafe_get txt i with + | 'A'..'Z' when allowUident -> loop (i + 1) + | 'a'..'z' | '_' -> loop (i + 1) + | _ -> ExoticIdent + else + match String.unsafe_get txt i with + | 'A'..'Z' | 'a'..'z' | '0'..'9' | '\'' | '_' -> loop (i + 1) + | _ -> ExoticIdent + in + loop 0 let printIdentLike ?allowUident txt = match classifyIdentContent ?allowUident txt with @@ -421501,16 +421279,21 @@ and printTypExpr (typExpr : Parsetree.core_type) cmtTbl = doc in Doc.concat [typ; Doc.text " as "; Doc.concat [Doc.text "'"; printIdentLike alias]] - | Ptyp_constr({txt = Longident.Ldot(Longident.Lident "Js", "t")}, [{ptyp_desc = Ptyp_object (_fields, _openFlag)} as typ]) -> - let bsObject = printTypExpr typ cmtTbl in - begin match typExpr.ptyp_attributes with - | [] -> bsObject - | attrs -> - Doc.concat [ - printAttributes ~inline:true attrs cmtTbl; - printTypExpr typ cmtTbl; - ] - end + + (* object printings *) + | Ptyp_object (fields, openFlag) -> + printObject ~inline:false fields openFlag cmtTbl + | Ptyp_constr(longidentLoc, [{ptyp_desc = Ptyp_object (fields, openFlag)}]) -> + (* for foo<{"a": b}>, when the object is long and needs a line break, we + want the <{ and }> to stay hugged together *) + let constrName = printLidentPath longidentLoc cmtTbl in + Doc.concat([ + constrName; + Doc.lessThan; + printObject ~inline:true fields openFlag cmtTbl; + Doc.greaterThan; + ]) + | Ptyp_constr(longidentLoc, [{ ptyp_desc = Parsetree.Ptyp_tuple tuple }]) -> let constrName = printLidentPath longidentLoc cmtTbl in Doc.group( @@ -421525,17 +421308,6 @@ and printTypExpr (typExpr : Parsetree.core_type) cmtTbl = let constrName = printLidentPath longidentLoc cmtTbl in begin match constrArgs with | [] -> constrName - | [{ - Parsetree.ptyp_desc = - Ptyp_constr({txt = Longident.Ldot(Longident.Lident "Js", "t")}, - [{ptyp_desc = Ptyp_object (fields, openFlag)}]) - }] -> - Doc.concat([ - constrName; - Doc.lessThan; - printBsObjectSugar ~inline:true fields openFlag cmtTbl; - Doc.greaterThan; - ]) | _args -> Doc.group( Doc.concat([ constrName; @@ -421639,8 +421411,6 @@ and printTypExpr (typExpr : Parsetree.core_type) cmtTbl = ) end | Ptyp_tuple types -> printTupleType ~inline:false types cmtTbl - | Ptyp_object (fields, openFlag) -> - printBsObjectSugar ~inline:false fields openFlag cmtTbl | Ptyp_poly([], typ) -> printTypExpr typ cmtTbl | Ptyp_poly(stringLocs, typ) -> @@ -421727,8 +421497,7 @@ and printTypExpr (typExpr : Parsetree.core_type) cmtTbl = ) in let shouldPrintItsOwnAttributes = match typExpr.ptyp_desc with - | Ptyp_arrow _ (* es6 arrow types print their own attributes *) - | Ptyp_constr({txt = Longident.Ldot(Longident.Lident "Js", "t")}, _) -> true + | Ptyp_arrow _ (* es6 arrow types print their own attributes *) -> true | _ -> false in let doc = begin match typExpr.ptyp_attributes with @@ -421744,7 +421513,7 @@ and printTypExpr (typExpr : Parsetree.core_type) cmtTbl = in printComments doc cmtTbl typExpr.ptyp_loc -and printBsObjectSugar ~inline fields openFlag cmtTbl = +and printObject ~inline fields openFlag cmtTbl = let doc = match fields with | [] -> Doc.concat [ Doc.lbrace; @@ -422072,11 +421841,7 @@ and printPackageConstraint i cmtTbl (longidentLoc, typ) = ] and printExtension ~atModuleLvl (stringLoc, payload) cmtTbl = - let txt = match stringLoc.Location.txt with - | "bs.raw" -> "raw" - | "bs.obj" -> "obj" - | txt -> txt - in + let txt = convertBsExtension stringLoc.Location.txt in let extName = let doc = Doc.concat [ Doc.text "%"; @@ -424895,34 +424660,10 @@ and printPayload (payload : Parsetree.payload) cmtTbl = ] and printAttribute ((id, payload) : Parsetree.attribute) cmtTbl = - let contents = match id.txt with - | "bs.val" -> "val" - | "bs.module" -> "module" - | "bs.scope" -> "scope" - | "bs.splice" | "bs.variadic" -> "variadic" - | "bs.set" -> "set" - | "bs.set_index" -> "set_index" - | "bs.get" -> "get" - | "bs.get_index" -> "get_index" - | "bs.new" -> "new" - | "bs.obj" -> "obj" - | "bs.return" -> "return" - | "bs.uncurry" -> "uncurry" - | "bs.this" -> "this" - | "bs.meth" -> "meth" - | "bs.deriving" -> "deriving" - | "bs.string" -> "string" - | "bs.int" -> "int" - | "bs.ignore" -> "ignore" - | "bs.unwrap" -> "unwrap" - | "bs.as" -> "as" - | "bs.optional" -> "optional" - | txt -> txt - in Doc.group ( Doc.concat [ Doc.text "@"; - Doc.text contents; + Doc.text (convertBsExternalAttribute id.txt); printPayload payload cmtTbl ] ) @@ -425634,17 +425375,6 @@ let makeListPattern loc seq ext_opt = in handle_seq seq - -(* {"foo": bar} -> Js.t({. foo: bar}) - * {.. "foo": bar} -> Js.t({.. foo: bar}) - * {..} -> Js.t({..}) *) -let makeBsObjType ~attrs ~loc ~closed rows = - let obj = Ast_helper.Typ.object_ ~loc rows closed in - let jsDotTCtor = - Location.mkloc (Longident.Ldot (Longident.Lident "Js", "t")) loc - in - Ast_helper.Typ.constr ~loc ~attrs jsDotTCtor [obj] - (* TODO: diagnostic reporting *) let lidentOfPath longident = match Longident.flatten longident |> List.rev with @@ -426120,7 +425850,8 @@ let parseTemplateStringLiteral s = | '`' as c -> Buffer.add_char b c; loop (i + 2) - | c when Res_character_codes.isLineBreak (Char.code c) -> + | '\n' | '\r' -> + (* line break *) loop (i + 2) | c -> Buffer.add_char b '\\'; @@ -427125,10 +426856,9 @@ and parseAtomicExpr p = Recover.defaultExpr () | token -> let errPos = p.prevEndPos in + Parser.err ~startPos:errPos p (Diagnostics.unexpected token p.breadcrumbs); begin match skipTokensAndMaybeRetry p ~isStartOfGrammar:Grammar.isAtomicExprStart with - | None -> - Parser.err ~startPos:errPos p (Diagnostics.unexpected token p.breadcrumbs); - Recover.defaultExpr () + | None -> Recover.defaultExpr() | Some () -> parseAtomicExpr p end in @@ -427640,7 +427370,6 @@ and parseAttributesAndBinding (p : Parser.t) = let err = p.scanner.err in let ch = p.scanner.ch in let offset = p.scanner.offset in - let rdOffset = p.scanner.rdOffset in let lineOffset = p.scanner.lineOffset in let lnum = p.scanner.lnum in let mode = p.scanner.mode in @@ -427663,7 +427392,6 @@ and parseAttributesAndBinding (p : Parser.t) = p.scanner.err <- err; p.scanner.ch <- ch; p.scanner.offset <- offset; - p.scanner.rdOffset <- rdOffset; p.scanner.lineOffset <- lineOffset; p.scanner.lnum <- lnum; p.scanner.mode <- mode; @@ -428220,7 +427948,10 @@ and parseExprBlockItem p = Parser.next p; begin match p.token with | Lparen -> - parseFirstClassModuleExpr ~startPos p + let expr = parseFirstClassModuleExpr ~startPos p in + let a = parsePrimaryExpr ~operand:expr p in + let expr = parseBinaryExpr ~a p 1 in + parseTernaryExpr expr p | _ -> let name = match p.Parser.token with | Uident ident -> @@ -428997,7 +428728,7 @@ and parseAtomicTypExpr ~attrs p = let loc = mkLoc startPos p.prevEndPos in Ast_helper.Typ.extension ~attrs ~loc extension | Lbrace -> - parseBsObjectType ~attrs p + parseRecordOrBsObjectType ~attrs p | token -> Parser.err p (Diagnostics.unexpected token p.breadcrumbs); begin match skipTokensAndMaybeRetry p ~isStartOfGrammar:Grammar.isAtomicTypExprStart with @@ -429056,7 +428787,8 @@ and parsePackageConstraint p = Some (typeConstr, typ) | _ -> None -and parseBsObjectType ~attrs p = +and parseRecordOrBsObjectType ~attrs p = + (* for inline record in constructor *) let startPos = p.Parser.startPos in Parser.expect Lbrace p; let closedFlag = match p.token with @@ -429073,7 +428805,7 @@ and parseBsObjectType ~attrs p = in Parser.expect Rbrace p; let loc = mkLoc startPos p.prevEndPos in - makeBsObjType ~attrs ~loc ~closed:closedFlag fields + Ast_helper.Typ.object_ ~loc ~attrs fields closedFlag (* TODO: check associativity in combination with attributes *) and parseTypeAlias p typ = @@ -429475,7 +429207,7 @@ and parseConstrDeclArgs p = in Parser.expect Rbrace p; let loc = mkLoc startPos p.prevEndPos in - let typ = makeBsObjType ~attrs:[] ~loc ~closed:closedFlag fields in + let typ = Ast_helper.Typ.object_ ~loc ~attrs:[] fields closedFlag in Parser.optional p Comma |> ignore; let moreArgs = parseCommaDelimitedRegion @@ -429526,7 +429258,7 @@ and parseConstrDeclArgs p = ) in Parser.expect Rbrace p; let loc = mkLoc startPos p.prevEndPos in - let typ = makeBsObjType ~attrs:[] ~loc ~closed:closedFlag fields in + let typ = Ast_helper.Typ.object_ ~loc ~attrs:[] fields closedFlag in Parser.optional p Comma |> ignore; let moreArgs = parseCommaDelimitedRegion @@ -429858,7 +429590,7 @@ and parseRecordOrBsObjectDecl p = Parser.expect Rbrace p; let loc = mkLoc startPos p.prevEndPos in let typ = - makeBsObjType ~attrs:[] ~loc ~closed:closedFlag fields + Ast_helper.Typ.object_ ~loc ~attrs:[] fields closedFlag |> parseTypeAlias p in let typ = parseArrowTypeRest ~es6Arrow:true ~startPos typ p in @@ -429905,7 +429637,7 @@ and parseRecordOrBsObjectDecl p = Parser.expect Rbrace p; let loc = mkLoc startPos p.prevEndPos in let typ = - makeBsObjType ~attrs:[] ~loc ~closed:closedFlag fields |> parseTypeAlias p + Ast_helper.Typ.object_ ~loc ~attrs:[] fields closedFlag |> parseTypeAlias p in let typ = parseArrowTypeRest ~es6Arrow:true ~startPos typ p in (Some typ, Asttypes.Public, Parsetree.Ptype_abstract) @@ -430817,6 +430549,9 @@ and parseModuleOrModuleTypeImplOrPackExpr ~attrs p = | Typ -> parseModuleTypeImpl ~attrs startPos p | Lparen -> let expr = parseFirstClassModuleExpr ~startPos p in + let a = parsePrimaryExpr ~operand:expr p in + let expr = parseBinaryExpr ~a p 1 in + let expr = parseTernaryExpr expr p in Ast_helper.Str.eval ~attrs expr | _ -> parseMaybeRecModuleBinding ~attrs ~startPos p @@ -431376,6 +431111,7 @@ and parseAttributeId ~startPos p = and parsePayload p = match p.Parser.token with | Lparen when p.startPos.pos_cnum = p.prevEndPos.pos_cnum -> + Parser.leaveBreadcrumb p Grammar.AttributePayload; Parser.next p; begin match p.token with | Colon -> @@ -431392,6 +431128,7 @@ and parsePayload p = Parsetree.PTyp (parseTypExpr p) in Parser.expect Rparen p; + Parser.eatBreadcrumb p; payload | Question -> Parser.next p; @@ -431404,6 +431141,7 @@ and parsePayload p = None in Parser.expect Rparen p; + Parser.eatBreadcrumb p; Parsetree.PPat (pattern, expr) | _ -> let items = parseDelimitedRegion @@ -431413,6 +431151,7 @@ and parsePayload p = p in Parser.expect Rparen p; + Parser.eatBreadcrumb p; Parsetree.PStr items end | _ -> Parsetree.PStr [] @@ -431504,24 +431243,18 @@ val writeFile: filename: string -> contents: string -> unit end = struct #1 "res_io.ml" -(* random chunk size: 2^15, TODO: why do we guess randomly? *) -let chunkSize = 32768 - let readFile ~filename = let chan = open_in filename in - let buffer = Buffer.create chunkSize in - let chunk = (Bytes.create [@doesNotRaise]) chunkSize in - let rec loop () = - let len = try input chan chunk 0 chunkSize with Invalid_argument _ -> 0 in - if len == 0 then ( - close_in_noerr chan; - Buffer.contents buffer - ) else ( - Buffer.add_subbytes buffer chunk 0 len; - loop () - ) + let content = + try really_input_string chan (in_channel_length chan) + with End_of_file -> "" in - loop () + close_in_noerr chan; + content + + +(* random chunk size: 2^15, TODO: why do we guess randomly? *) +let chunkSize = 32768 let readStdin () = let buffer = Buffer.create chunkSize in @@ -431651,7 +431384,7 @@ let parsingEngine = { | _ as diagnostics -> (true, diagnostics) in { filename = engine.scanner.filename; - source = Bytes.to_string engine.scanner.src; + source = engine.scanner.src; parsetree = structure; diagnostics; invalid; @@ -431666,7 +431399,7 @@ let parsingEngine = { | _ as diagnostics -> (true, diagnostics) in { filename = engine.scanner.filename; - source = Bytes.to_string engine.scanner.src; + source = engine.scanner.src; parsetree = signature; diagnostics; invalid; @@ -432065,43 +431798,18 @@ let normalize = extension = (fun mapper ext -> match ext with | (id, payload) -> - let contents = match id.txt with - | "bs.raw" -> "raw" - | "bs.obj" -> "obj" - | txt -> txt - in - ({id with txt = contents}, default_mapper.payload mapper payload) - + ( + {id with txt = Res_printer.convertBsExtension id.txt}, + default_mapper.payload mapper payload + ) ); attribute = (fun mapper attr -> match attr with | (id, payload) -> - (* Reminder, keep this in sync with src/res_printer.ml *) - let contents = match id.txt with - | "bs.val" -> "val" - | "bs.module" -> "module" - | "bs.scope" -> "scope" - | "bs.splice" | "bs.variadic" -> "variadic" - | "bs.set" -> "set" - | "bs.set_index" -> "set_index" - | "bs.get" -> "get" - | "bs.get_index" -> "get_index" - | "bs.new" -> "new" - | "bs.obj" -> "obj" - | "bs.return" -> "return" - | "bs.uncurry" -> "uncurry" - | "bs.this" -> "this" - | "bs.meth" -> "meth" - | "bs.deriving" -> "deriving" - | "bs.string" -> "string" - | "bs.int" -> "int" - | "bs.ignore" -> "ignore" - | "bs.unwrap" -> "unwrap" - | "bs.as" -> "as" - | "bs.optional" -> "optional" - | txt -> txt - in - ({id with txt = contents}, default_mapper.payload mapper payload) + ( + {id with txt = Res_printer.convertBsExternalAttribute id.txt}, + default_mapper.payload mapper payload + ) ); attributes = (fun mapper attrs -> attrs @@ -432124,6 +431832,14 @@ let normalize = | _ -> default_mapper.pat mapper p end; + typ = (fun mapper typ -> + match typ.ptyp_desc with + | Ptyp_constr({txt = Longident.Ldot(Longident.Lident "Js", "t")}, [arg]) -> + (* Js.t({"a": b}) -> {"a": b} + Since compiler >9.0.1 objects don't need Js.t wrapping anymore *) + mapper.typ mapper arg + | _ -> default_mapper.typ mapper typ + ); expr = (fun mapper expr -> match expr.pexp_desc with | Pexp_constant (Pconst_string (txt, None)) -> @@ -432311,7 +432027,6 @@ let replaceStringLiteralSignature stringData signature = let mapper = stringLiteralMapper stringData in mapper.Ast_mapper.signature mapper signature - end module Res_driver_ml_parser : sig #1 "res_driver_ml_parser.mli" @@ -432450,7 +432165,7 @@ let extractConcreteSyntax filename = if String.length filename > 0 then IO.readFile ~filename else IO.readStdin () in - let scanner = Res_scanner.make (Bytes.of_string src) ~filename in + let scanner = Res_scanner.make src ~filename in let rec next prevEndPos scanner = let (startPos, endPos, token) = Res_scanner.scan scanner in @@ -433008,6 +432723,8 @@ let printPolyVarIdent txt = Doc.join ~sep:Doc.space ( List.map (fun var -> Doc.text ("'" ^ var)) vars ); + Doc.dot; + Doc.space; printOutTypeDoc outType; ] ) diff --git a/lib/4.06.1/whole_compiler.ml.d b/lib/4.06.1/whole_compiler.ml.d index edbcbb9730..f1dade88d2 100644 --- a/lib/4.06.1/whole_compiler.ml.d +++ b/lib/4.06.1/whole_compiler.ml.d @@ -590,7 +590,6 @@ ../lib/4.06.1/whole_compiler.ml: ./napkin/reactjs_jsx_ppx_v3.mli ../lib/4.06.1/whole_compiler.ml: ./napkin/res_ast_conversion.ml ../lib/4.06.1/whole_compiler.ml: ./napkin/res_ast_conversion.mli -../lib/4.06.1/whole_compiler.ml: ./napkin/res_character_codes.ml ../lib/4.06.1/whole_compiler.ml: ./napkin/res_comment.ml ../lib/4.06.1/whole_compiler.ml: ./napkin/res_comment.mli ../lib/4.06.1/whole_compiler.ml: ./napkin/res_comments_table.ml diff --git a/scripts/tasks.js b/scripts/tasks.js index c7dec91e07..84e9e36d64 100644 --- a/scripts/tasks.js +++ b/scripts/tasks.js @@ -87,7 +87,9 @@ function onSourceChange(eventType, filename) { filename.endsWith(".mli") || filename.endsWith(".json") || filename.endsWith(".re") || - filename.endsWith(".rei") + filename.endsWith(".rei") || + filename.endsWith(".res") || + filename.endsWith(".resi") ) { rebuild(); } diff --git a/syntax b/syntax index 557c0631e9..556bd96a95 160000 --- a/syntax +++ b/syntax @@ -1 +1 @@ -Subproject commit 557c0631e9652dab2411511a6afd4462ca64ce51 +Subproject commit 556bd96a951a4e7a114f300db2ef567fbe755687