diff --git a/CHANGELOG.md b/CHANGELOG.md index 940037e207..3509a78bb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ #### :rocket: New Feature - Reanalyze: add parallel processing for CMT file analysis with new `-parallel` and `-timing` flags, plus benchmark infrastructure for performance testing. https://github.com/rescript-lang/rescript/pull/8089 +- Allow `$` in identifiers. https://github.com/rescript-lang/rescript/pull/8095 #### :bug: Bug fix diff --git a/compiler/frontend/ast_utf8_string_interp.ml b/compiler/frontend/ast_utf8_string_interp.ml index 90c5043256..cd8f499cdd 100644 --- a/compiler/frontend/ast_utf8_string_interp.ml +++ b/compiler/frontend/ast_utf8_string_interp.ml @@ -64,13 +64,13 @@ type exn += Error of pos * pos * error let valid_lead_identifier_char x = match x with - | 'a' .. 'z' | '_' -> true + | 'a' .. 'z' | '_' | '$' -> true | _ -> false (** Invariant: [valid_lead_identifier] has to be [valid_identifier] *) let valid_identifier_char x = match x with - | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '_' | '\'' -> true + | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '_' | '\'' | '$' -> true | _ -> false let valid_identifier s = diff --git a/compiler/syntax/src/res_printer.ml b/compiler/syntax/src/res_printer.ml index 7132e16dd9..866473c054 100644 --- a/compiler/syntax/src/res_printer.ml +++ b/compiler/syntax/src/res_printer.ml @@ -406,11 +406,11 @@ let classify_ident_content ?(allow_uident = false) ?(allow_hyphen = false) txt = match String.unsafe_get txt i with | '\\' -> UppercaseExoticIdent | 'A' .. 'Z' when allow_uident -> loop (i + 1) - | 'a' .. 'z' | '_' -> 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) + | 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' | '\'' | '_' | '$' -> loop (i + 1) | '-' when allow_hyphen -> loop (i + 1) | _ -> ExoticIdent in diff --git a/compiler/syntax/src/res_scanner.ml b/compiler/syntax/src/res_scanner.ml index 5615b32add..5ec1aad888 100644 --- a/compiler/syntax/src/res_scanner.ml +++ b/compiler/syntax/src/res_scanner.ml @@ -190,7 +190,7 @@ let scan_identifier scanner = let start_off = scanner.offset in let rec skip_good_chars scanner = match scanner.ch with - | 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' | '_' | '\'' -> + | 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' | '_' | '\'' | '$' -> next scanner; skip_good_chars scanner | _ -> () @@ -751,7 +751,7 @@ let rec scan scanner = let token = match scanner.ch with (* peeking 0 char *) - | 'A' .. 'Z' | 'a' .. 'z' -> scan_identifier scanner + | 'A' .. 'Z' | 'a' .. 'z' | '$' -> scan_identifier scanner | '0' .. '9' -> scan_number scanner | '`' -> next scanner; diff --git a/packages/@rescript/runtime/Primitive_curry.res b/packages/@rescript/runtime/Primitive_curry.res index 2f1dfc7e51..34f072ac92 100644 --- a/packages/@rescript/runtime/Primitive_curry.res +++ b/packages/@rescript/runtime/Primitive_curry.res @@ -87,22 +87,19 @@ let curry_1 = (o, a0, arity) => switch arity { | 1 => apply1(Obj.magic(o), a0) | 2 => param => apply2(Obj.magic(o), a0, param) - | 3 => Obj.magic((param, \"param$1") => apply3(Obj.magic(o), a0, param, \"param$1")) - | 4 => - Obj.magic((param, \"param$1", \"param$2") => - apply4(Obj.magic(o), a0, param, \"param$1", \"param$2") - ) + | 3 => Obj.magic((param, param$1) => apply3(Obj.magic(o), a0, param, param$1)) + | 4 => Obj.magic((param, param$1, param$2) => apply4(Obj.magic(o), a0, param, param$1, param$2)) | 5 => - Obj.magic((param, \"param$1", \"param$2", \"param$3") => - apply5(Obj.magic(o), a0, param, \"param$1", \"param$2", \"param$3") + Obj.magic((param, param$1, param$2, param$3) => + apply5(Obj.magic(o), a0, param, param$1, param$2, param$3) ) | 6 => - Obj.magic((param, \"param$1", \"param$2", \"param$3", \"param$4") => - apply6(Obj.magic(o), a0, param, \"param$1", \"param$2", \"param$3", \"param$4") + Obj.magic((param, param$1, param$2, param$3, param$4) => + apply6(Obj.magic(o), a0, param, param$1, param$2, param$3, param$4) ) | 7 => - Obj.magic((param, \"param$1", \"param$2", \"param$3", \"param$4", \"param$5") => - apply7(Obj.magic(o), a0, param, \"param$1", \"param$2", \"param$3", \"param$4", \"param$5") + Obj.magic((param, param$1, param$2, param$3, param$4, param$5) => + apply7(Obj.magic(o), a0, param, param$1, param$2, param$3, param$4, param$5) ) | _ => Obj.magic(app(o, [a0])) } @@ -130,18 +127,16 @@ let curry_2 = (o, a0, a1, arity) => | 1 => app(apply1(Obj.magic(o), a0), [a1]) | 2 => apply2(Obj.magic(o), a0, a1) | 3 => param => apply3(Obj.magic(o), a0, a1, param) - | 4 => Obj.magic((param, \"param$1") => apply4(Obj.magic(o), a0, a1, param, \"param$1")) + | 4 => Obj.magic((param, param$1) => apply4(Obj.magic(o), a0, a1, param, param$1)) | 5 => - Obj.magic((param, \"param$1", \"param$2") => - apply5(Obj.magic(o), a0, a1, param, \"param$1", \"param$2") - ) + Obj.magic((param, param$1, param$2) => apply5(Obj.magic(o), a0, a1, param, param$1, param$2)) | 6 => - Obj.magic((param, \"param$1", \"param$2", \"param$3") => - apply6(Obj.magic(o), a0, a1, param, \"param$1", \"param$2", \"param$3") + Obj.magic((param, param$1, param$2, param$3) => + apply6(Obj.magic(o), a0, a1, param, param$1, param$2, param$3) ) | 7 => - Obj.magic((param, \"param$1", \"param$2", \"param$3", \"param$4") => - apply7(Obj.magic(o), a0, a1, param, \"param$1", \"param$2", \"param$3", \"param$4") + Obj.magic((param, param$1, param$2, param$3, param$4) => + apply7(Obj.magic(o), a0, a1, param, param$1, param$2, param$3, param$4) ) | _ => Obj.magic(app(o, [a0, a1])) } @@ -170,14 +165,14 @@ let curry_3 = (o, a0, a1, a2, arity) => | 2 => app(apply2(Obj.magic(o), a0, a1), [a2]) | 3 => apply3(Obj.magic(o), a0, a1, a2) | 4 => param => apply4(Obj.magic(o), a0, a1, a2, param) - | 5 => Obj.magic((param, \"param$1") => apply5(Obj.magic(o), a0, a1, a2, param, \"param$1")) + | 5 => Obj.magic((param, param$1) => apply5(Obj.magic(o), a0, a1, a2, param, param$1)) | 6 => - Obj.magic((param, \"param$1", \"param$2") => - apply6(Obj.magic(o), a0, a1, a2, param, \"param$1", \"param$2") + Obj.magic((param, param$1, param$2) => + apply6(Obj.magic(o), a0, a1, a2, param, param$1, param$2) ) | 7 => - Obj.magic((param, \"param$1", \"param$2", \"param$3") => - apply7(Obj.magic(o), a0, a1, a2, param, \"param$1", \"param$2", \"param$3") + Obj.magic((param, param$1, param$2, param$3) => + apply7(Obj.magic(o), a0, a1, a2, param, param$1, param$2, param$3) ) | _ => Obj.magic(app(o, [a0, a1, a2])) } @@ -207,10 +202,10 @@ let curry_4 = (o, a0, a1, a2, a3, arity) => | 3 => app(apply3(Obj.magic(o), a0, a1, a2), [a3]) | 4 => apply4(Obj.magic(o), a0, a1, a2, a3) | 5 => param => apply5(Obj.magic(o), a0, a1, a2, a3, param) - | 6 => Obj.magic((param, \"param$1") => apply6(Obj.magic(o), a0, a1, a2, a3, param, \"param$1")) + | 6 => Obj.magic((param, param$1) => apply6(Obj.magic(o), a0, a1, a2, a3, param, param$1)) | 7 => - Obj.magic((param, \"param$1", \"param$2") => - apply7(Obj.magic(o), a0, a1, a2, a3, param, \"param$1", \"param$2") + Obj.magic((param, param$1, param$2) => + apply7(Obj.magic(o), a0, a1, a2, a3, param, param$1, param$2) ) | _ => Obj.magic(app(o, [a0, a1, a2, a3])) } @@ -241,8 +236,7 @@ let curry_5 = (o, a0, a1, a2, a3, a4, arity) => | 4 => app(apply4(Obj.magic(o), a0, a1, a2, a3), [a4]) | 5 => apply5(Obj.magic(o), a0, a1, a2, a3, a4) | 6 => param => apply6(Obj.magic(o), a0, a1, a2, a3, a4, param) - | 7 => - Obj.magic((param, \"param$1") => apply7(Obj.magic(o), a0, a1, a2, a3, a4, param, \"param$1")) + | 7 => Obj.magic((param, param$1) => apply7(Obj.magic(o), a0, a1, a2, a3, a4, param, param$1)) | _ => Obj.magic(app(o, [a0, a1, a2, a3, a4])) } diff --git a/tests/syntax_tests/data/parsing/errors/pattern/expected/templateLiteral.res.txt b/tests/syntax_tests/data/parsing/errors/pattern/expected/templateLiteral.res.txt index 380027d9ba..d4470d7c88 100644 --- a/tests/syntax_tests/data/parsing/errors/pattern/expected/templateLiteral.res.txt +++ b/tests/syntax_tests/data/parsing/errors/pattern/expected/templateLiteral.res.txt @@ -11,18 +11,6 @@ String interpolation is not supported in pattern matching. - Syntax error! - syntax_tests/data/parsing/errors/pattern/templateLiteral.res:5:33 - - 3 │ switch l { - 4 │ | `zero coord ${zeroCoord}` => () - 5 │ | `first coord ${zeroCoord} snd ${zeroCoord} ` => () - 6 │ | _ => () - 7 │ } - - Not sure what to do with this character: "$". - - Syntax error! syntax_tests/data/parsing/errors/pattern/templateLiteral.res:5:3-46 diff --git a/tests/syntax_tests/data/parsing/errors/scanner/expected/badCharacter.res.txt b/tests/syntax_tests/data/parsing/errors/scanner/expected/badCharacter.res.txt index 02d25879dc..a2dfcba0da 100644 --- a/tests/syntax_tests/data/parsing/errors/scanner/expected/badCharacter.res.txt +++ b/tests/syntax_tests/data/parsing/errors/scanner/expected/badCharacter.res.txt @@ -1,17 +1 @@ - - Syntax error! - syntax_tests/data/parsing/errors/scanner/badCharacter.res:1:5 - - 1 │ let $ = 1 - - Not sure what to do with this character: "$". - - - Syntax error! - syntax_tests/data/parsing/errors/scanner/badCharacter.res:1:5-7 - - 1 │ let $ = 1 - - I was expecting a name for this let-binding. Example: `let message = "hello"` - -let 1 = [%rescript.exprhole ] \ No newline at end of file +let ($) = 1 \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/other/dollarIdent.res b/tests/syntax_tests/data/parsing/other/dollarIdent.res new file mode 100644 index 0000000000..6f5d6f1dcf --- /dev/null +++ b/tests/syntax_tests/data/parsing/other/dollarIdent.res @@ -0,0 +1,6 @@ +let count$ = signal(0) +let $count = count$ + 1 +let value$$ = {count$, $count} +let update = {...value$$, count$: $count} +let fn = (~count$, ~on$count) => count$ + on$count +let fromRecord = value$$.count$ + value$$.$count diff --git a/tests/syntax_tests/data/parsing/other/expected/dollarIdent.res.txt b/tests/syntax_tests/data/parsing/other/expected/dollarIdent.res.txt new file mode 100644 index 0000000000..9954769743 --- /dev/null +++ b/tests/syntax_tests/data/parsing/other/expected/dollarIdent.res.txt @@ -0,0 +1,6 @@ +let count$ = signal 0 +let ($count) = count$ + 1 +let value$$ = { count$; ($count) } +let update = { value$$ with count$ = ($count) } +let fn [arity:2]~count$ ~on$count = count$ + on$count +let fromRecord = value$$.count$ + value$$.($count) \ No newline at end of file diff --git a/tests/syntax_tests/data/ppx/react/expected/v4.res.txt b/tests/syntax_tests/data/ppx/react/expected/v4.res.txt index e5f9b37a7a..994d03fc4a 100644 --- a/tests/syntax_tests/data/ppx/react/expected/v4.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/v4.res.txt @@ -57,11 +57,11 @@ module Rec = { type props = {} let rec make = { - let \"make$Internal" = (_: props): React.element => { + let make$Internal = (_: props): React.element => { make(({}: props)) } let make = { - let \"V4$Rec" = props => \"make$Internal"(props) + let \"V4$Rec" = props => make$Internal(props) \"V4$Rec" } @@ -74,11 +74,11 @@ module Rec1 = { type props = {} let rec make = { - let \"make$Internal" = (_: props): React.element => { + let make$Internal = (_: props): React.element => { React.null } let make = { - let \"V4$Rec1" = props => \"make$Internal"(props) + let \"V4$Rec1" = props => make$Internal(props) \"V4$Rec1" } @@ -91,11 +91,11 @@ module Rec2 = { type props = {} let rec make = { - let \"make$Internal" = (_: props): React.element => { + let make$Internal = (_: props): React.element => { mm(({}: props)) } let make = { - let \"V4$Rec2" = props => \"make$Internal"(props) + let \"V4$Rec2" = props => make$Internal(props) \"V4$Rec2" } diff --git a/tests/syntax_tests/data/printer/identifiers/dollarIdent.res b/tests/syntax_tests/data/printer/identifiers/dollarIdent.res new file mode 100644 index 0000000000..b2035c9d23 --- /dev/null +++ b/tests/syntax_tests/data/printer/identifiers/dollarIdent.res @@ -0,0 +1,4 @@ +let count$ = signal(0) +let $count = count$ + 1 +let record$ = {count$, $count} +let access = record$.count$ + record$.$count diff --git a/tests/syntax_tests/data/printer/identifiers/expected/dollarIdent.res.txt b/tests/syntax_tests/data/printer/identifiers/expected/dollarIdent.res.txt new file mode 100644 index 0000000000..b2035c9d23 --- /dev/null +++ b/tests/syntax_tests/data/printer/identifiers/expected/dollarIdent.res.txt @@ -0,0 +1,4 @@ +let count$ = signal(0) +let $count = count$ + 1 +let record$ = {count$, $count} +let access = record$.count$ + record$.$count diff --git a/tests/tests/src/dollar_escape_test.res b/tests/tests/src/dollar_escape_test.res index 7f046e5926..f6e3c911fb 100644 --- a/tests/tests/src/dollar_escape_test.res +++ b/tests/tests/src/dollar_escape_test.res @@ -1,9 +1,9 @@ open Mocha open Test_utils -let \"$$" = (x, y) => x + y +let $$ = (x, y) => x + y -let v = \"$$"(1, 2) +let v = $$(1, 2) let \"$$+" = (x, y) => x * y diff --git a/tests/tests/src/dollar_ident_js_test.mjs b/tests/tests/src/dollar_ident_js_test.mjs new file mode 100644 index 0000000000..4aee97860d --- /dev/null +++ b/tests/tests/src/dollar_ident_js_test.mjs @@ -0,0 +1,44 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + + +let $count = 3; + +let record$ = { + count$: 1, + $count: $count +}; + +let sum$ = 1 + $count | 0; + +let update$ = { + count$: 5, + $count: $count +}; + +function fn$(count$, inc$) { + return count$ + inc$ | 0; +} + +let $ = 1 + $count | 0; + +function $$(value$) { + console.log(value$); + return value$ + $count | 0; +} + +let call$$ = $$($); + +let count$ = 1; + +export { + count$, + $count, + record$, + sum$, + update$, + fn$, + $, + $$, + call$$, +} +/* call$$ Not a pure module */ diff --git a/tests/tests/src/dollar_ident_js_test.res b/tests/tests/src/dollar_ident_js_test.res new file mode 100644 index 0000000000..3b56557ed5 --- /dev/null +++ b/tests/tests/src/dollar_ident_js_test.res @@ -0,0 +1,18 @@ +let count$ = 1 +let $count = count$ + 2 + +type t$ = {count$: int, $count: int} + +let record$: t$ = {count$, $count} +let sum$ = record$.count$ + record$.$count + +let update$ = {...record$, count$: 5} + +let fn$ = (~count$, ~inc$) => count$ + inc$ + +let $ = count$ + $count +let $$ = value$ => { + Console.log(value$) + value$ + $count +} +let call$$ = $$($)