Re: retrieving line number in case of error - "simple debugger"



Bryan Oakley napisał(a):
set lineno 0
foreach line [split $script \n] {
    if {![info exists command]} {
        set command $line
    } else {
        append command \n $line
    }
    incr lineno
    if {[info complete $command]} {
        if {[catch $command err]} {
            puts "error on line $lineno: $err"
        }
        unset command
    }
}

That one's actually tricky.

(zoro) 55 % info complete "puts \\"
1
(zoro) 56 % eval "puts \\
x"
x

So this would not get parsed correctly. The other tricky thing is:

(zoro) 57 % eval "puts \\\\
x"

Since it's the opposite. The moral of the story is that \\ at the end of a line are a PITA in this case. I think it is much safer to do info complete and check for an even number of \ at the end (0 is also even for me :-). But there's probably still some other issue (or issues) that do not make it work... Anyway, here's "info complete on steroids" :-)

proc checkIfReallyComplete {line} {
    if {![info complete $line]} {
        return false
    }
    set count 0
    while {[string index $line end-$count] == "\\"} {
        incr count
    }
    if {$count % 2} {
        return false
    }  else  {
        return true
    }
}

So, perhaps the answer to your question is "info complete" and "catch". That is, you can use [info complete] to know if a line or lines of user-entered text is a complete command, and "catch" to execute the command and to retrieve the result.

I no longer believe in [info complete]. Well, it would be great if Tcl would have [info complete -strict] or something, that would actually check what the interpreter would do. For now, I guess most people could live with the equivalent above.


But, assuming he would have a proc called checkIfReallyComplete (or something), then it would be much better to:

set realcode ""
set script [.text get 1.0 end-1c]
set lineno 0
foreach line [split $script \n] {
    if {![info exists command]} {
        set command $line
    } else {
        append command \n $line
    }
    incr lineno
    if {[checkIfReallyComplete $command]} {
        append realcode [list set ::lineNumber $lineno] \n $command \n
    }
}
if {[catch $realcode]} {
    puts "Error in $::lineNumber $::errorInfo"
}

And even better, this would allow storing the "debugable" version of the script.

Then we would probably come up with a problem with control statements like for/foreach/while/if and would have to emulate those (preferably at "parser" level). Well then we will have custom control statements... (ie expect command from expect or tcom::foreach). I think this is a messy situation. Well, not to mention considering namespaces while parsing (ie :

namespace eval tcom {
    foreach ....
}

is not quite obvious. Especially with the following "crude" code:

set ns tcom
namespace eval $ns {
    foreach ....
}

Then there's metaprogramming ;-)

I wonder if an universal parser could be produced this way - one that would return return something similar to command tree so that everyone could debug it the way they want to (GUI, logging, whatever).

Just my 2 cents in this matter. If anybody wants to pursue this, I would love to see the results. I wanted to, but got scared by the problems above. Anyway, for a plain Tcl script, those problems do not really exist :-) So just reparsing using info complete + for/foreach/while/if would easily debug 90% of the scripts.

--
WK
.



Relevant Pages