From thomas.friedrichsmeier at ruhr-uni-bochum.de  Thu Jan 18 13:47:40 2007
From: thomas.friedrichsmeier at ruhr-uni-bochum.de (Thomas Friedrichsmeier)
Date: Thu, 18 Jan 2007 13:47:40 +0100
Subject: [Rd] Emulating a REPL in frontends
Message-ID: <200701181347.44744.thomas.friedrichsmeier@ruhr-uni-bochum.de>

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 


[Rd] Emulating a REPL in frontends
Thomas Friedrichsmeier