Skip to content

Commit e160a72

Browse files
handle newer error in R_ParseVector(), i.e. those related to |> (#49)
* handle newer error in R_ParseVector(), i.e. those related to |> * more invalid_code_samples
1 parent 06c4253 commit e160a72

File tree

2 files changed

+49
-20
lines changed

2 files changed

+49
-20
lines changed

src/xinterpreter.cpp

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -109,27 +109,56 @@ void interpreter::configure_impl()
109109
r::invoke_xeusr_fn("configure");
110110
}
111111

112-
nl::json interpreter::is_complete_request_impl(const std::string& code)
112+
nl::json interpreter::is_complete_request_impl(const std::string& code_)
113113
{
114-
SEXP s_code = PROTECT(Rf_mkString(code.c_str()));
115-
ParseStatus status;
116-
117-
// Currently ignore the result of R_ParseVector, and only care about status
118-
R_ParseVector(s_code, -1, &status, R_NilValue);
119-
UNPROTECT(1); // s_code
120-
121-
switch(status) {
122-
case PARSE_EOF:
123-
case PARSE_NULL:
124-
case PARSE_OK:
125-
return xeus::create_is_complete_reply("complete", "");
126-
127-
case PARSE_INCOMPLETE:
128-
return xeus::create_is_complete_reply("incomplete", "");
114+
// initially code holds the string, but then it is being
115+
// replaced by incomplete, invalid or complete either in the
116+
// body handler or the error handler
117+
SEXP code = PROTECT(Rf_mkString(code_.c_str()));
118+
119+
// we can't simply use an R callback because the R parse(text =)
120+
// approach does not distinguish between invalid code and
121+
// incomplete: they both just throw an error
122+
R_tryCatchError(
123+
[](void* void_code) { // body
124+
ParseStatus status;
125+
SEXP code = reinterpret_cast<SEXP>(void_code);
126+
127+
R_ParseVector(code, -1, &status, R_NilValue);
128+
129+
switch(status) {
130+
case PARSE_INCOMPLETE:
131+
SET_STRING_ELT(code, 0, Rf_mkChar("incomplete"));
132+
break;
133+
134+
case PARSE_ERROR:
135+
SET_STRING_ELT(code, 0, Rf_mkChar("invalid"));
136+
break;
137+
138+
default:
139+
SET_STRING_ELT(code, 0, Rf_mkChar("complete"));
140+
}
141+
142+
return R_NilValue;
143+
},
144+
reinterpret_cast<void*>(code),
145+
146+
[](SEXP, void* void_code) { // handler
147+
// some parse error cases are not propagated to PARSE_ERROR
148+
// but rather throw an error, so we need to catch it
149+
// and set the result to invalid
150+
SEXP code = reinterpret_cast<SEXP>(void_code);
151+
SET_STRING_ELT(code, 0, Rf_mkChar("invalid"));
152+
153+
return R_NilValue;
154+
},
155+
reinterpret_cast<void*>(code)
156+
);
129157

130-
case PARSE_ERROR:
131-
return xeus::create_is_complete_reply("invalid", "");
132-
}
158+
// eventually we just have to extract the string from code (which has been replaced)
159+
auto result = xeus::create_is_complete_reply(CHAR(STRING_ELT(code, 0)), "");
160+
UNPROTECT(1);
161+
return result;
133162
}
134163

135164
nl::json interpreter::complete_request_impl(const std::string& code,

test/test_xr_kernel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def _execute_code(self, code, tests=True, silent=False, store_history=True):
3636

3737
complete_code_samples = ["fun()", "1 + 2", "a %>% b", "a |> b()", "a |> b(c = _)"]
3838
incomplete_code_samples = ["fun(", "1 + "]
39-
invalid_code_samples = ["fun())"]
39+
invalid_code_samples = ["fun())", "a |> b", "a |> b(_)", "a |> b(c(_))"]
4040

4141
def test_stdout(self):
4242
self.flush_channels()

0 commit comments

Comments
 (0)