[Rcpp-devel] Catching parse errors with RInside::parseEval()
Dirk Eddelbuettel
edd at debian.org
Thu Oct 11 00:28:42 CEST 2012
Hi Theodore,
On 11 October 2012 at 00:39, Theodore Lytras wrote:
| I studied RInside.cpp, made a few trial-and-error tests, and it appears to
| work for me now - parseEval() doesn't crash when fed with syntactically
| incorrect R code.
|
| I am attaching a patch I made with quilt (interactive.diff), so you can see
| what I did.
|
| Turns out, that boolean you were talking about (on line 152) did no good.
| Leaving it to false results in a crash ("Execution halted"), switching it to
| true results in a crash plus a segmentation fault!
|
| Turns out also that the switch statement is indeed evaluated. It gets there.
| What halts execution is indeed the Rf_error() function; changing that to
| Rf_warning() solves the problem, and allows parseEval() to return 1 and
| parseEvalQ() to throw its exception.
Good catch.
| One also needs to call mb_m.rewind() afterwards, or the MemBuf is not cleared
| and subsequent calls to parseEval() fail as well.
Very good, that's along the lines that I was thinking about.
| As a result I added a boolean to RInside.h (interactiveMode) along with a
| setter function (setInteractive(bool)), in order to enable this behaviour
| instead of the old one.
|
| Now it works perfectly for me!
|
| As an alternative, we could get rid of the boolean altogether and also get rid
| of the Rf_error() or Rf_warning(). I don't think it can ever be good to "halt
| execution" when dealing with syntactically incorrect R code. Returning an
| error code or throwing an exception is better. And the Rf_warning() is also
| unnecessary, if case PARSE_NULL: and case PARSE_ERROR: return their own error
| code (eg. 2 and 3). No code using RInside should be affected by this, since
| until now the error code was not returned and execution was halted at
| Rf_error().
|
| So if you aggree with that, you can apply instead the other patch I'm
| attaching (simplified.diff). I guess that's the best solution.
|
| Hope this helps a bit! It sure solved my problem.
That helps a ton. I glanced briefly at the simpler patch, and I will start
with that.
Thanks for all this -- this is greatly appreciated.
Cheers, Dirk
| Regards,
|
| Theodore
|
| ----------------------------------------------------------------------
| Index: RInside/inst/include/RInside.h
| ===================================================================
| --- RInside.orig/inst/include/RInside.h 2010-07-09 23:59:14.000000000 +0300
| +++ RInside/inst/include/RInside.h 2012-10-10 19:25:37.302217289 +0300
| @@ -32,6 +32,7 @@
| Rcpp::Environment global_env ;
|
| bool verbose_m; // private switch
| + bool interactiveMode; // if true, parseEval() will not cause crash when evaluating a line with syntax errors
|
| void init_tempdir(void);
| void init_rand(void);
| @@ -56,6 +57,8 @@
| int parseEval(const std::string & line, SEXP &ans); // parse line, return in ans; error code rc
| void parseEvalQ(const std::string & line); // parse line, no return (throws on error)
|
| + void setInteractive(const bool interactive) { interactiveMode = interactive; }
| +
| class Proxy {
| public:
| Proxy(SEXP xx): x(xx) { };
| Index: RInside/src/RInside.cpp
| ===================================================================
| --- RInside.orig/src/RInside.cpp 2012-09-08 04:00:46.000000000 +0300
| +++ RInside/src/RInside.cpp 2012-10-10 19:30:08.427279004 +0300
| @@ -57,7 +57,7 @@
|
| RInside::RInside()
| #ifdef RINSIDE_CALLBACKS
| - : callbacks(0)
| + : interactiveMode(false), callbacks(0)
| #endif
| {
| initialize( 0, 0, false );
| @@ -99,7 +99,7 @@
|
| RInside::RInside(const int argc, const char* const argv[], const bool loadRcpp)
| #ifdef RINSIDE_CALLBACKS
| -: callbacks(0)
| +: interactiveMode(false), callbacks(0)
| #endif
| {
| initialize( argc, argv, loadRcpp );
| @@ -333,14 +333,28 @@
| // need to read another line
| break;
| case PARSE_NULL:
| - Rf_error("%s: ParseStatus is null (%d)\n", programName, status);
| - UNPROTECT(2);
| - return 1;
| + if (interactiveMode) {
| + Rf_warning("%s: ParseStatus is null (%d)\n", programName, status);
| + UNPROTECT(2);
| + mb_m.rewind();
| + return 1;
| + } else {
| + Rf_error("%s: ParseStatus is null (%d)\n", programName, status);
| + UNPROTECT(2);
| + return 1;
| + }
| break;
| case PARSE_ERROR:
| - Rf_error("Parse Error: \"%s\"\n", line.c_str());
| - UNPROTECT(2);
| - return 1;
| + if (interactiveMode) {
| + Rf_warning("Parse Error: \"%s\"\n", line.c_str());
| + UNPROTECT(2);
| + mb_m.rewind();
| + return 1;
| + } else {
| + Rf_error("Parse Error: \"%s\"\n", line.c_str());
| + UNPROTECT(2);
| + return 1;
| + }
| break;
| case PARSE_EOF:
| Rf_error("%s: ParseStatus is eof (%d)\n", programName, status);
|
| ----------------------------------------------------------------------
| Index: RInside/src/RInside.cpp
| ===================================================================
| --- RInside.orig/src/RInside.cpp 2012-10-11 00:30:43.228525248 +0300
| +++ RInside/src/RInside.cpp 2012-10-11 00:32:01.676564524 +0300
| @@ -333,14 +333,14 @@
| // need to read another line
| break;
| case PARSE_NULL:
| - Rf_error("%s: ParseStatus is null (%d)\n", programName, status);
| UNPROTECT(2);
| - return 1;
| + mb_m.rewind();
| + return 2;
| break;
| case PARSE_ERROR:
| - Rf_error("Parse Error: \"%s\"\n", line.c_str());
| UNPROTECT(2);
| - return 1;
| + mb_m.rewind();
| + return 3;
| break;
| case PARSE_EOF:
| Rf_error("%s: ParseStatus is eof (%d)\n", programName, status);
--
Dirk Eddelbuettel | edd at debian.org | http://dirk.eddelbuettel.com
More information about the Rcpp-devel
mailing list