TIP:		353
Title:		NR-enabled Expressions for Extensions
Version:	$Revision: 1.7 $
Author:		Don Porter <dgp@users.sf.net>
State:		Final
Type:		Project
Vote:		Done
Created:	29-Jul-2009
Tcl-Version:	8.6
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
[https://sourceforge.net/support/tracker.php?aid=2823282] and 2823276
[https://sourceforge.net/support/tracker.php?aid=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 [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 [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
[https://sourceforge.net/support/tracker.php?aid=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.
