From fcd48cf7ab3d0f04e2bdbf1a2051e0967fb64408 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Tue, 14 Nov 2023 14:37:48 +0100 Subject: [PATCH 1/2] handle newer error in R_ParseVector(), i.e. those related to |> --- src/xinterpreter.cpp | 67 ++++++++++++++++++++++++++++++------------ test/test_xr_kernel.py | 2 +- 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index 9dc2ba8..7394d6a 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -109,27 +109,56 @@ void interpreter::configure_impl() r::invoke_xeusr_fn("configure"); } -nl::json interpreter::is_complete_request_impl(const std::string& code) +nl::json interpreter::is_complete_request_impl(const std::string& code_) { - SEXP s_code = PROTECT(Rf_mkString(code.c_str())); - ParseStatus status; - - // Currently ignore the result of R_ParseVector, and only care about status - R_ParseVector(s_code, -1, &status, R_NilValue); - UNPROTECT(1); // s_code - - switch(status) { - case PARSE_EOF: - case PARSE_NULL: - case PARSE_OK: - return xeus::create_is_complete_reply("complete", ""); - - case PARSE_INCOMPLETE: - return xeus::create_is_complete_reply("incomplete", ""); + // initially code holds the string, but then it is being + // replaced by incomplete, invalid or complete either in the + // body handler or the error handler + SEXP code = PROTECT(Rf_mkString(code_.c_str())); + + // we can't simply use an R callback because the R parse(text =) + // approach does not distinguish between invalid code and + // incomplete: they both just throw an error + R_tryCatchError( + [](void* void_code) { // body + ParseStatus status; + SEXP code = reinterpret_cast(void_code); + + R_ParseVector(code, -1, &status, R_NilValue); + + switch(status) { + case PARSE_INCOMPLETE: + SET_STRING_ELT(code, 0, Rf_mkChar("incomplete")); + break; + + case PARSE_ERROR: + SET_STRING_ELT(code, 0, Rf_mkChar("invalid")); + break; + + default: + SET_STRING_ELT(code, 0, Rf_mkChar("complete")); + } + + return R_NilValue; + }, + reinterpret_cast(code), + + [](SEXP, void* void_code) { // handler + // some parse error cases are not propagated to PARSE_ERROR + // but rather throw an error, so we need to catch it + // and set the result to invalid + SEXP code = reinterpret_cast(void_code); + SET_STRING_ELT(code, 0, Rf_mkChar("invalid")); + + return R_NilValue; + }, + reinterpret_cast(code) + ); - case PARSE_ERROR: - return xeus::create_is_complete_reply("invalid", ""); - } + // eventually we just have to extract the string from code (which has been replaced) + auto result = xeus::create_is_complete_reply(CHAR(STRING_ELT(code, 0)), ""); + UNPROTECT(1); + return result; } nl::json interpreter::complete_request_impl(const std::string& code, diff --git a/test/test_xr_kernel.py b/test/test_xr_kernel.py index b7a3d12..ac8187a 100644 --- a/test/test_xr_kernel.py +++ b/test/test_xr_kernel.py @@ -36,7 +36,7 @@ def _execute_code(self, code, tests=True, silent=False, store_history=True): complete_code_samples = ["fun()", "1 + 2", "a %>% b", "a |> b()", "a |> b(c = _)"] incomplete_code_samples = ["fun(", "1 + "] - invalid_code_samples = ["fun())"] + invalid_code_samples = ["fun())", "a |> b"] def test_stdout(self): self.flush_channels() From a2b9e02ff6b2ae0b8a6107bb4e7fa5be29a6f707 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Tue, 14 Nov 2023 14:42:21 +0100 Subject: [PATCH 2/2] more invalid_code_samples --- test/test_xr_kernel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_xr_kernel.py b/test/test_xr_kernel.py index ac8187a..a51cd0f 100644 --- a/test/test_xr_kernel.py +++ b/test/test_xr_kernel.py @@ -36,7 +36,7 @@ def _execute_code(self, code, tests=True, silent=False, store_history=True): complete_code_samples = ["fun()", "1 + 2", "a %>% b", "a |> b()", "a |> b(c = _)"] incomplete_code_samples = ["fun(", "1 + "] - invalid_code_samples = ["fun())", "a |> b"] + invalid_code_samples = ["fun())", "a |> b", "a |> b(_)", "a |> b(c(_))"] def test_stdout(self): self.flush_channels()