Interesting behavior about condition statement in TCL - tcl

This might be a silly question. But I just found one confusing thing in TCL.
Below is an example code.
set x test_string
if {$x==y} {
puts "Matching!"
} else {
puts "Not matching."
}
if {$x==z} {
puts "Matching!"
} else {
puts "Not matching."
}
When I run this code, it ends up with below error.
invalid bareword "z"
in expression "$x==z";
should be "$z" or "{z}" or "z(...)" or ...
(parsing expression "$x==z")
invoked from within
"if {$x==z} {
puts "Matching!"
} else {
puts "Not matching."
}"
(file "test.tcl" line 3)
I'm aware that when we do comparison with string, the string should be quoted. So the following makes more sense.
if {$x=="y"} {
puts "Matching!"
} else {
puts "Not matching."
}
if {$x=="z"} {
puts "Matching!"
} else {
puts "Not matching."
}
But what makes me curious is, it seems the character y doesn't cause such error.
Could anybody please give some explanation about this? I cannot tell if there's anything special about this y here.
Thanks!

Because boolean values don't have to be quoted.
% expr true
true
% expr false
false
% expr not_a_boolean
invalid bareword "not_a_boolean"
...
See the Tcl_GetBoolean man page:
Tcl_GetBoolean expects src to specify a boolean value. If src is any of 0, false, no, or off, then Tcl_GetBoolean stores a zero value at *boolPtr. If src is any of 1, true, yes, or on, then 1 is stored at *boolPtr. Any of these values may be abbreviated, and upper-case spellings are also acceptable.
y is the unambiguous abbreviation of yes, a true value

Related

How to check if 3 or more variable contains same value in tcl

I wanted to know a good way to check if the variable values are same across three or more of the variables in tcl.
Suppose $a=abc
$b=abc
$c=abc
if ($a,$b,$c value is same)
puts"write something"
else
puts "not same"
The tcl::mathop::eq (or tcl::mathop::== for numeric comparison) command can do exactly that:
if {[tcl::mathop::eq $a $b $c]} {
puts "all equal"
} else {
puts "at least one is different"
}

How can I get the code line number along with errorinfo but prior to 8.5?

I am using the following TCL code:
proc RunCSM { scen } {
catch { $scen start }
if { "[$scen status]" != "SUCCESS" } {
puts "$scen FAILED. Error Info:"
puts "[$scen errorInfo]" ...
I need also the line number of the code that fails. In 8.5 and onwards this is achieved by this nice solution
How can I get the code line number along with errorinfo?
How is it possible to achieve the same but in version 8.4?
The easiest approach is to parse the errorInfo variable. Here's what an example looks like:
% parray foo
"foo" isn't an array
% set errorInfo
"foo" isn't an array
while executing
"error "\"$a\" isn't an array""
(procedure "parray" line 4)
invoked from within
"parray foo"
Parsing that with regexp isn't too hard, provided we use the -line option.
proc getLineFromErrorInfo {} {
global errorInfo
if {[regexp -line { line (\d+)\)$} $errorInfo -> line]} {
return $line
} else {
# No guarantee that there's information there...
return "unknown"
}
}
On our example from before, we can then do:
getLineFromErrorInfo
and it will return 4. You might want to extend the RE to also capture the name of the procedure; line numbers in 8.4 and before are always relative to their procedure. (This is also mostly true in 8.5 onwards; this is an area where backward compatibility is a bit painful IMO.) Here's how you might do that:
proc getLocusFromErrorInfo {} {
global errorInfo
if {[regexp -line {\(procedure "(.*?)" line (\d+)\)$} $errorInfo -> proc line]} {
return [list $proc $line]
} else {
# No guarantee that there's information there...
return "unknown"
}
}
Note that merely knowing where the error came from doesn't necessarily tell you where the problem is, especially in production code, since it could be due to bad arguments elsewhere that have been passed around a bit…

How to find multiple sub string patterns in a string in TCL

I am trying to find multiple string patterns in a string in TCL. I cannot get the correct and optimized way to do that.
I have tried some code and it is not working
I have to find -h ,-he,-hel ,-help in the string -help
set args "-help"
set res1 [string first "-h" $args]
set res2 [ string first -he $args]
set res3 [string first -hel $args]
set res4 [string first "-help" $args"]
if { $res1 == -1 || $res2 || $res3 || $res4 } {
puts "\n string not found"
} else {
puts "\n string found"
}
how to use regexp here I am not sure , so need some inputs.
The expected output is
This is a case where using regexp is easier. (Asking if a string is a prefix of -help is a separate problem.) The trick here is to use ? and (…) (or rather (?:…) which is the non-capturing version) in the RE and you must use the -- option because the RE begins with a -:
if {[regexp -- {-h(?:e(?:lp?)?)?} $string]} {
puts "Found the string"
} else {
puts "Did not find the string"
}
If you want to know what string you actually found, add in a variable to pick up the overall match:
if {[regexp -- {-h(?:e(?:lp?)?)?} $string matched]} {
puts "Found the string '$matched'"
} else {
puts "Did not find the string"
}
If you instead want the indices where it matched, you need an extra option:
if {[regexp -indices -- {-h(?:e(?:lp?)?)?} $string match]} {
puts "Found the string at $match"
} else {
puts "Did not find the string"
}
If you were instead interested in whether the string was a prefix of -help, you instead should do:
if {[string equal -length [string length $string] $string "-help"]} {
puts "Found the string"
} else {
puts "Did not find the string"
}
Many uses of this sort of thing are actually doing command line parsing. In that case, the tcl::prefix command is very useful. For example, tcl::prefix match finds the entry in a list of options that a string is a unique prefix of and generates an error message when things are ambiguous or simply don't match; the result can be switched on easily:
set MY_OPTIONS {
-help
-someOtherOpt
}
switch [tcl::prefix match $MY_OPTIONS $string] {
-help {
puts "I have -help"
}
-someOtherOpt {
puts "I have -someOtherOpt"
}
}

In TCL, what what does if { [a 0] } mean? 'a' is defined true

I am referring tcl code of my project, I don't understand what it means.
TCL Usage:
if {[Variable 0] } {
return 1
}
Please help!
The command:
if {[Variable 0] } {
return 1
}
is a conditional. It evaluates the expression:
[Variable 0]
and if that is a value considered to be true, will evaluate the (sub-)script:
return 1
The expression [Variable 0] calls the command Variable with the argument 0 and uses the result of the command as the result of the expression. (There's various values that can be true, but “non-zero if numeric, or the string literals true, on and yes” cover most of it.) The command Variable is not a standard Tcl command; what it does will depend on how it has been defined, but we can't help you with finding that.
The command return 1 causes the currently-executing procedure to stop executing and successfully return the value 1.

Is there a way to use wildcards in '==' test in tcl?

This might not be possible but is there a way to pass a regular expression in tcl.
I have this function that i cannot change which you pass in a string if finds something and compares them to see if they are equal.
proc check {a } {
// find b in the database
if {$a == $b} {
puts "equals"
} {
puts "Not equals"
}
}
The problem is that the function uses '=='. this only matches if they are exact, but i need to have wild cards in 'a' so that 'a' and 'b' are equal if 'a' and 'b' start with the same words.
I have this function that i cannot change
Why? In tcl, you could easily redefine it with
proc check {a } {
# find b in the database
if {[string match -nocase $a $b]} {
puts "equals"
} {
puts "Not equals"
}
}
Or you could redefine if, although not recommended.
You could even search and replace the if line at runtime with
proc check [info args check] [string map {
{if {$a == $b}} {if {[string match -nocase $a $b]}}
} [info body check]]
So: Why can't you change the function?
The behavior of the == operator is fixed; it always does an equality test. Indeed it does an equality test that prefers to do numeric equality and only falls back to string equality if it has no other way.
Therefore, to change the behavior of check you have to get really tricky.
I'd look at using execution traces to intercept something inside of check so that you can then put a read trace on the local a variable so that when you read it you get actually whether its value matches something according to complex rules. (b can probably just hold a 1 for boolean truth.) The code to do this is sufficiently mind-bendingly complex that I'd really try to avoid doing it!
Much easier, if you can, is to redefine proc so that you can put a prefix on the body of check so you can apply the trace there. Or even massage the test itself.
# Rename the command to something that nothing else knows about (tricky!)
rename proc my_real_proc
my_real_proc proc {name arguments body} {
# Replace a simple equality with a basic no-case pattern match
if {$name eq "check"} {
set body [string map {{$a == $b} {[string match -nocase $b $a]}} $body]
}
# Delegate to the original definition of [proc] for everything else
uplevel my_real_proc $name $arguments $body
}
So long as you run that code before the definition of check, you'll change the code (without “changing the code”) and get the sort of capabilities you want.