TIP #353: NR-ENABLED EXPRESSIONS FOR EXTENSIONS ================================================= Version: $Revision: 1.7 $ Author: Don Porter State: Final Type: Project Tcl-Version: 8.6 Vote: Done Created: Wednesday, 29 July 2009 URL: https://tip.tcl-lang.org353.html Post-History: ------------------------------------------------------------------------- ABSTRACT ========== This TIP proposes the new public routine *Tcl_NRExprObj* to provide extension commands that evaluate Tcl expressions the ability to do so in a non-recursive manner. BACKGROUND ============ In a few contexts, expressions that contain *yield* raise the error "/cannot yield: C stack busy/"; see Tcl Bugs 2823282 [] and 2823276 []. This is because a few little-visited corners of Tcl's implementation call the routine *Tcl_ExprObj* and that routine is not NR-enabled. For extensions wishing to evaluate Tcl expressions, *Tcl_ExprObj* is not little-visited. It is the public, supported, recommended tool for the job. Just as [TIP #322] provided a routine *Tcl_NREvalObj* as an NR-enabled replacement for *Tcl_EvalObj*, extensions wishing to NR-enable their commands need an analogous replacement for *Tcl_ExprObj*. RATIONALE =========== Tcl has a long history of providing extensions access to the same capabilities available to the built-in command set so that extension commands are on an equal footing, not in a second class status. Keeping with that, we want extensions to be able to create NR-enabled commands, so we need to provide an interface for extensions to evaluate expressions in an NR-enabled manner. This TIP can be seen as filling up a hole in [TIP #322]. SCOPE LIMITATIONS =================== The Tcl public C interface provides a whole family of variants of *Tcl_ExprObj*: *Tcl_ExprLongObj*, *Tcl_ExprDoubleObj*, *Tcl_ExprBooleanObj*, *Tcl_ExprLong*, *Tcl_ExprDouble*, *Tcl_ExprBoolean*, *Tcl_ExprString*. NR-enabled counterparts to these routines are /not/ proposed. Extensions rewriting their command procedures to use the proposed *Tcl_NRExprObj* for sake of NR-enabling can at the same time be expected to convert from these convenience wrappers to more direct use of a single NR-enabled primitive. PROPOSAL ========== Add the following routine to Tcl's public interface: int *Tcl_NRExprObj*(Tcl_Interp */interp/, Tcl_Obj */objPtr/, Tcl_Obj */resultPtr/) This routine places on the NR stack a request that the Tcl non-recursive trampoline evaluate the /objPtr/ value as a Tcl expression in interpreter /interp/. This routine returns the value *TCL_OK*, since there is (currently) no way this request operation can fail. The proposed interface still provides for an int return value so that future revisions to Tcl's internals have the freedom to change that without need to change the public interface. The /resultPtr/ argument must be an unshared Tcl value. When expression evaluation succeeds, the result of the expression is written to /resultPtr/ in the same way that *Tcl_SetStringObj* would write a string value to an unshared Tcl value. If expression evaluation produces any return code other than *TCL_OK*, the value of /resultPtr/ is left untouched. Callers of *Tcl_NRExprObj* will also need to call *Tcl_NRAddCallback* to request a *Tcl_NRPostProc* callback routine be placed on the NR stack which can take care of managing /resultPtr/ as appropriate depending on the /result/ value. IMPLEMENTATION ================ The patch attached to Tcl Bug 2823282 [] implements this proposal. COMPATIBILITY =============== There should be no compatibility issues, since at the interface level this is just the addition of a new routine. Revisions to the internal implementations of existing routines should be harmless. MIGRATION =========== As an example for extensions to follow, consider this template for a *Tcl_ObjCmdProc* currently calling *Tcl_ExprObj*. int ObjCmd(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { int code; Tcl_Obj *resultPtr; /* determine expression, objPtr */ code = Tcl_ExprObj(interp, objPtr, &resultPtr); if (code != TCL_OK) {return code} /* resultPtr holds expression result; continue */ } Tcl_CreateObjCommand(interp, name, ObjCmd, /* ... */); To use *Tcl_NRExprObj* to NR-enable this command, rewrite along these lines: int ObjCmd(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { return Tcl_NRCallObjProc(interp, NRObjCmd, cd, objc, objv); } int NRObjCmd(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_Obj *resultPtr = Tcl_NewObj(); /* determine expression, objPtr */ Tcl_NRAddCallback(interp, Callback, resultPtr, /*...*/); return Tcl_NRExprObj(interp, objPtr, resultPtr); } int Callback(ClientData data[], Tcl_Interp *interp, int code) { Tcl_Obj *resultPtr = data[0]; if (code != TCL_OK) {Tcl_DecrRefCount(resultPtr); return code;} /* resultPtr holds expression result; continue */ } Tcl_NRCreateCommand(interp, name, ObjCmd, NRObjCmd, /*...*/); COPYRIGHT =========== This document has been placed in the public domain. ------------------------------------------------------------------------- TIP AutoGenerator - written by Donal K. Fellows