tcl catch: different -code and -level in caught result options when return code is TCL_RETURN - return

Please take a look at the following code snippet:
set catch_r [catch {
return -code return -level 0 "something"
#return -code break -level 1 "something"
} result options]
puts "catch result: \n$catch_r"
puts result:
puts $result
puts options:
puts $options
the output is
catch result:
2
result:
something
options:
-code 0 -level 1
If I use the commented line return -code break -level 1 "something", the output is
catch result:
2
result:
something
options:
-code 3 -level 1
I tried more return -code -level, such as -code continue or -code error with different -level numbers, and found that values of -code and -level in options are the same as return -code -level if it is NOT return -code return.
If it's return -code return -level N, options will be -code 0 -level N+1, not -code 2 -level N. Why return -code return is treated this way by catch? What's the meaning of it?
Thanks

Every kind of return -code X gives TCL_RETURN until it has gone through as many stack levels as the value of the -level options. Then it gives the TCL_ code that corresponds to its -code value. For -code return that is TCL_RETURN, which means it will go through one more stack level and give TCL_OK. As you write, return -code return -level N is the same as return -code ok -level N+11. That is by design.
Documentation:
return
1 pseudocode: the command will not actually do level arithmetic.

Related

How to use Jenkins local defined variable in IF statement?

I have below code
def result = readFile('res.txt')
echo "${result}"
if ("${result}" > 5 )
{
echo "Yes"
} else {
echo "No"
}
}
It is printing NO but the answer in ${result} -> 11 which means it should print Yes. Can u pls help.
As noted in comments - you should cast the result to integer. Both result.toInteger() and Integer.parseInt(result) will work, although the first one is more straightforward and doesn't complain about extra white space (e.g. trailing endline character).
Besides, you don't need to mess with strange constructs like "${result}" because result is a normal variable, so your code may look like this:
def result = readFile('res.txt')
echo "${result}"
if (result.toInteger() > 5 ) {
echo "Yes"
} else {
echo "No"
}

How works return -code $value from the proc in TCL

I must use a proc Z, which returns something like "return -code 2" $something , but I can't understand how it's works.
proc X {} {
puts "X - in"
Y
puts "X - out"
}
proc Y {} {
puts "Y - in"
Z
puts "Y - out"
}
proc Z {} {
puts "Z - in"
return -code 2 "some value"
puts "Z - out"
}
X
Output is :
X - in
Y - in
Z - in
X - out
I can't change return in proc Z, but I also want that it continues his work in proc Y and puts "Y-out". Can i do it ?
The return command conceptually raises an exception (of type return, which corresponds to the TCL_RETURN result code in the Tcl C API) that is trapped by the procedure (strictly, by the procedure hull that does things like callframe management and argument marshalling) in which it runs to make that do something. In the case of a normal return, it simply makes the procedure return that value, but when the -code option is provided, it can direct the procedure to do something else; this is done after the callframe has been popped from the Tcl call stack.
Because 2 is the actual value of the TCL_RETURN result code, it means that it is telling the procedure hull (i.e., the overall Z procedure) to behave like it was a simple call to return, which is why puts "Y - out" isn't being executed; there's a return exception being processed as a result of the execution of Z. (FWIW, return -code 2 is pretty rare. return -code 1/return -code error is much more common, as it is a way of getting an error exception to be thrown, which is useful because stack traces are only built by error exceptions.)
You can override this processing using catch or try. It's a bit easier to get this right with try, since catch is a fairly broad-brush primitive. Try this:
proc Y {} {
puts "Y - in"
try {
Z
} on return {msg} {
puts "Z told us to return '$msg'"
}
puts "Y - out"
}
If you're just wanting to make a short piece of code work after the call but otherwise not interfere, try…finally is the right way:
proc Y {} {
puts "Y - in"
try {
Z
} finally {
puts "Y - out"
}
}
If you're using Tcl 8.5 (or before), you won't have try. Here's a version with catch:
proc Y {} {
puts "Y - in"
set code [catch {
Z
} msg]
if {$code == 2} {
puts "Z told us to return '$msg'"
}
puts "Y - out"
}
The problem with catch is that it is very easy to lose errors by accident.

break more than 1 level of foreach nesting in tcl

Is it possible to exit in 1 command, from two levels of nesting? That is, let us say I have this code:
foreach l { 1 2 3 4 } {
foreach k { 3 4 5 6 } {
if { $k > 4 } {
break2
} else {
puts "$k $l"
}
}
What that I would like to see output is:
1 3
1 4
The question is, how can one code the break2 (if possible)?.I do not know of such "feature" in any language, other than wrapping this in a proc, and using return to stop the proc, which is more of a hack than proper language constructThanks.
It's not possible to do it directly; the break machinery doesn't have anything to trace up to anything other than the nearest looping context.
The easiest way of handling this is to use try in 8.6 and a custom exception code (i.e., any value from 5 upwards).
foreach a {b c} {
puts "a=$a; to show that this is not stopping the outermost loop"
try {
foreach l { 1 2 3 4 } {
foreach k { 3 4 5 6 } {
if { $k > 4 } {
# Generating a custom exception code is a bit messy
return -level 0 -code 5
}
puts "$k $l"
}
}
} on 5 {} {
# Do nothing here; we've broken out
}
}
Running that gives this output:
a=b; to show that this is not stopping the outermost loop
3 1
4 1
a=c; to show that this is not stopping the outermost loop
3 1
4 1
But it's pretty messy to do this; the best approach is typically to refactor your code so that you can just return ordinarily to end the loop. Using apply might make this easier:
foreach a {b c} {
puts "a=$a; to show that this is not stopping the outermost loop"
apply {{} {
foreach l { 1 2 3 4 } {
foreach k { 3 4 5 6 } {
if { $k > 4 } {
return
}
puts "$k $l"
}
}
}}
}
The downside of using apply is that it is a different variable context and has quite a bit more overhead (because of all the stack frame management). Still, the variable context thing can be worked around using upvar if you're careful.
It's possible in Tcl ≥ 8.5:
foreach l { 1 2 3 4 } {
foreach k { 3 4 5 6 } {
if {$k > 4} {
return -code break -level 2
} else {
puts "$k $l"
}
}
}
That return -code break -level 2 works like "make the enclosing command two levels up the stack return as if it has called break".
The return command manual page.

Trouble using catch in simple tcl proc

I have the following proc which basically looks upp a couple of values in a dictonary and returns them as a list.
proc GetAllow { PID Ply } {
# read a dictonary from a file
catch {
append PlyAndDirXt $Ply "_xt"
append PlyAndDirYt $Ply "_yt"
set x_allow_tens [ dict get $allowables $PID $PlyAndDirXt ]
set y_allow_tens [ dict get $allowables $PID $PlyAndDirYt ]
set allowables [ list $x_allow_tens $y_allow_tens ]
} res
if { $res == 0 } {
return $allowables
}
if { $res != 0 } {
return 999
}
}
As I understand "catch" if everything is ok $res should be 0 = TCL_OK. In that case I would like the proc to return the list $allowables.
In case the values are not found in the dict due to none matching keys. I would like it to return 999. But I always get 999 back. What am I'm doing wrong here ?
As per the manual:
If script raises an error, catch will return a non-zero integer value corresponding to the exceptional return code returned by evaluation of script. Tcl defines the normal return code from script evaluation to be zero (0), or TCL_OK.
If the varName argument is given, then the variable it names is set to the result of the script evaluation. When the return code from the script is 1 (TCL_ERROR), the value stored in varName is an error message. When the return code from the script is 0 (TCL_OK), the value stored in resultVarName is the value returned from script.
As such, $res will not be equal to 0 unless the result of your script returns 0.
You can set catch to a variable like this:
set err [catch {
append PlyAndDirXt $Ply "_xt"
append PlyAndDirYt $Ply "_yt"
set x_allow_tens [ dict get $allowables $PID $PlyAndDirXt ]
set y_allow_tens [ dict get $allowables $PID $PlyAndDirYt ]
set allowables [ list $x_allow_tens $y_allow_tens ]
} res]
Then check
if { $err == 0 } {
return $allowables ;# Or return $res since that's the last evaluated line
}
if { $err != 0 } {
return 999
}

Behavior of load() when chunk function returns nil

From the Lua 5.1 documentation for load():
Loads a chunk using function func to get its pieces. Each call to func must return a string that concatenates with previous results. A return of an empty string, nil, or no value signals the end of the chunk.
From my testing, this is not actually true. Or, rather, the documentation is at a minimum misleading.
Consider this example script:
function make_loader(return_at)
local x = 0
return function()
x = x + 1
if x == return_at then return 'return true' end
return nil
end
end
x = 0
repeat
x = x + 1
until not load(make_loader(x))()
print(x)
The output is the number of successive calls to the function returned by make_loader() that returned nil before load() gives up and returns a function returning nothing.
One would expect the output here to be "1" if the documentation is to be taken at face value. However, the output is "3". This implies that the argument to load() is called until it returns nil three times before load() gives up.
On the other hand, if the chunk function returns a string immediately and then nil on subsequent calls, it only takes one nil to stop loading:
function make_loader()
local x = 0
return {
fn=function()
x = x + 1
if x == 1 then return 'return true' end
return nil
end,
get_x=function() return x end
}
end
loader = make_loader()
load(loader.fn)
print(loader.get_x())
This prints "2" as I would expect.
So my question is: is the documentation wrong? Is this behavior desirable for some reason? Is this simply a bug in load()? (It seems to appear intentional, but I cannot find any documentation explaining why.)
This is a bug in 5.1. It has been corrected in 5.2, but we failed to incorporate the correction in 5.1.
I get slightly different results from yours, but they are still not quite what the documentation implies:
function make_loader(return_at)
local x = 0
return function()
x = x + 1
print("make_loader", return_at, x)
if x == return_at then return 'return true' end
return nil
end
end
for i = 1, 4 do
load(make_loader(i))
end
This returns the following results:
make_loader 1 1
make_loader 1 2
make_loader 2 1
make_loader 2 2
make_loader 2 3
make_loader 3 1
make_loader 3 2
make_loader 4 1
make_loader 4 2
For 1 it's called two times because the first one was return true and the second one nil. For 2 it's called three times because the first one was nil, then return true, and then nil again. For all other values it's called two times: it seems like the very first nil is ignored and the function is called at least once more.
If that's indeed the case, the documentation needs to reflect that. I looked at the source code, but didn't see anything that could explain why the first returned nil is ignored (I also tested with empty string and no value with the same result).

Resources