diff --git a/CHANGELOG.md b/CHANGELOG.md index 59e40a7a2c..8fa8129e2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ # 12.0.0-alpha.13 (Unreleased) +#### :rocket: New Feature + +- Add shift (`<<`, `>>`, `>>>`) operators for `int` and `bigint`. https://github.com/rescript-lang/rescript/pull/7183 + # 12.0.0-alpha.12 #### :bug: Bug fix diff --git a/compiler/ml/unified_ops.ml b/compiler/ml/unified_ops.ml index 997606135e..25f19c1ade 100644 --- a/compiler/ml/unified_ops.ml +++ b/compiler/ml/unified_ops.ml @@ -148,6 +148,39 @@ let entries = string = None; }; }; + { + path = builtin "<<"; + name = "%lsl"; + form = Binary; + specialization = + { + int = Plslint; + bool = None; + float = None; + bigint = Some Plslbigint; + string = None; + }; + }; + { + path = builtin ">>"; + name = "%asr"; + form = Binary; + specialization = + { + int = Pasrint; + bool = None; + float = None; + bigint = Some Pasrbigint; + string = None; + }; + }; + { + path = builtin ">>>"; + name = "%lsr"; + form = Binary; + specialization = + {int = Plsrint; bool = None; float = None; bigint = None; string = None}; + }; { path = builtin "mod"; name = "%mod"; diff --git a/compiler/syntax/src/res_parsetree_viewer.ml b/compiler/syntax/src/res_parsetree_viewer.ml index 5305bd3fc2..b2d6444e59 100644 --- a/compiler/syntax/src/res_parsetree_viewer.ml +++ b/compiler/syntax/src/res_parsetree_viewer.ml @@ -274,10 +274,11 @@ let operator_precedence operator = | "&&" -> 3 | "^" -> 4 | "==" | "===" | "<" | ">" | "!=" | "<>" | "!==" | "<=" | ">=" | "|>" -> 5 - | "+" | "+." | "-" | "-." | "++" -> 6 - | "*" | "*." | "/" | "/." | "%" -> 7 - | "**" -> 8 - | "#" | "##" | "->" -> 9 + | "<<" | ">>" | ">>>" -> 6 + | "+" | "+." | "-" | "-." | "++" -> 7 + | "*" | "*." | "/" | "/." | "%" -> 8 + | "**" -> 9 + | "#" | "##" | "->" -> 10 | _ -> 0 let is_unary_operator operator = @@ -300,7 +301,7 @@ let is_binary_operator operator = match operator with | ":=" | "||" | "&&" | "==" | "===" | "<" | ">" | "!=" | "!==" | "<=" | ">=" | "|>" | "+" | "+." | "-" | "-." | "++" | "*" | "*." | "/" | "/." | "**" - | "->" | "<>" | "%" | "^" -> + | "->" | "<>" | "%" | "^" | "<<" | ">>" | ">>>" -> true | _ -> false diff --git a/compiler/syntax/src/res_scanner.ml b/compiler/syntax/src/res_scanner.ml index c581a52abc..6ce390a68d 100644 --- a/compiler/syntax/src/res_scanner.ml +++ b/compiler/syntax/src/res_scanner.ml @@ -886,16 +886,30 @@ let rec scan scanner = | _ -> next scanner; Token.Plus) - | '>' -> ( + | '>' when not (in_diamond_mode scanner) -> ( match peek scanner with - | '=' when not (in_diamond_mode scanner) -> + | '=' -> next2 scanner; Token.GreaterEqual + | '>' -> ( + match peek2 scanner with + | '>' -> + next3 scanner; + Token.RightShiftUnsigned + | _ -> + next2 scanner; + Token.RightShift) | _ -> next scanner; Token.GreaterThan) + | '>' -> + next scanner; + Token.GreaterThan | '<' when not (in_jsx_mode scanner) -> ( match peek scanner with + | '<' when not (in_diamond_mode scanner) -> + next2 scanner; + Token.LeftShift | '=' -> next2 scanner; Token.LessEqual diff --git a/compiler/syntax/src/res_token.ml b/compiler/syntax/src/res_token.ml index 29d2b503d4..f3e8ad1cbc 100644 --- a/compiler/syntax/src/res_token.ml +++ b/compiler/syntax/src/res_token.ml @@ -98,6 +98,9 @@ type t = | Try | DocComment of Location.t * string | ModuleComment of Location.t * string + | LeftShift + | RightShift + | RightShiftUnsigned let precedence = function | HashEqual | ColonEqual -> 1 @@ -107,11 +110,12 @@ let precedence = function | Equal | EqualEqual | EqualEqualEqual | LessThan | GreaterThan | BangEqual | BangEqualEqual | LessEqual | GreaterEqual | BarGreater -> 5 - | Plus | PlusDot | Minus | MinusDot | PlusPlus -> 6 - | Asterisk | AsteriskDot | Forwardslash | ForwardslashDot | Percent -> 7 - | Exponentiation -> 8 - | MinusGreater -> 9 - | Dot -> 10 + | LeftShift | RightShift | RightShiftUnsigned -> 6 + | Plus | PlusDot | Minus | MinusDot | PlusPlus -> 7 + | Asterisk | AsteriskDot | Forwardslash | ForwardslashDot | Percent -> 8 + | Exponentiation -> 9 + | MinusGreater -> 10 + | Dot -> 11 | _ -> 0 let to_string = function @@ -212,6 +216,9 @@ let to_string = function | Try -> "try" | DocComment (_loc, s) -> "DocComment " ^ s | ModuleComment (_loc, s) -> "ModuleComment " ^ s + | LeftShift -> "<<" + | RightShift -> ">>" + | RightShiftUnsigned -> ">>>" let keyword_table = function | "and" -> And diff --git a/runtime/Pervasives.res b/runtime/Pervasives.res index 6d997a603f..1edd2cc01c 100644 --- a/runtime/Pervasives.res +++ b/runtime/Pervasives.res @@ -65,9 +65,12 @@ external \"-": ('a, 'a) => 'a = "%sub" external \"*": ('a, 'a) => 'a = "%mul" external \"/": ('a, 'a) => 'a = "%div" external \"%": ('a, 'a) => 'a = "%mod" +external \"<<": ('a, 'a) => 'a = "%lsl" external mod: ('a, 'a) => 'a = "%mod" external \"**": ('a, 'a) => 'a = "%pow" external \"^": ('a, 'a) => 'a = "%bitxor" +external \">>": ('a, 'a) => 'a = "%asr" +external \">>>": ('a, 'a) => 'a = "%lsr" /* Comparisons */ /* Note: Later comparisons will be converted to unified operations too */ diff --git a/tests/syntax_tests/data/parsing/errors/typexpr/expected/typeConstructorArgs.res.txt b/tests/syntax_tests/data/parsing/errors/typexpr/expected/typeConstructorArgs.res.txt index 4aced9f2c8..7e6bbff333 100644 --- a/tests/syntax_tests/data/parsing/errors/typexpr/expected/typeConstructorArgs.res.txt +++ b/tests/syntax_tests/data/parsing/errors/typexpr/expected/typeConstructorArgs.res.txt @@ -38,18 +38,20 @@ Syntax error! - syntax_tests/data/parsing/errors/typexpr/typeConstructorArgs.res:9:28 + syntax_tests/data/parsing/errors/typexpr/typeConstructorArgs.res:8:16-17 + 6 │ type t<'a> = private Belt.Map.t('a) 7 │ 8 │ type t = option<> 9 │ type t = option(>) 10 │ - I'm not sure what to parse here when looking at ")". + I'm not sure what to parse here when looking at "<<". type nonrec 'a node = { _value: 'a Js.Nullable.value } type nonrec 'a t = 'a Belt.Map.t type nonrec 'a t = private 'a Belt.Map.t -type nonrec t = int node option +type nonrec t = option +;;node < (int >> ([%rescript.exprhole ])) type nonrec t = int node option \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/binary.res b/tests/syntax_tests/data/parsing/grammar/expressions/binary.res index 224eb2da39..92eae96eb8 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/binary.res +++ b/tests/syntax_tests/data/parsing/grammar/expressions/binary.res @@ -29,6 +29,9 @@ let x = a + -1 + -2 let x = a + @attr -1 + @attr -2 let x = a % a == 0 let x = a ^ a == 0 +let x = a << a == 0 +let x = a >> a == 0 +let x = a >>> a == 0 // should be interpreted as binary expression not prefix op let x = a -b diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/binary.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/binary.res.txt index 14d06adaa4..7f85025c99 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/binary.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/binary.res.txt @@ -15,6 +15,9 @@ let x = (a + (-1)) + (-2) let x = (a + (((-1))[@attr ])) + (((-2))[@attr ]) let x = (a % a) == 0 let x = a ^ (a == 0) +let x = (a << a) == 0 +let x = (a >> a) == 0 +let x = (a >>> a) == 0 let x = a - b let x = a -. b ;;Constructor (a, b) diff --git a/tests/syntax_tests/data/printer/expr/binary.res b/tests/syntax_tests/data/printer/expr/binary.res index 6f43115a00..b36e4a70fc 100644 --- a/tests/syntax_tests/data/printer/expr/binary.res +++ b/tests/syntax_tests/data/printer/expr/binary.res @@ -55,6 +55,9 @@ x + y / z x / y + z x % y * z x ^ y + z +x << y + z +x >> y + z +x >>> y + z 100 * x / total 2 / 3 * 10 / 2 + 2 let rotateX = ((range / rect.height) * refY - range / 2) * getXMultiplication(rect.width) diff --git a/tests/syntax_tests/data/printer/expr/expected/binary.res.txt b/tests/syntax_tests/data/printer/expr/expected/binary.res.txt index 8717665e87..e1918cd184 100644 --- a/tests/syntax_tests/data/printer/expr/expected/binary.res.txt +++ b/tests/syntax_tests/data/printer/expr/expected/binary.res.txt @@ -86,6 +86,9 @@ x + y / z x / y + z x % y * z x ^ y + z +x << y + z +x >> y + z +x >>> y + z 100 * x / total 2 / 3 * 10 / 2 + 2 let rotateX = (range / rect.height * refY - range / 2) * getXMultiplication(rect.width) diff --git a/tests/tests/src/belt_int_test.mjs b/tests/tests/src/belt_int_test.mjs index 1c659da1e9..fe660cd9a1 100644 --- a/tests/tests/src/belt_int_test.mjs +++ b/tests/tests/src/belt_int_test.mjs @@ -37,6 +37,9 @@ Mocha.describe("Belt_int_test", () => { Test_utils.eq("File \"belt_int_test.res\", line 42, characters 7-14", 0, 0); Test_utils.eq("File \"belt_int_test.res\", line 43, characters 7-14", 0, 0); Test_utils.eq("File \"belt_int_test.res\", line 44, characters 7-14", 1, 1); + Test_utils.eq("File \"belt_int_test.res\", line 45, characters 7-14", 16, 16); + Test_utils.eq("File \"belt_int_test.res\", line 46, characters 7-14", 2, 2); + Test_utils.eq("File \"belt_int_test.res\", line 47, characters 7-14", 2, 2); }); }); diff --git a/tests/tests/src/belt_int_test.res b/tests/tests/src/belt_int_test.res index 0c77875493..8a2ee86238 100644 --- a/tests/tests/src/belt_int_test.res +++ b/tests/tests/src/belt_int_test.res @@ -42,5 +42,8 @@ describe(__MODULE__, () => { eq(__LOC__, 2 / 3, 0) eq(__LOC__, 2 % 2, 0) eq(__LOC__, 2 ^ 3, 1) + eq(__LOC__, 2 << 3, 16) + eq(__LOC__, 16 >> 3, 2) + eq(__LOC__, 16 >>> 3, 2) }) }) diff --git a/tests/tests/src/unified_ops_test.mjs b/tests/tests/src/unified_ops_test.mjs index 9d1b858078..5cae89378f 100644 --- a/tests/tests/src/unified_ops_test.mjs +++ b/tests/tests/src/unified_ops_test.mjs @@ -75,8 +75,18 @@ function bxor_bigint(a, b) { return a ^ b; } +let bigintShiftLeft = (1n << 2n); + +let bigintShiftRight = (8n >> 2n); + let int = 3; +let intShiftLeft = 4; + +let intShiftRight = 2; + +let intShiftRightUnsigned = 2147483647; + export { int, float, @@ -101,5 +111,10 @@ export { pow_overflow, bxor_int, bxor_bigint, + intShiftLeft, + intShiftRight, + intShiftRightUnsigned, + bigintShiftLeft, + bigintShiftRight, } /* No side effect */ diff --git a/tests/tests/src/unified_ops_test.res b/tests/tests/src/unified_ops_test.res index d98d453837..6d814f0a23 100644 --- a/tests/tests/src/unified_ops_test.res +++ b/tests/tests/src/unified_ops_test.res @@ -29,3 +29,10 @@ let pow_overflow = 2147483647 ** 2 let bxor_int = (a, b) => a ^ b let bxor_bigint = (a: bigint, b) => a ^ b + +let intShiftLeft = 1 << 2 +let intShiftRight = 8 >> 2 +let intShiftRightUnsigned = -2 >>> 1 + +let bigintShiftLeft = 1n << 2n +let bigintShiftRight = 8n >> 2n