Q: How to split a string into elements exactly as eval would do

129 views
Skip to first unread message

Harald Kirsch

unread,
Sep 7, 1998, 3:00:00 AM9/7/98
to

Is there a way to split a string into parts the same way as eval would
do?

Example:

set cmd "set a 1; set b {hello bello}; puts gaga"
eval $cmd

Here, eval splits the list at the semicolons and then evaluates one
command after another. If $cmd would contain newlines, those would also
be points to split for eval.

Now suppose I want better control over the eval. In may particular case,
I want to add an additional parameter to each of the commands,
e.g. something similar to

foreach x [commandsplit $cmd] {
puts "running `$x' with additional arg blabliblu"
eval "$cmd blabliblu"
}

Is there a thing like `commandsplit' available?

Harald Kirsch

P.S.: [split $cmd ";\n"] does not work, of course.
--
-------------------------------------------------+------------------
Harald Kirsch, k...@iitb.fhg.de, +49 721 6091 369 | Now I rebooted.
FhG/IITB, Fraunhoferstr.1, 76131 Karlsruhe | --- Jerry Pournelle

Paul Duffin

unread,
Sep 7, 1998, 3:00:00 AM9/7/98
to Harald Kirsch

It is quite complicated but [info complete] should be a big help.

--
Paul Duffin
DT/6000 Development Email: pdu...@hursley.ibm.com
IBM UK Laboratories Ltd., Hursley Park nr. Winchester
Internal: 7-246880 International: +44 1962-816880

Alexandre Ferrieux

unread,
Sep 7, 1998, 3:00:00 AM9/7/98
to
Paul Duffin wrote:
>
> Harald Kirsch wrote:
> >
> > Is there a way to split a string into parts the same way as eval would
> > do?
> > P.S.: [split $cmd ";\n"] does not work, of course.
>
> It is quite complicated but [info complete] should be a big help.

Complicated, yes.
This might be a hint at an interesting feature for future Tcl:
bidirectional trace.
By this, I mean the equivalent at Tcl command level of {/proc,ptrace()}
(in strace/truss/trace) at system call level.
Of course, there is $::tcl_traceExec, but it is "read-only" in the sense
that executed instructions are just written to stdout (and not to any
other stream, to my great disappointment, Grrr...). What would be
useful, is to add a backwards path, so that the 'tracer' explicitly
allows the traced interp to go on, just like /proc and ptrace(). With
this, step-by-step debuggers *without* instrumentation would be
possible, which is a great step towards debugger transparency.

Reactions ?

-Alex

Harald Kirsch

unread,
Sep 7, 1998, 3:00:00 AM9/7/98
to
In article <35F3F3...@cnet.francetelecom.fr> Alexandre Ferrieux <alexandre...@cnet.francetelecom.fr> writes:
> > Harald Kirsch wrote:
> > >
> > > Is there a way to split a string into parts the same way as eval would
> > > do?
> > > P.S.: [split $cmd ";\n"] does not work, of course.

> With


> this, step-by-step debuggers *without* instrumentation would be
> possible, which is a great step towards debugger transparency.

Yes, I recognized that this feature would easily allow for running
a tcl-script step-by-step when I prepared my message and expected
pointers Tcl-Pro or this other debugger (Tuba ?) annonced recently.

Nevertheless I wanted it for something different. And I was really
irritated to find out that it is not at all simple to do.

Harald Kirsch
--
---------------------+------------------+--------------------------
Harald Kirsch (@home)| | Now I rebooted.
k...@iitb.fhg.de | | --- Jerry Pournelle, BYTE
gegen Punktfilitis hilft nur `chmod u-w ~'

Gordon Johnstone

unread,
Sep 8, 1998, 3:00:00 AM9/8/98
to
Harald Kirsch wrote:

[cut]

> foreach x [commandsplit $cmd] {
> puts "running `$x' with additional arg blabliblu"
> eval "$cmd blabliblu"
> }
>
> Is there a thing like `commandsplit' available?
>
> Harald Kirsch
>

> P.S.: [split $cmd ";\n"] does not work, of course.

> --
> -------------------------------------------------+------------------
> Harald Kirsch, k...@iitb.fhg.de, +49 721 6091 369 | Now I rebooted.
> FhG/IITB, Fraunhoferstr.1, 76131 Karlsruhe | --- Jerry Pournelle


What about

proc commandsplit {mylist} {
regsub -all -- {;} $mylist \n foo
return [split $foo \n]
}

GordonJ


Simon King

unread,
Sep 8, 1998, 3:00:00 AM9/8/98
to
This is inefficient and messy, but I think it should do what you want.
Have fun...

Simon


proc commandsplit {CommandList} {
set PossibleCommand {}
set OutputList {}
set RealOutputList {}
while 1 {
set Rc [regexp -indices -- "\[;\n\]" $CommandList Indices]
if {$Rc} {
set Index [lindex $Indices 0]
set PossibleCommand \
"$PossibleCommand[string range $CommandList 0 $Index]"
set CommandList [string range $CommandList [expr $Index + 1] end]
if {[info complete $PossibleCommand]} {
lappend OutputList [string trim $PossibleCommand " \t\n;"]
set PossibleCommand {}
}
} else {
set CommandList "$PossibleCommand$CommandList"
if {[info complete $CommandList]} {
lappend OutputList [string trim $CommandList " \t\n;"]
break
} else {
error "Not a well formed command"
}
}
}

foreach command $OutputList {
if {$command != {}} {
lappend RealOutputList $command
}
}

return $RealOutputList
}


Harald Kirsch wrote:
>
> Is there a way to split a string into parts the same way as eval would
> do?
>

> Example:
>
> set cmd "set a 1; set b {hello bello}; puts gaga"
> eval $cmd
>
> Here, eval splits the list at the semicolons and then evaluates one
> command after another. If $cmd would contain newlines, those would also
> be points to split for eval.
>
> Now suppose I want better control over the eval. In may particular case,
> I want to add an additional parameter to each of the commands,
> e.g. something similar to
>

Donal K. Fellows

unread,
Sep 8, 1998, 3:00:00 AM9/8/98
to
In article <KIR.98Se...@neptun.iitb.fhg.de>,

Harald Kirsch <k...@iitb.fhg.de> wrote:
> Is there a way to split a string into parts the same way as eval would
> do?

Tricky. What follows is a first hack at it. It returns a list of
commands, but it does not try to trim any trailing spaces from the
commands (not exactly trivial that...)

proc cmdsplit {script} {
set cmds {}
set cmd {}
for {set i 0} {$i<[string length $script]} {incr i} {
set c [string index $script $i]
switch -exact -- $c {
"\n" - ";" {
if {[info complete $cmd]} {
# The next line is not strictly necessary...
if {[string length $cmd]} {
lappend cmds [string trimleft $cmd]
}
set cmd {}
} else {
append cmd $c
}
}
default {
append cmd $c
}
}
}
if {[string length $cmd]} {
if {[info complete $cmd]} {
lappend cmds [string trimleft $cmd]
} else {
return -code error "Not a well-formatted Tcl script"
}
}
return $cmds
}

Donal.
--
Donal K. Fellows http://www.cs.man.ac.uk/~fellowsd/ fell...@cs.man.ac.uk
Department of Computer Science, University of Manchester, U.K. +44-161-275-6137
--
If you staple a penguin to the head of a gnu, the result won't catch herring...

Harald Kirsch

unread,
Sep 8, 1998, 3:00:00 AM9/8/98
to
In article <35F4F549...@scs.dera.gov.uk> Gordon Johnstone <g_d_jo...@scs.dera.gov.uk> writes:
> Harald Kirsch wrote:
>
> [cut]

>
> > foreach x [commandsplit $cmd] {
> > puts "running `$x' with additional arg blabliblu"
> > eval "$cmd blabliblu"
> > }
> >
> > Is there a thing like `commandsplit' available?
> >
> > Harald Kirsch
> >
> > P.S.: [split $cmd ";\n"] does not work, of course.
>
> What about
>
> proc commandsplit {mylist} {
> regsub -all -- {;} $mylist \n foo
> return [split $foo \n]
> }
>

Autsch!
What will this return for
set cmd "puts {;;;;;;;}; puts {;;}"

Harald Kirsch

unread,
Sep 9, 1998, 3:00:00 AM9/9/98
to
In article <35F4FE4F...@europem01.nt.com> Simon King <sk...@europem01.nt.com> writes:

[script deleted]

> Harald Kirsch wrote:
> >
> > Is there a way to split a string into parts the same way as eval would
> > do?

Thank you for your solution. It survived at least the following
quick&dirty test:

------------------------------------------------------------
set cmd {
puts "\{"
puts hallo
puts "hallo;ballo"
set a \
bbb
set a "adfadfasdf
asdfadsf"
set b "adfadfadf \
adfadfasdf"
puts {
eins zwei ;
drei
}
}
set res [$cmdsplit $cmd]
foreach cmd $res {
puts ">>$cmd<<"
}

puts [time {$cmdsplit $cmd} 100]
------------------------------------------------------------

The time was 1560 microseconds per iteration which is about 4 times
faster than Mr. Fellows entry :-)

Harald Kirsch

unread,
Sep 9, 1998, 3:00:00 AM9/9/98
to
In article <6t30ge$l5m$1...@m1.cs.man.ac.uk> fell...@cs.man.ac.uk (Donal K. Fellows) writes:

> In article <KIR.98Se...@neptun.iitb.fhg.de>,


> Harald Kirsch <k...@iitb.fhg.de> wrote:
> > Is there a way to split a string into parts the same way as eval would
> > do?
>

Thank you for your solution. It survived the following quick&dirty test:

------------------------------------------------------------
set cmd {
puts "\{"
puts hallo
puts "hallo;ballo"
set a \
bbb
set a "adfadfasdf
asdfadsf"
set b "adfadfadf \
adfadfasdf"
puts {
eins zwei ;
drei
}
}
set res [$cmdsplit $cmd]
foreach cmd $res {
puts ">>$cmd<<"
}

puts [time {$cmdsplit $cmd} 100]
------------------------------------------------------------

However I am sorry to say that it is about four times slower than
Mr. King's solution ;-)

Thanks again to both of you. Although in the meantime thought I came up
with a solution which does not need a `cmdsplit', I might now consider
this approach again, because the solution I have involves quite some
trickery which I might not understand again in a month.

Simon King

unread,
Sep 9, 1998, 3:00:00 AM9/9/98
to
I've just noticed that the my solution won't cope with semi-colons in comments,
so if comments may become part of the command list, you'll have to do some sort
of extra check. It might get quite messy...

Simon

.
.
.

Donald G Porter

unread,
Sep 9, 1998, 3:00:00 AM9/9/98
to
In article <KIR.98Se...@neptun.iitb.fhg.de>,
Harald Kirsch <k...@iitb.fhg.de> wrote:
> Is there a way to split a string into parts the same way as eval would
> do?

Here's a routine I wrote to support parsing of class bodies in an
itcl-like, pure Tcl, OO framework into Tcl commands. I've modified
the interface to match yours. It handles the "semicolon in a comment"
problem.

proc cmdSplit {body} {
set commands {}
set chunk ""
foreach line [split $body "\n"] {
append chunk $line
if {[info complete "$chunk\n"]} {
# $chunk ends in a complete Tcl command, and none of the
# newlines within it end a complete Tcl command. If there
# are multiple Tcl commands in $chunk, they must be
# separated by semi-colons.
set cmd ""
foreach part [split $chunk ";"] {
append cmd $part
if {[info complete "$cmd\n"]} {
set cmd [string trimleft $cmd]
# Drop empty commands and comments
if {![string match {} $cmd] \
&& ![string match #* $cmd]} {
lappend commands $cmd
}
if {[string match #* $cmd]} {
set cmd "#;"
} else {
set cmd ""
}
} else {
# No complete command yet.
# Replace semicolon and continue
append cmd ";"
}
}
set chunk ""
} else {
# No end of command yet. Put the newline back and continue
append chunk "\n"
}
}
if {![string match {} [string trimright $chunk]]} {
return -code error "Can't parse body into a\
sequence of commands.\n\tIncomplete\
command:\n-----\n$chunk\n-----"
}
return $commands
}

--
| Don Porter, D.Sc. Mathematical and Computational Sciences Division |
| donald...@nist.gov Information Technology Laboratory |
| http://math.nist.gov/mcsd/Staff/DPorter/ NIST |
|______________________________________________________________________|

Reply all
Reply to author
Forward
0 new messages