set a 55
set b aa
puts [expr 0x$a + 0x$b]
puts [expr {0x$a + 0x$b}]
the first expr command line works as I expect, but the second expr
command gives an error, which I didn't expect:
syntax error in expression "0x$a + 0x$b": extra tokens at end of
expression
Why does the second expr give an error?
Thanks,
Paul.
Ah! Thank you for the explanation.
Paul.
I don't think this is quite right.
(G) 2 % set a 1
1
(G) 3 % puts b$a
b1
"If a word contains a dollar-sign (�$�) followed by one of the forms
described below, then Tcl performs variable substitution...."
"beginning of an operand" != "beginning of a word"
But you're correct that the Harald Oehlman explanation isn't technically
correct in every detail. The issue is that parsing of the expression
as an expression takes place before any variable substitution *by expr*.
[expr {0x$a}] -> syntax error
[expr {"0x$a"}] -> success
--
| Don Porter Mathematical and Computational Sciences Division |
| donald...@nist.gov Information Technology Laboratory |
| http://math.nist.gov/~DPorter/ NIST |
|______________________________________________________________________|
> [expr {"0x$a"}] -> success
I admit the numeric result of this surprised me!
I thought the quotes, in expr, caused it to succeed
but merely spit back the string, as with
expr {"123+456"}
This makes me rething using expr to select strings,
as with
expr {1>2 ? "foo":"bar"}
in case one of the strings might be read as a number.
--
Donald Arseneau as...@triumf.ca
Hi Don, Hi Donald,
The fact, that the two given examples work, is new for me, thank you.
Donald, under which circumstance does this actually work:
expr { BoolExpr ? string : string }
I have tested this years ago (because I like this compact way) and I
had issues with it.
For me, another expr magic question is: when do "==" work for strings?
Thank you,
Harald
% proc tcl::mathfunc::foo x { puts "foo:>$x<"; return $x }
%
% puts "expr<[expr {foo( 0x42 )}]>"
foo:>0x42<
expr<66>
%
% puts "expr<[expr {foo(" 0x42 ")}]>"
foo:> 0x42 <
expr<66>
%
% puts "expr<[expr {foo(foo(" 0x42 "))}]>"
foo:> 0x42 <
foo:> 0x42 <
expr<66>
%
But then:
% puts "expr<[expr {foo(true?" 0x42 ":"hello, world")}]>"
foo:>66<
expr<66>
%
It seems, like the ?-operator attempts numeric conversion on its own.
But even, if "?" didn't, expr would still attempt one at its end.
Also, hex-numbers aren't yet converted on parsing, but only later.
The "?" operator does nothing special, it's only expr that has an
(over?)eager convert-to-numeric strategy built in :}
% proc f x {expr {"0x$x"}}
% ::tcl::unsupported::disassemble proc f
[...]
(0) push1 0 # "0x"
(2) loadScalar1 %v0 # var "x"
(4) concat1 2
(6) tryCvtToNumeric
(7) done
As you can see above, there's no trace of the initial quotes (that we
humans tend to interpret as a type hint) in the data path,
I admit that this final tryCvtToNumeric is questionable, but I'm
afraid that 18 yrs of backward compatibility imply a lock-in :(
Bottom line: [expr] is primarily for math, and even if the ability to
manipulate strings and call arbitrary commands let you think it could
do otherwise, it will insist on morphing its _output_ into a number.
When possible: [expr {"08"}]->08 / [expr {"07"}]->7. Yes: Die, Octal,
Die !!!
-Alex
I thought it was clear from my examples, that for the ?-operator even
the interims-result had been converted before being passed on to foo().
Most (if not all) other operations and functions return numbers, anyway,
so that's how the ?-op really sticks out from the others.
That's just because the contract of a mathop is to take a number as
input.
If you nest two levels of "?" and look at the byte code, you'll see
that there's only one tryCvtToNumeric at the end of expr (or on next
entry to a mathop, builtin or not):
% dis script {expr {$x?($y?"$z":"08"):0}}
Command 1: "expr {$x?($y?\"$z\":\"08\"):0}"
(0) push1 0 # "x"
(2) loadScalarStk
(3) jumpFalse1 +16 # pc 19
(5) push1 1 # "y"
(7) loadScalarStk
(8) jumpFalse1 +7 # pc 15
(10) push1 2 # "z"
(12) loadScalarStk
(13) jump1 +4 # pc 17
(15) push1 3 # "08"
(17) jump1 +4 # pc 21
(19) push1 4 # "0"
(21) tryCvtToNumeric
(22) done
> Most (if not all) other operations and functions return numbers, anyway,
> so that's how the ?-op really sticks out from the others.
Yes, it sticks out in _not_ forcing numeric conversion. But like
Shrodinger's cat's fate, nothing of these remarkable deeds can ever
make it to the outside world, so that hardly matters ;-)
-Alex
Personally I keep wondering why anyone uses Tcl to crunch numbers. The
main reason to avoid this stupid trap is the number of "use cases"
required to test any expression. Until we provide an [expr] testing
suite which can probe for potential problems with every expression,
everyone should expect unexpected results and errors when using
[expr].
In case anyone thinks I'm dissing Tcl, I should add that most real-
world calculations don't work well with typical programming languages.
Most real-world applications deal with base 10, but programming
languages handle floating point calculations in base 2. Since there is
no one-to-one mapping between these two bases, simple things like
adding tax to a purchase represented as a decimal number fail often
enough to guarantee failure eight days a week. If simple stuff fails,
why trust anything more complicated?
Use decimal math for decimal calculations and carefully handle other
bases. But [expr] isn't designed to "do math", otherwise you have to
explain a number of operators which have nothing to do with math.
[expr] is really designed as a distinct set of parsing rules. To
understand [expr], first dispense of any prior ideas you hold due to a
background in mathematics. They don't help.
> On 27 Okt., 02:52, Donald Arseneau <a...@triumf.ca> wrote:
> > Don Porter <d...@nist.gov> writes:
> > > [expr {"0x$a"}] -> success
> > expr {1>2 ? "foo":"bar"}
> > in case one of the strings might be read as a number.
>
> Hi Don, Hi Donald,
>
> The fact, that the two given examples work, is new for me, thank you.
> Donald, under which circumstance does this actually work:
> expr { BoolExpr ? string : string }
> I have tested this years ago (because I like this compact way) and I
> had issues with it.
It works when both strings are either quoted (with "" or {}) or are
variable references, and the strings can't be interpreted as numbers.
The same restrictions that apply to
if { string == string }
--
Donald Arseneau as...@triumf.ca
Tom, you are soo correct...and that's why there is COBOL!
All the original operations and functions (other than "?:") *naturally*
return only numbers in the first place. "?:" can return also non-numbers,
but does an extra *attempt* to make numbers from what wasn't before.
If "?:" does its conversion just to "protect" those internal mathfuncs
from seeing stuff like literal "0x1", then it overzealously does it also
for script-defined mathfuncs, which are otherwise not thusly protected.
Again, I'm saying that ?: does no conversion by itself. The
tryConvertToNumeric instruction is generated only at two places: (1)
entry to a built-in math op, and (2) (unfortunalely) final return from
[expr].
As a proof:
% dis script {expr {f($x?"0x41":"0x42")}}
ByteCode 0x0x835fbc8, refCt 1, epoch 4, interp 0x0x82c1e00 (epoch 4)
Source "expr {f($x?\"0x41\":\"0x42\")}"
Cmds 1, src 26, inst 17, litObjs 4, aux 0, stkDepth 2, code/src 0.00
Commands 1:
1: pc 0-15, src 0-25
Command 1: "expr {f($x?\"0x41\":\"0x42\")}"
(0) push1 0 # "tcl::mathfunc::f"
(2) push1 1 # "x"
(4) loadScalarStk
(5) jumpFalse1 +6 # pc 11
(7) push1 2 # "0x41"
(9) jump1 +4 # pc 13
(11) push1 3 # "0x42"
(13) invokeStk1 2
(15) tryCvtToNumeric
(16) done
As you can see, the user-provided func c gets the unconverted result
from ?:, here a string.
-Alex
Mystery solved!
Try this:
% dis script {expr {f(1?"0x41":"0x42")}}
(i.e. with a constant instead of the $x)
Seems like a bug, even if just for a non-typical situation.
% expr {foo(1?" 0x42 ":" 0x41" )}
foo:>>>66<<<
66
% set x 1; expr {foo($x?" 0x42 ":" 0x41" )}
foo:>>> 0x42 <<<
66
%
Nice shot !
This is due to the constant-expression-folding algorithm, which must
of course stop at f() since f is externally provided and can have side-
effects. The constant value is necessarily what comes out of an [expr]
call, done at compile-time, on the constant subexpression, and we've
just seen that a tryCvtToNumeric was always at the exit gate...
Please file a bug :)
-Alex
Glitch in the constant expression optimization. Please file as a bug.
Done: #3107983
(I'm not sure if the category (47 bytecode compiler) is correct, though)
PS: I just saw, Alex already found it, before I could even send
this posting to clt... :-)