How to get line number and filename locations for errors that occur while using interp eval?

72 views Asked by At

In case it is relevant, I am observing this behavior in Tcl 8.6.13.

Normally, errors in Tcl include line numbers and filenames when applicable.

However, I'm finding that when errors occur in scripts executed with interp eval, I do not get this information.

The two examples below are exactly the same, except that Example 1 evaluates the code in the main/parent interpreter, while Example 2 evaluates it in the child interpreter.

Example 1

#!/bin/sh    
# This line continues for Tcl, but is a single line for 'sh' \    
exec tclsh "$0" ${1+"$@"}    
    
::safe::interpCreate i    
eval expr {"a" + 1}

Output

./example.tcl 
invalid bareword "a"
in expression "a + 1";
should be "$a" or "{a}" or "a(...)" or ...
    (parsing expression "a + 1")
    invoked from within
"expr "a" + 1"
    ("eval" body line 1)
    invoked from within
"eval expr {"a" + 1}"
    (file "./example.tcl" line 6)

Example 2

#!/bin/sh    
# This line continues for Tcl, but is a single line for 'sh' \    
exec tclsh "$0" ${1+"$@"}    
    
::safe::interpCreate i    
i eval expr {"a" + 1}

Output

./example.tcl 
invalid bareword "a"
in expression "a + 1";
should be "$a" or "{a}" or "a(...)" or ...
    (parsing expression "a + 1")
    invoked from within
"expr "a" + 1"
    invoked from within
"i eval expr {"a" + 1}"
    (file "./example.tcl" line 6)

The error messages are nearly the same, except one line is missing in Example 2's output: ("eval" body line 1)

In this example, missing that part of the error message is not a problem, since there is only one line of code being evaluated; if it were a large script, or if the error occurred when source'ing a file, that might be a different story.

This behavior seems weird; partially because because it is inconsistent, but also because the child interpreter must know which code it is executing, so it should be able to report the line numbers of errors in that code; also, when sourceing a file, it should know the file it is reading the code from, since the source command was invoked from the child.

So is there any way to get line and file information when using interp eval? Alternatively, is there a way to write this code differently that could provide better error messages in scripts run in child interpreters?

Some additional examples (their output still misses the same line):

Example 3 (passing code to child interpreter as a single argument).

#!/bin/sh    
# This line continues for Tcl, but is a single line for 'sh' \    
exec tclsh "$0" ${1+"$@"}    
    
::safe::interpCreate i    
i eval {expr {"a" + 1}}

Output

./example.tcl 
can't use non-numeric string as operand of "+"
    invoked from within
"expr {"a" + 1}"
    invoked from within
"i eval {expr {"a" + 1}}"
    (file "./example.tcl" line 6)

Example 4 (passing code to child interpreter as a single argument in a list).

#!/bin/sh    
# This line continues for Tcl, but is a single line for 'sh' \    
exec tclsh "$0" ${1+"$@"}    
    
::safe::interpCreate i    
i eval [list expr {"a" + 1}]

Output

./example.tcl 
can't use non-numeric string as operand of "+"
    while executing
"expr {"a" + 1}"
    invoked from within
"i eval [list expr {"a" + 1}]"
    (file "./example.tcl" line 6)

Example 5 (passing code to child interpreter as a single argument built from lists).

#!/bin/sh    
# This line continues for Tcl, but is a single line for 'sh' \    
exec tclsh "$0" ${1+"$@"}    
    
::safe::interpCreate i    
i eval [list expr [list "a" + 1]]

Output

./example.tcl 
invalid bareword "a"
in expression "a + 1";
should be "$a" or "{a}" or "a(...)" or ...
    (parsing expression "a + 1")
    invoked from within
"expr {a + 1}"
    invoked from within
"i eval [list expr [list "a" + 1]]"
    (file "./example.tcl" line 6)

I also tried Examples 3, 4 and 5 with normal eval, i.e. in the main/parent interpreter. In those cases, they produced the same output as when run with the child interpreter, except they included the missing line.

0

There are 0 answers