Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

hex numbers with expr

1,193 views
Skip to first unread message

Paul

unread,
Oct 26, 2010, 4:13:38 AM10/26/10
to
For the following code:

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.

Message has been deleted

Paul

unread,
Oct 26, 2010, 7:32:41 AM10/26/10
to
On Oct 26, 11:04 am, Harald Oehlmann <wortka...@yahoo.de> wrote:
> In the first case, the tcl interpreter is expanding the variables, in
> the second it is the expr command.
> They do it differently.
>
> What happens in the first case:
> - the TCL parser gets "expr 0x$a + 0x$b"
> - Variables expanded while keeping word together: expr 0x55 + 0xaa
> - Call expr with this string: "0x55 + 0xaa" which gives the result
> of : 255
>
> What happens in the second case:
> - the TCL parser gets "expr {0x$a + 0x$b}"
> - Call expr with this string: "0x$a + 0x$b"
> - Here, $-expansion is only done, if at a beginning of an operand, so
> we get the the operants "0x$a" which are illegal.
> I would do it like that

> set a 55
> set b aa
> if {1 == [scan $a %x ad] && 1 == [scan $b %x bd]} {
> set r [expr {$ad + $bd}]}
>
> This is good for performance, because ad and bd are native integers
> and thus expr only use native numbers and no strings.

Ah! Thank you for the explanation.

Paul.

Gerry Snyder

unread,
Oct 26, 2010, 2:26:51 PM10/26/10
to
On 10/26/2010 3:04 AM, Harald Oehlmann wrote:
>....

> What happens in the second case:
> - the TCL parser gets "expr {0x$a + 0x$b}"
> - Call expr with this string: "0x$a + 0x$b"
> - Here, $-expansion is only done, if at a beginning of an operand, so
> we get the the operants "0x$a" which are illegal.


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...."

Don Porter

unread,
Oct 26, 2010, 2:41:43 PM10/26/10
to

"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 |
|______________________________________________________________________|

Donald Arseneau

unread,
Oct 26, 2010, 8:52:36 PM10/26/10
to
Don Porter <d...@nist.gov> writes:

> [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

Harald Oehlmann

unread,
Oct 27, 2010, 3:06:23 AM10/27/10
to
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.

For me, another expr magic question is: when do "==" work for strings?

Thank you,
Harald

Andreas Leitgeb

unread,
Oct 27, 2010, 8:08:19 AM10/27/10
to
Harald Oehlmann <wort...@yahoo.de> wrote:
> 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.
> 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 }

% 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.

Alexandre Ferrieux

unread,
Oct 27, 2010, 8:46:32 AM10/27/10
to
On Oct 27, 2:08 pm, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at>
wrote:

>
> 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


Andreas Leitgeb

unread,
Oct 27, 2010, 10:25:19 AM10/27/10
to
Alexandre Ferrieux <alexandre...@gmail.com> wrote:
>> 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 ...
>
> Bottom line: [expr] ... will insist on morphing its _output_ into a number.

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.

Alexandre Ferrieux

unread,
Oct 27, 2010, 10:45:51 AM10/27/10
to
On Oct 27, 4:25 pm, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at>
wrote:

> Alexandre Ferrieux <alexandre.ferri...@gmail.com> wrote:
> >> 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 ...
>
> > Bottom line: [expr] ... will insist on morphing its _output_ into a number.
>
> 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().

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


tom.rmadilo

unread,
Oct 27, 2010, 2:15:42 PM10/27/10
to

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.

Donald Arseneau

unread,
Oct 30, 2010, 1:54:10 AM10/30/10
to
Harald Oehlmann <wort...@yahoo.de> writes:

> 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

rick h

unread,
Nov 1, 2010, 12:29:40 PM11/1/10
to

Tom, you are soo correct...and that's why there is COBOL!

Andreas Leitgeb

unread,
Nov 11, 2010, 12:11:38 PM11/11/10
to
Alexandre Ferrieux <alexandre...@gmail.com> wrote:
> On Oct 27, 4:25 pm, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at>
>> 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().
> That's just because the contract of a mathop is to take a number as
> input.

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.

Alexandre Ferrieux

unread,
Nov 11, 2010, 1:09:14 PM11/11/10
to
On Nov 11, 6:11 pm, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at>
wrote:

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

Andreas Leitgeb

unread,
Nov 11, 2010, 2:30:30 PM11/11/10
to
Alexandre Ferrieux <alexandre...@gmail.com> wrote:
> As a proof:
> % dis script {expr {f($x?"0x41":"0x42")}}

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
%

Alexandre Ferrieux

unread,
Nov 11, 2010, 4:56:51 PM11/11/10
to
On Nov 11, 8:30 pm, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at>
wrote:

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

Donald G Porter

unread,
Nov 12, 2010, 10:16:30 AM11/12/10
to
Andreas Leitgeb wrote:
> % 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.

Glitch in the constant expression optimization. Please file as a bug.

Andreas Leitgeb

unread,
Nov 12, 2010, 10:38:08 AM11/12/10
to
Donald G Porter <d...@nist.gov> wrote:
> Andreas Leitgeb wrote:
>> % 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.
> 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... :-)

0 new messages