Summary: Would it be possible to have something like a [namespace eval]
command, to override the resolution of (primarily) command names within a
code fragment, that does not also shadow the local variables?
The concrete problem that got me thinking about this was a networking
protocol that (like many Tcl programs) involve exchanging composite data
values, but which (unlike Tcl) is dynamically typed, so that e.g. sending
"9" as a number might end up meaning something different than sending it as
a string. It would be possible to devise an encoding (with explicit type
tags for each piece of a value) of the foreign data model and require users
to express their messages in that format, but it would not produce a
pleasant programming experience. Instead, it occurred to me that I'd like to
try an interface where the user supplies a _script_ that constructs the
message piece item by item -- for example
$connectionObj send {
apply {
symbol relation1 eq
apply {
symbol artih1 plus
integer 2
integer 2
}
integer 4
}
}
might result in first forming and then transmitting the XML fragment
<OMA><OMS cd="relation1" name="eq"/><OMA><OMS cd="arith1"
name="plus"/><OMI>2</OMI><OMI>2</OMI></OMA><OMI>4</OMI></OMA>
The documentation of the protocol I'm thinking about (which is not what is
shown the example, but I feel more familiar with the above) uses a similar
layout with one atomic data item per line when showing the structure of
messages, so I would expect it to look reasonable also in production code.
I've been successful with this kind of technically-a-script encoding of
structured data several times in the past -- parsing ends up simply being
evaluation in a prepared context where the commands each encode and append
one data item to the data structure being constructed -- but in those cases
I used it for encoding static data. When used to construct messages, one
would rather want the script to have access to all local variables in the
calling context, but all techniques I can think of for making sure that the
item-creating commands are interpreted in that exceptional way also have the
side-effect of switching the context for variable resolution. Good old
[namespace eval], for example, causes unqualified variable names to
reference variables in the specified namespace rather than local variables;
if one does
namespace eval ::foo {
proc int {num} { #... }
proc string {text} { #... }
proc array {script} { #... }
proc dict {script} { #... }
}
proc caller {somearg} {
set somevar 2
# ...
namespace eval ::foo {
string bar
int 3
int [expr {2+2}]
array {
string baz
for {set k 0} {$k < 4} {incr k} {
int $k
}
}
int $somevar ; # Error here
}
}
then [caller whatever] will error out at the
int $somevar
because that references $::foo::somevar, not the $somevar variable local to
the caller procedure. The [for] loop works (and mixing data generation with
standard control structures like that is a nifty feature of this kind of
generation script), but it leaves $::foo::k globally set to 4, which is not
necessarily expected.
For me right now, there are ways of going forward -- e.g. run the data
construction script in a helper procedure into which I copy all local
variables from the calling context, or require users to instead of variable
substitution use some [set]-like command for accessing local variables --
but it would be a lot nicer if there was some sibling of [namespace eval]
that could make the above Just Work. And from RTFS, this doesn't look
implausible at all!
In tclInt.h, we find
typedef struct CallFrame {
Namespace *nsPtr; /* Points to the namespace used to resolve
* commands and global variables. */
int isProcCallFrame; /* If 0, the frame was pushed to execute a
* namespace command and var references are
* treated as references to namespace vars;
* varTablePtr and compiledLocals are ignored.
* If FRAME_IS_PROC is set, the frame was
* pushed to execute a Tcl procedure and may
* have local vars. */
[namespace eval] pushes a CallFrame with nsPtr to a specified namespace, and
isProcCallFrame set to 0. The [namespace localeval] (or whatever) should
temporarily switch the nsPtr? Or push a new CallFrame with the desired nsPtr
but isProcCallFrame nonzero? My gut feeling is that it's not quite that easy
(due to this being very central areas where any change can have tricky
side-effects), but that it shouldn't be /very/ difficult either. (Of course,
the NRE rewrite didn't exactly simplify the code structure in these parts.)
Tcl_EvalObjv does a bunch of temporarily swapping the namespace, which I
suppose is one way to achieve the result I want, but maybe that is related
to the TCL_EVAL_INVOKE mode of operation; I seem to recall that this has
been troublesome in the past, so it might not be something to imitate. Right
now the sanest way forward rather seems to be to push a new stack frame,
whose local variable fields (isProcCallFrame, varTablePtr, compiledLocals,
etc.) are copied from an existing stack frame, but whose nsPtr can be
something completely different. Are there any obvious problems with that
approach? Would there perhaps need to be a flag for "don't delete the
variables when popping stack frame -- they're not owned by it?" Well, at the
moment I'm mostly brainstorming.
One could of course argue that the proposed use-case is a bad idea -- that
having script arguments of commands where some commands (string, array, and
dict appear to be likely victims in the intended use-case) mean something
completely different than they do in the calling context is an inherently
confusing design and should be avoided -- but I don't think it would be.
Also it does have some precedence in for example the oo::define defScript,
which sports a whole bunch of semi-private commands of which the variable
command is not at all the vanilla ::variable command. As it happens, that
too involves some unusual CallFrame manoeuvres (FRAME_IS_OO_DEFINE) ...
Feedback, anyone? I expect that whatever approach one would try, the big
danger is that one somehow ends up confusing the compiler, so any insight
into how to not do that is appreciated.
Lars Hellström
------------------------------------------------------------------------------
BPM Camp - Free Virtual Workshop May 6th at 10am PDT/1PM EDT
Develop your own process in accordance with the BPMN 2 standard
Learn Process modeling best practices with Bonita BPM through live exercises
http://www.bonitasoft.com/be-part-of-it/events/bpm-camp-virtual- event?utm_
source=Sourceforge_BPM_Camp_5_6_15&utm_medium=email&utm_campaign=VA_SF
_______________________________________________
Tcl-Core mailing list
Tcl-...@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/tcl-core