[Rd] Emulating a REPL in frontends
Thomas Friedrichsmeier
thomas.friedrichsmeier at ruhr-uni-bochum.de
Thu Jan 18 13:47:40 CET 2007
A common need in R frontends is to provide some sort of read, (parse),
evaluate, print loop. However, there are also a number of points where
frontends may want to differ from the standard REPL as available e.g. in
R_ReplDLLdo1().
First some thoughts on what is needed, and what is already there, or missing.
If you want to skip over this, a short summary is provided in the second
half, below a line marked "--------":
Read stage:
- R_ReplDLLdo1() calls ReadConsole to fetch an input buffer. This is not well
ideally for some situations. For example, the "Console", and the R process
may be running on different machines. Or a frontend may provide several
consoles or console-alikes working on the same workspace. Or the frontend may
want to differentiate between "submitting code to the backend", and answering
requests for input from read.line().
Parse stage:
- If (due to the above mentioned aspects, or for some other reason), a
frontend does not use R_ReplDLLdo1(), it will have to use R_ParseVector() for
parsing. There's nothing wrong with that, except for the fact, that there
does not seem to be a way to get at the full error message in case of a parse
error, as parseError() is not exported.
Evaluate stage:
- Frontends may want to evaluate a sequence of statements at once, or one at a
time.
- A typical requirement of a frontend is for evaluation to be guaranteed to
return to the point of code it was called from.
- Both of this is available using R_tryEval().
Print stage:
- Frontends may want to always print the result of evaluation for some
statements, they may want to suppress printing entirely for some (internal)
statement, but most importantly, they will probably want to auto-print values
for most statements (i.e. print them if and only if R_Visible is TRUE).
- There does not seem to be a good way (from the C-API) to do auto-printing of
values outside of R_ReplDLLdo1(): R_Visible is not exported any longer, and
PrintValueEnv() is neither. Rf_PrintValue() should yield the same results as
PrintValueEnv() in most, but probably not all cases.
- From R API, withVisible() provides a way to emulate auto-printing, but with
the drawback that wrapping all statements inside withVisible() makes
potential errors harder to read (you get something like "Error in
eval.with.vis(x, parent.frame(), baseenv()) :", unless using extra magic to
convert the Error magic back to "normal").
Other things done in R_ReplDLLdo1() / other REPLs:
- Frontends may want to set R_LastValueSymbol for most (statement entered by
the user directly), but not all statements (so as to keep .Last.value
unchanged over internal operations like e.g. updating information on an
object in an object browser)
- Frontends may want to call toplevel handlers after some statements, but not
after others (probably a similar distinction).
----------
In summary, it seems like many frontends can not rely on R_ReplDLLdo1() (let
alone running run_Rmainloop()), or at least not exclusively so. The
alternative, using R_ParseVector() and R_tryEval() also has drawbacks, most
importantly problems getting at parse errors, and auto-printing.
Two suggestions, which I hope are not too intrusive:
1) Make parseError() part of the public API. If there is a concern that the
details of this function may change, a wrapper like the following should be
good enough for most, if not all frontend purposes:
void Rf_parseErrorDefault()
{
parseError(R_NilValue, 0);
}
2) Add a simple function to do auto-printing to the public API. E.g.:
Rboolean Rf_doAutoPrint(SEXP exp)
{
if(R_Visible) {
printValueEnv(exp, R_GlobalEnv);
return TRUE; /* was printed */
}
return FALSE;
}
Another suggestion, which might well be seen as adding too many constraints on
future development, but provided for completeness: A more generic version of
the EPL-part of R_ReplDLLDo1() with function parameters to determine, which
steps are taken/omitted. I'm attaching a sketch of this. It is a copy with
minimal modifications of the relevant code sections from R_ReplDLLdo1() and
friends, which could then be simplified to use this function, internally.
Regards
Thomas Friedrichsmeier
-------------- next part --------------
/* print mode: 0: print if visible. 1: always print 2: never print */
SEXP R_DLLGenericEplDo1 (unsigned char *buffer, ParseStatus *parse_status, Rboolean set_last_sym_value, int print_mode, Rboolean do_toplevel_callbacks)
{
int c;
ParseStatus status;
SEXP value;
SEXP rho = R_GlobalEnv;
Rboolean wasDisplayed = FALSE;
while((c = *buffer++)) {
R_IoBufferPutc(c, &R_ConsoleIob);
if(c == ';' || c == '\n') break;
}
R_PPStackTop = 0;
R_CurrentExpr = R_Parse1Buffer(&R_ConsoleIob, 0, &status);
if(parse_status) *parse_status = status;
switch(status) {
case PARSE_NULL:
R_IoBufferWriteReset(&R_ConsoleIob);
break;
case PARSE_OK:
R_IoBufferReadReset(&R_ConsoleIob);
R_CurrentExpr = R_Parse1Buffer(&R_ConsoleIob, 1, &status);
R_Visible = FALSE;
R_EvalDepth = 0;
PROTECT(R_CurrentExpr);
R_Busy(1);
value = eval(R_CurrentExpr, rho);
if(set_last_sym_value)
SET_SYMVALUE(R_LastvalueSymbol, value);
if ((print_mode==1) || (R_Visible && (print_mode==0)))
wasDisplayed = TRUE;
PrintValueEnv(R_CurrentExpr, rho);
if (R_CollectWarnings)
PrintWarnings();
if(do_toplevel_callbacks) {
Rf_callToplevelHandlers(R_CurrentExpr, value, TRUE, wasDisplayed);
}
R_CurrentExpr = value;
UNPROTECT(1);
R_IoBufferWriteReset(&R_ConsoleIob);
R_Busy(0);
return value;
case PARSE_ERROR:
parseError(R_NilValue, 0);
R_IoBufferWriteReset(&R_ConsoleIob);
break;
case PARSE_INCOMPLETE:
R_IoBufferReadReset(&R_ConsoleIob);
break;
case PARSE_EOF:
break;
}
return R_NilValue;
}
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
Url : https://stat.ethz.ch/pipermail/r-devel/attachments/20070118/e635bb8a/attachment.bin
More information about the R-devel
mailing list