Tcl Source Code

View Ticket
Bounty program for improvements to Tcl and certain Tcl packages.
Ticket UUID: 536831
Title: Tcl_SubstObj: add precompilation
Type: RFE Version: None
Submitter: dgp Created on: 2002-03-29 18:25:20
Subsystem: 18. Commands M-Z Assigned To: dgp
Priority: 4 Severity:
Status: Closed Last Modified: 2003-03-13 09:41:28
Resolution: Duplicate Closed By: dgp
    Closed on: 2003-03-13 02:41:28
Lines 2442-2448 of tclCmdMZ.c (Tcl_SubstObj):

 *      This function performs the substitutions
specified on the
 *      given string as described in the user
documentation for the
 *      "subst" Tcl command.  This code is heavily
based on an
 *      implementation by Andrew Payne.  Note that if a
 *      substitution returns TCL_CONTINUE or TCL_RETURN
from its
 *      evaluation and is not completely well-formed,
the results are
 *      not defined.

I don't know what that last comment means, or if it
is even still valid.  Some examples would help, and
it this is an important special case, it should be
noted in subst.n.
User Comments: dgp added on 2003-03-13 09:41:28:
Logged In: YES 

The patches for FR 684982 now
fully contain these.  Closing...

dgp added on 2003-03-13 09:08:13:
File Added - 44902: alternative.patch

Logged In: YES 

I was wrong about being wrong. :)

Here's an alternative patch that
preserves more compatibility with
prior Tcl releases.  In particular,
only 4 tests in subst.test fail
after applying the alternative patch,
and those demonstrate behavior
that clearly has no value.

dkf added on 2003-03-11 18:57:06:
Logged In: YES 

Ah!  So you agree with me, and want to go the route that
leads to precompilation.

The expensive bit's the parsing of commands, and it was
something I never really understood in detail enough.  But
if you can get it so that that is handled correctly, I can
then take that and produce the actual compilation (which'll
speed up repeated substitution of the same string...)

dgp added on 2003-03-11 04:33:07:
Logged In: YES 

I was wrong.  There's really no straightforward
way to subst up to where the parsing error
occurs.  not without getting into a full
specification of how each kind of parsing
error would be treated.  If we have to 
invent a spec. anyway, best to invent the
simplest one:  any parsing error throws an
error before any substitution gets done.

dkf added on 2003-03-10 18:47:14:
Logged In: YES 

I assume you're proposing that that test give a result of
   {1 0 {missing bracket}}
or something like that?  If so, I think that's an inevitable
result if doing the job properly and so the test should be
modified.  You might want to ask the opinion of other TCT
members on this though, and maybe also some other people
like Chang Li who (IIRC) expressed interest in the original
creation of Tcl_SubstObj()...

(Part of the problem is that I don't really use [subst] in
my own code.)

dgp added on 2003-03-09 05:33:29:
Logged In: YES 

Please comment on the
incompatibility represented
by test subst-5.7.

The patch can be modified
so that subst-5.7 continues
to pass, by seeing that any
substitution side effects up to
the parsing error are still carried out.

Should it be so modified?

dgp added on 2003-03-08 03:42:53:
File Added - 44395: subst.patch

Logged In: YES 

Here's a patch that completely
replaces Tcl_SubstObj() with
a new implementation that makes
use of the same (slightly modified)
internal parsing and substitution
engines as normal script evaluation.

The script-level difference that will
be seen is reflected in the 7 tests
in subst.test that fail after applying this
patch.  They fail because this patch
changes the way [subst] deals with
parsing errors.  As requested by
this Feature Request (and in agreement
with the way "-quoted words get substituted
in normal Tcl scripts), after applying this
patch parsing errors will be detected and
reported before anything else.

This patch also paves the way for a
larger cleanup in the inner evaluation
routines, making them smaller and simpler
(see FR 684982).

This patch also fixes Bug 685106.

By making better code re-use, this patch
also set the stage for any parsing
improvements for normal script evaluation
(for example, a Tcl_ObjType that caches
pased token sequences) to equally benefit
the [subst] command.

dkf added on 2002-11-16 19:05:48:
Logged In: YES 

Need (optional?) capability to precompile subst-ed objects;
feature probably could do with flags to control whether it
happens or not as you'll take a performance hit for one-off
things even though it'll be better when repeated application

At the same time, adding scanning for a matching close
bracket would speed things up mightily and would simplify
other parts of the core (as you'd always be evaluating
complete scripts and not scripts with possible trailing junk.)

dkf added on 2002-04-18 21:18:59:
Logged In: YES 

Partial fix; the comment should be clearer now. But the
underlying problem hasn't gone away.

dkf added on 2002-04-18 20:22:18:
Logged In: YES 

The problem is that the code to recover from an exceptional
situation (i.e. continue) can't figure out where the script
portion of the string ends when that script is not
well-formed.  In a sense, the terminator for the script
actually lies beyond the end of the string it is embedded
in!  This is not good.  At this point, it either picks a
random square bracket that would otherwise have been part of
the script itself, or just uses the end of the command that
generated the exceptional condition, and while it would be
possible (I suppose) to characterise this behaviour, it
seems to me that this would be specifying a bug.

As I said below, the fix is to work out where the boundaries
of the command are before evaluating it.  That's best
combined with some kind of cached compilation step (you can
only cache if the subst flags are the same, of course.)

Nothing else in Tcl uses this tricky kind of simultaneous
parsing and evaluation.  (Not nowadays anyway.) 
Furthermore, it is only now that there is a way to tell it
occurred because before all exceptions stopped the
processing of the string.

dgp added on 2002-04-06 02:50:35:
Logged In: YES 

I still don't think I get it.  Here's what I see in
Tcl 8.3.4:

% subst {abc[continue;[foobarspong}
invalid command name "foobarspong"
% subst {abc[continue;[foobarspong]}
% subst {abc[continue;[foobarspong]]}

And here's what I see in the HEAD:

% subst {abc[continue;[foobarspong}
missing close-bracket
% subst {abc[continue;[foobarspong]}
% subst {abc[continue;[foobarspong]]}

In both cases, the first is an error, and
the latter two show the effect of [continue]
in a [subst].

Where's the "not defined" here?  Is that so we
can change the behavior later?  What do we want
to change it to?

dkf added on 2002-04-05 15:45:07:
Logged In: YES 

Example that exhibits the problem:
   subst {abc[continue;[foobarspong}

The problem is that there is currently no pre-parsing stage
involved in Tcl_SubstObj (I thought about adding one, but
didn't) so scanning forward on a command substitution to
find the end of the command substitution (which would more
accurately be described as a script substitution, BTW) is
only done when needed.  Now, for each of the evaluation
return codes, the following is the behaviour:
  TCL_OK: insert string from interp
  TCL_ERROR: error!  :^)
  TCL_BREAK: return everything done up to the start of the
             command substitution
  TCL_CONTINUE: insert nothing and go from end of command
  TCL_RETURN: as with TCL_OK
The problem with TCL_CONTINUE and TCL_RETURN stems from the
fact that they can be issued from the middle of a script,
yet their behaviour really requires scanning to the end of
the script (TCL_OK is not a problem, since that only ever
comes from the end of the script.)  If the script being
evaluated is not well-formed, the code that does the
scanning forward (located somewhere inside the
implementation of Tcl_EvalEx) does not operate properly, and
I'm not sure how to change this (or even what the recovery
ought to be.)

As it is; the problem is a deep and nasty parsing problem.
The comment just documents the fact, and the problem isn't
exposed elsewhere because nowhere else in the core needs to
access parts of a string following an ill-formed command.

Suggested fix: compile [subst] strings so you can apply the
well-formedness checks first (and gain a performance boost
on objects that get fed into [susbst] several times too.)