can't use non-numeric string as operand of "&" in TCL - tcl

if { ($name1 == "john") & ($name2 == "smith") } { puts "hello world" }
i got error:can't use non-numeric string as operand of "&"
I have try :
if { $name1 == "john" & $name2 == "smith" } { puts "hello world" }
if { {$name1 == "john"} & {$name2 == "smith"} } { puts "hello world" }
what i suppose to do?

The expr command in Tcl allows two forms of AND operations: bitwise (using the operator &) and logical (using the operator &&). The bitwise operator only allows integer operands: the logical operator can deal with both boolean and numeric (integer and floating point values; 0 or 0.0 means false in this case) operands. Use the logical AND operator unless you specifically want to work with bit patterns.
An expression like
$foo eq "abc" && $bar eq "def"
works because the eq operators evaluate to boolean values (BTW: prefer the new eq (equal) operator to == if you're making string equality comparisons, as it's more efficient), leaving && with two boolean operands.
The following code, however
{$foo eq "abc"} && {$bar eq "def"}
fails because the braces prevent substitution and forces the && to deal with two string operands. In this case, the && operator gives the error message
expected boolean value but got "$foo eq "abc""
and the & operator gives the message
can't use non-numeric string as operand of "&"
which was what you got.

Related

Interesting behavior about condition statement in 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

Numeric Value Comparison in Tcl

I want to know how to get a numeric value in TCL. What I mean is that, if the value is not numeric, result should fail else pass.
The below is what I have tried;
set trueKIND false
set trueKINDlist [list 1 2 3 4 5 6 7 8 9 10]
if {[lsearch -exact $trueKINDlist $Registrant(KIND)] >= 0} {
set trueKIND true
}
But what happens if the value of trueKINDlist > 10, this code certainly will fail...
Can somebody please tell me how I can write this in TCL? Or assist me with the operator to use in achieving this...
Thanks
Mattie
You can validate the string by [string is ...] procedure. For example:
set trueKIND [string is integer -strict $Registrant(KIND)]
Reference: https://www.tcl.tk/man/tcl8.6/TclCmd/string.htm#M10
You've got to think what kind of validation you want. For example, if you want to just validate that the value is an integer, any integer, do this:
if {![string is entier -strict $value]} {
error "$value is not an integer"
}
(There is also string is integer, but that uses a restricted 32-bit range for historical reasons, and string is wide uses a 64-bit range. For floating point numbers, use string is double. The -strict is required here; without it the empty string is also accepted; again, this is for historical reasons.)
When you have a particular range you want the value to be in, you use a compound condition:
if {![string is entier -strict $value] || !($value >= 0 && $value <= 10)} {
error "$value is not an integer in the range (0..10)"
}
If you are doing this a lot, use a procedure to make it clearer:
proc IntegerInRange {value lowerBound upperBound} {
expr {[string is entier -strict $value] && $value >= $lowerBound && $value <= $upperBound}
}
if {![IntegerInRange $value 0 10]} {
error "$value is not an integer in the range (0..10)"
}
if {![IntegerInRange $value2 3 25]} {
error "$value2 is not an integer in the range (3..25)"
}

What is the magic behind the TCL command expr with the operator eq

I would like to understand the TCL command expr for string comparison:
I tried the following:
expr {t eq t}
=> 1
expr {tru eq tr}
=> 0
expr {tru eq truee}
=> invalid bareword "truee" ...
expr {a eq a}
=> invalid bareword "a" ...
What is the magic behind the words t, tr, tru? Does Tcl special handling for these strings? I learnt that I had to quote the string if I use expr with eq, but I have some legacy programs which use this form of comparison. I would like to understand it. Thanks.
In Tcl, eq and ne does the string comparison while == does the numerical comparison.
% expr {1 == 1.0}
1
% expr {1 eq 1.0}
0
%
Whenever you are using the eq and if the input non-numeric, then it should be used with double quotes or with a variable reference. You can't use the literal bareword string notation.
For e.g
% expr {"a" eq "b"}; # Strings with double quotes
0
% set i a
a
% expr {$i eq "a"}; # String with variable reference
1
% expr {a eq b}; # Literally using the string as bareword
invalid bareword "a"
in expression "a eq b";
should be "$a" or "{a}" or "a(...)" or ...
%
There is a exception in this rule, where the Tcl boolean comes into play.
In Tcl, a proper boolean value is either a proper integer, with, like in C, zero meaning false and non-zero meaning true, or one of the following:
yes, true, on --> Boolean 1
no, false, off --> Boolean 0
When they are used partially, Tcl tends to match with any of the known items and evaluation happens accordingly.
If you evaluate expr with these special words, then it will return the same.
% expr {true}
true
% expr {false}
false
% expr {t}; # Tcl automatically matches internally and map it to 'true'
t
% expr {fa} ; # Similary, mapped to 'false'
fa
% expr {o} ; # Will throw error as it is conflicting with 'on' & 'off'
invalid bareword "o"
in expression "o";
should be "$o" or "{o}" or "o(...)" or ...
% expr {on}
on
% expr {of}; # Matching 'off' boolean
of
% if true {puts ya}
ya
% if n {puts ya}; # Matching boolean 'no'
% if f {puts ya}; # Matching boolean 'false'
% if false {puts ya}
% if yes {puts ya}
ya
% if y {puts ya}; # Matching boolean 'y'
ya
So, If your input is mapping with boolean, they are still valid, but treated as string only.
Now, lets go back to your original question.
% expr {t eq t}; # Tcl mapped the 'true' boolean and string-wise both are same. So, returned 1
1
% expr {tru eq tr}; # Both mapped 'true'. But, string-wise differs. So, returned 0
% expr {tru eq truee}; # 'tru' mapped to 'true' and 'truee' is unknown. So error
invalid bareword "truee"
% expr {a eq a}; # Both are used literally, so error throw.
invalid bareword "a"
Now, your question from the comments,
% expr {yes == true}; # String-wise, both are different. So, returned 0
0

Why does expr $a eq $b fail with "invalid bareword"?

I have TCL 8.6 and the following code works fine:
set a abc
set b abcd
if {$a eq $b} {puts hi}
But the following gets me error:
set a abc
set b abcd
expr $a eq $b
invalid bareword "abc"
in expression "abc eq abcd";
should be "$abc" or "{abc}" or "abc(...)" or ...
I wonder what is going on? Isn't that the condition expression in if command same as the expression in expr command?
No, it's not the same. There is a difference between what you see, and what expr sees, i.e. the string that it will try to evaluate. This is because every word of the command invocation (just like with every command invocation in Tcl) is subjected to substitution before the command is executed.
First case: the braces prevent the contents of the expression from being prematurely substituted. You see {$a eq $b}, expr sees $a eq $b. This is two operands ($a and $b) and an operator (eq): expr can work with this.
Second case: the three arguments are substituted before they are passed to expr. You see $a eq $b, expr sees abc eq abcd. That's two nonsense values and an operator, and expr can't deal with that.
If there is a string that isn't a boolean value or the name of an operator in an expr expression, it should be part of a variable substitution ($abc), or the name of a function (abc(...)), or part of a command substitution ([... abc ...]), or else be explicitly quoted ("abc" or {abc}).
Always brace the arguments to expr. Doing so prevents many problems, this one being one of the milder.
Documentation: expr

Why does expr "i==i" fail with "invalid bareword"?

1)
% expr "1==1"
1
2)
% expr "i==i"
invalid bareword "i"
in expression "i==i";
should be "$i" or "{i}" or "i(...)" or ...
Why am getting this error in step - 2
1) % if {"i" == "i"} {
puts "hai"
}
hai
2) % if {i == "i"} {
puts "hai"
}
invalid bareword "i"
in expression "i == "i"";
should be "$i" or "{i}" or "i(...)" or ...
if {"i" == "i"} This is wotking with if condition .
Here I found like expr command evaluating only integers , not comparing strings , But the In "if" condition everything (integer as well as string) are evaluating .
How Things are working here ?
The answer is in the expr man page.
Operands may be specified in any of the following ways:
...
[4] As a string enclosed in double-quotes. The expression parser
will perform backslash, variable, and command substitutions on
the information between the quotes, and use the resulting value
as the operand
[5] As a string enclosed in braces. The characters between the open
brace and matching close brace will be used as the operand with‐
out any substitutions.
...
So, expr can compare strings, but you must enclose them in double quotes or curly braces, depending on whether you want substituions performed or not.
Therefore, in your example 2, you must use
% expr {"i" == "i"}
or
% expr {{i} == {i}}
Better to user the string comparison operands:
% expr {"i" eq "i"}
% expr {{i} eq {i}}
to be sure that the content of the string is not converted to numerical values.
In Tcl 8.4
you can use
%expr {"i" == "i"}
or
%expr ( "i" == "i" )
Both syntaxes will work.