diff --git a/CHANGELOG.md b/CHANGELOG.md index ba3fd69f59..dde3c2e946 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,8 @@ #### :rocket: New Feature -- Introduce "Unified operators" for arithmetic operators (`+`, `-`, `*`, `/`, `mod`). See https://github.com/rescript-lang/rescript-compiler/pull/7057 +- Introduce "Unified operators" for arithmetic operators (`+`, `-`, `*`, `/`, `mod`). https://github.com/rescript-lang/rescript-compiler/pull/7057 +- Add remainder (`%`, aka modulus) operator. https://github.com/rescript-lang/rescript-compiler/pull/7152 # 12.0.0-alpha.4 diff --git a/compiler/ml/unified_ops.ml b/compiler/ml/unified_ops.ml index 3aaa4dd021..c57c14bce0 100644 --- a/compiler/ml/unified_ops.ml +++ b/compiler/ml/unified_ops.ml @@ -135,6 +135,19 @@ let entries = string = None; }; }; + { + path = builtin "%"; + name = "%mod"; + form = Binary; + specialization = + { + int = Pmodint Safe; + bool = None; + float = Some Pmodfloat; + bigint = Some Pmodbigint; + string = None; + }; + }; { path = builtin "mod"; name = "%mod"; diff --git a/compiler/syntax/src/res_core.ml b/compiler/syntax/src/res_core.ml index a4779d7b90..7538874010 100644 --- a/compiler/syntax/src/res_core.ml +++ b/compiler/syntax/src/res_core.ml @@ -2221,7 +2221,7 @@ and parse_binary_expr ?(context = OrdinaryExpr) ?a p prec = * * First case is unary, second is a binary operator. * See Scanner.isBinaryOp *) - | (Minus | MinusDot | LessThan) + | (Minus | MinusDot | LessThan | Percent) when (not (Scanner.is_binary_op p.scanner.src p.start_pos.pos_cnum p.end_pos.pos_cnum)) diff --git a/compiler/syntax/src/res_parsetree_viewer.ml b/compiler/syntax/src/res_parsetree_viewer.ml index 0b3b0ae857..1e751ded1e 100644 --- a/compiler/syntax/src/res_parsetree_viewer.ml +++ b/compiler/syntax/src/res_parsetree_viewer.ml @@ -302,7 +302,7 @@ let operator_precedence operator = | "&&" -> 3 | "=" | "==" | "<" | ">" | "!=" | "<>" | "!==" | "<=" | ">=" | "|>" -> 4 | "+" | "+." | "-" | "-." | "^" -> 5 - | "*" | "*." | "/" | "/." -> 6 + | "*" | "*." | "/" | "/." | "%" -> 6 | "**" -> 7 | "#" | "##" | "|." | "|.u" -> 8 | _ -> 0 @@ -326,7 +326,7 @@ let is_binary_operator operator = match operator with | ":=" | "||" | "&&" | "=" | "==" | "<" | ">" | "!=" | "!==" | "<=" | ">=" | "|>" | "+" | "+." | "-" | "-." | "^" | "*" | "*." | "/" | "/." | "**" | "|." - | "|.u" | "<>" -> + | "|.u" | "<>" | "%" -> true | _ -> false diff --git a/compiler/syntax/src/res_token.ml b/compiler/syntax/src/res_token.ml index 7410b3ba38..b55d7c0ec7 100644 --- a/compiler/syntax/src/res_token.ml +++ b/compiler/syntax/src/res_token.ml @@ -106,7 +106,7 @@ let precedence = function | BangEqualEqual | LessEqual | GreaterEqual | BarGreater -> 4 | Plus | PlusDot | Minus | MinusDot | PlusPlus -> 5 - | Asterisk | AsteriskDot | Forwardslash | ForwardslashDot -> 6 + | Asterisk | AsteriskDot | Forwardslash | ForwardslashDot | Percent -> 6 | Exponentiation -> 7 | MinusGreater -> 8 | Dot -> 9 diff --git a/runtime/Pervasives.res b/runtime/Pervasives.res index f3552aae89..a19348538d 100644 --- a/runtime/Pervasives.res +++ b/runtime/Pervasives.res @@ -49,6 +49,7 @@ external \"+": ('a, 'a) => 'a = "%add" external \"-": ('a, 'a) => 'a = "%sub" external \"*": ('a, 'a) => 'a = "%mul" external \"/": ('a, 'a) => 'a = "%div" +external \"%": ('a, 'a) => 'a = "%mod" external mod: ('a, 'a) => 'a = "%mod" /* Comparisons */ diff --git a/runtime/Pervasives_mini.res b/runtime/Pervasives_mini.res index d38b877a32..9cf3cd90e4 100644 --- a/runtime/Pervasives_mini.res +++ b/runtime/Pervasives_mini.res @@ -28,6 +28,7 @@ external \"+": (int, int) => int = "%addint" external \"-": (int, int) => int = "%subint" external \"*": (int, int) => int = "%mulint" external \"/": (int, int) => int = "%divint" +external \"%": (int, int) => int = "%modint" external mod: (int, int) => int = "%modint" /* Comparisons */ diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/binary.res b/tests/syntax_tests/data/parsing/grammar/expressions/binary.res index 19e31b21ee..6c89739553 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/binary.res +++ b/tests/syntax_tests/data/parsing/grammar/expressions/binary.res @@ -27,6 +27,7 @@ let x = z |> @attr while condition { () } let x = a + -1 + -2 let x = a + @attr -1 + @attr -2 +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 9f2306aa06..7d4bc6327e 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 @@ -13,6 +13,7 @@ let x = while condition do () done z let x = ((while condition do () done)[@attr ]) z let x = (a + (-1)) + (-2) let x = (a + (((-1))[@attr ])) + (((-2))[@attr ]) +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 22507f3eee..6e47a546b3 100644 --- a/tests/syntax_tests/data/printer/expr/binary.res +++ b/tests/syntax_tests/data/printer/expr/binary.res @@ -53,6 +53,7 @@ let () = (x: int) |> (print_int: int => unit) // math 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 24dfb51c05..e57e20fedb 100644 --- a/tests/syntax_tests/data/printer/expr/expected/binary.res.txt +++ b/tests/syntax_tests/data/printer/expr/expected/binary.res.txt @@ -84,6 +84,7 @@ let () = (print_int: int => unit)((x: int)) // math 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 1d4d38075f..edcada2a0a 100644 --- a/tests/tests/src/belt_int_test.mjs +++ b/tests/tests/src/belt_int_test.mjs @@ -35,6 +35,7 @@ Mocha.describe("Belt_int_test", () => { Test_utils.eq("File \"belt_int_test.res\", line 40, characters 7-14", -1, -1); Test_utils.eq("File \"belt_int_test.res\", line 41, characters 7-14", 6, 6); 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); }); }); diff --git a/tests/tests/src/belt_int_test.res b/tests/tests/src/belt_int_test.res index d110a66f95..a6c418b7ca 100644 --- a/tests/tests/src/belt_int_test.res +++ b/tests/tests/src/belt_int_test.res @@ -40,5 +40,6 @@ describe(__MODULE__, () => { eq(__LOC__, 2 - 3, -1) eq(__LOC__, 2 * 3, 6) eq(__LOC__, 2 / 3, 0) + eq(__LOC__, 2 % 2, 0) }) }) diff --git a/tests/tests/src/unified_ops_test.mjs b/tests/tests/src/unified_ops_test.mjs index 03f68c97fd..c9635201cc 100644 --- a/tests/tests/src/unified_ops_test.mjs +++ b/tests/tests/src/unified_ops_test.mjs @@ -51,6 +51,14 @@ function case2(a, b) { return a + "test" + b; } +function even(n) { + return n % 2 === 0; +} + +function odd(n) { + return n % 2 === 1; +} + let int = 3; export { @@ -69,5 +77,7 @@ export { rhsstring, case1, case2, + even, + odd, } /* No side effect */ diff --git a/tests/tests/src/unified_ops_test.res b/tests/tests/src/unified_ops_test.res index 610a527a97..c35588167f 100644 --- a/tests/tests/src/unified_ops_test.res +++ b/tests/tests/src/unified_ops_test.res @@ -17,3 +17,6 @@ let rhsstring = (a, b: string) => a + b let case1 = a => 1 + a let case2 = (a, b) => a + "test" + b + +let even = n => n % 2 == 0 +let odd = n => n % 2 == 1