diff --git a/.github/workflows/lexer.yml b/.github/workflows/lexer.yml index 3e646dd3d48..c65765c189c 100644 --- a/.github/workflows/lexer.yml +++ b/.github/workflows/lexer.yml @@ -11,6 +11,7 @@ on: - code/src/include/Rinlinedfuns.h - code/tests/reg-encodings.R - code/tests/ir-unicode-lexer.R + - code/tests/ir-package-compat.R - .github/workflows/lexer.yml jobs: @@ -46,3 +47,14 @@ jobs: - name: Test Unicode lexer priority run: build/bin/R --vanilla < code/tests/ir-unicode-lexer.R + + - name: Install package compatibility oracle + run: | + mkdir -p build/compat-lib + IR_COMPAT_LIB="$PWD/build/compat-lib" \ + build/bin/Rscript -e 'install.packages(c("rlang", "testthat", "tibble", "dplyr"), lib = Sys.getenv("IR_COMPAT_LIB"), repos = "https://cloud.r-project.org", type = "source", Ncpus = 2L)' + + - name: Test unmodified-package compatibility + env: + IR_COMPAT_LIB: ${{ github.workspace }}/build/compat-lib + run: build/bin/R --vanilla < code/tests/ir-package-compat.R diff --git a/README.md b/README.md index 77c6c4cdccb..1664b7978ad 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -### fn λ → ÷ ≟ ← +### λ ƒ → ÷ ≟ ← → and ← can now be used for assignment, not just `<-` and `->`. -`function(x) x**3` can now be written `fn(x) x**3`, `λ(x) x**3`, or `ƒ(x) x**3`. +`function(x) x**3` can now be written `λ(x) x**3` or `ƒ(x) x**3`. ÷ means division. @@ -35,7 +35,7 @@ because ≟ tests equality. | `3 → x` | assign | | `3 ↠ x` | assign in an enclosing frame | | `left ≟ right` | test equality | -| `fn(x) expression`, `λ(x) expression`, or `ƒ(x) expression` | construct a function | +| `λ(x) expression` or `ƒ(x) expression` | construct a function | | `left ÷ right` | divide | @@ -61,7 +61,7 @@ stopifnot((answer = 4)) Code that used a single `=` as assignment must use an arrow in this R. The comma and argument-label rules have otherwise been left alone. -`fn` also used to be an ordinary name. It is reserved here, so old code using bare `fn` as a variable or argument must choose another name. This source uses `fun`; in particular, write `optim(par, fun = ...)` rather than `optim(par, fn = ...)`. +`fn` is an ordinary identifier, as in standard R. Code using `fn` as a variable, formal argument, or named argument does not need to change; for example, `optim(par, fn = ...)` remains valid. `fn(...)` is an ordinary call to a function named `fn`, not function-constructor syntax. ------ diff --git a/code/src/main/gram.c b/code/src/main/gram.c index d15fd020d38..6e871b82262 100644 --- a/code/src/main/gram.c +++ b/code/src/main/gram.c @@ -4493,7 +4493,6 @@ static keywords[] = { { "NA_character_", NUM_CONST }, { "NA_complex_", NUM_CONST }, { "function", FUNCTION }, - { "fn", FUNCTION }, { "λ", FUNCTION }, { "ƒ", FUNCTION }, { "while", WHILE }, diff --git a/code/src/main/gram.y b/code/src/main/gram.y index cf39e473eb8..e15141d81cb 100644 --- a/code/src/main/gram.y +++ b/code/src/main/gram.y @@ -2179,7 +2179,6 @@ static keywords[] = { { "NA_character_", NUM_CONST }, { "NA_complex_", NUM_CONST }, { "function", FUNCTION }, - { "fn", FUNCTION }, { "λ", FUNCTION }, { "ƒ", FUNCTION }, { "while", WHILE }, diff --git a/code/tests/ir-package-compat.R b/code/tests/ir-package-compat.R new file mode 100644 index 00000000000..8658c538e4d --- /dev/null +++ b/code/tests/ir-package-compat.R @@ -0,0 +1,40 @@ +## IR compatibility smoke: bare fn must remain ordinary R syntax. + +parse1 <- function(text) parse(text = text, keep.source = FALSE)[[1L]] + +## Regression for the shape used by rlang: fn is a formal and is assigned to. +stopifnot(identical(parse1("fn"), as.name("fn"))) +call_trace_context <- eval(parse1( + "function(call, fn) { + fn <- fn + fn + }" +)) +stopifnot( + identical(names(formals(call_trace_context)), c("call", "fn")), + identical(call_trace_context(NULL, 7L), 7L) +) + +## The IR constructor spellings remain distinctive syntax. +lambda_identity <- eval(parse1("λ(fn) fn")) +florin_identity <- eval(parse1("ƒ(fn) fn")) +stopifnot( + identical(lambda_identity(11L), 11L), + identical(florin_identity(12L), 12L) +) + +## Upstream packages are the oracle: do not patch or translate them. +compat_lib <- Sys.getenv("IR_COMPAT_LIB", "build/compat-lib") +.libPaths(c(compat_lib, .libPaths())) + +packages <- c("rlang", "testthat", "tibble", "dplyr") +for (package in packages) { + suppressPackageStartupMessages( + library(package, character.only = TRUE) + ) +} + +testthat::test_that("IR spellings work inside upstream testthat", { + answer ← 8 ÷ 2 + testthat::expect_true(answer ≟ 4) +})