Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

TCL's handling of "0x" is not what I expected

Status
Not open for further replies.

tktuffkid

Programmer
Dec 12, 2006
11
0
0
US
Lately, I have been writing procs that handle hexadecimal numbers. And if I understand how the TCL interpreter works, it performs the following in order:

1) Grouping
2) Single round of Substitution
3) Command Invocation

While learning TCL, I was confused on how curly braces' literal interpretation allowed various TCL commands like 'foreach' or 'if' to work. I then convinced myself that it is these 3 basic rules which allow TCL commands to separate its arguments with curly braces and evaluate each of the arguments' value at Command Invocation.

Until today, all was well until I noticed some peculiar observations while handling hexadecimal strings containing '0x'. I have been able to workaround these issues but am more curious as to "why" TCL is behaving as it is.

Problem #1
==========
In one of my (poorly written) procedures, I perform an operation on a hexadecimal number that is NOT preceeded by the string '0x'. So therefore, when I want 'expr' to add two hexadecimal numbers, I prepend an '0x' to them. Here's an example:

% expr {0x80 + 0x01}
129 <==== This is expected

% set value1 80
% set value2 01
% expr {0x$value1 + 0x$value2}
syntax error in expression "0x$value1 + 0x$value2": extra tokens at end of expression

This is obviously not what I expected. Why is this error happening? Doesn't this completely go against the 3 basic rules which I listed above?

As a workaround, I changed all of my procs to return a hexadecimal number WITH an 0x attached. By doing this, it makes 'expr' cleaner and causes it to work as I expect (i.e. expr {$value1 + $value2} works well since value1 and value2 already contain an '0x')

Problem #2
==========
I then needed to convert a decimal number to a hexadecimal number. It is essential that these hexadecimal numbers contain padded zeroes in the header. I decided that the 'format' command was the proper command to use. In one of my TCL references, it states that the format flag # causes a leading 0x to be printed and the format flag 0 causes leading zeroes to be printed. However, based on the ordering, the following doesn't seem right.

% format %#04x 129
0x81 <==== Shouldn't this have returned 0x0081
% format %#06x 129
0x0081 <==== Shouldn't this have returned 0x000081

Is the field width specifier also counting the 0x string?

Problem #3
==========
Finally, to account for Problem #1, I decided that my procs should be smart enough to accept hexadecimal numbers that may or may not be preceeded with an '0x' string. So in the first line of my proc, I decided to use the 'string trimleft' command. But as you'll see below, leading zeroes also got trimmed off.

% string trimleft 123abc 0x
123abc <==== This is expected
% string trimleft 0x123abc 0x
123abc <==== This is expected
% string trimleft 0x0123abc 0x
123abc <==== Shouldn't this have returned 0123abc

Well, those 3 problems are it in a nutshell and basically caused me to strongly second guess my knowledge of TCL. Any help would be greatly appreciated.
 
1 - Grouping

Tcl has eleven rules:
Expr gets its args from Tcl and does a second level of evaluation. Conversion of hexadecimal values is done during the substitution process.

So, as you seen, you can:
- give a full hexadecimal value (as 0x01)
or
- give two parts of the value (as 0x$value) but let Tcl do its substitution before passing the args to expr. That is: remove the braces around the expression (expr 0x$value+1)


2 - Format use

The # in the format pattern does not perform as you expect:

"#
Requests an alternate output form. For o and O conversions it guarantees that the first digit is always 0. For x or X conversions, 0x or 0X (respectively) will be added to the beginning of the result unless it is zero. For all floating-point conversions (e, E, f, g, and G) it guarantees that the result always has a decimal point. For g and G conversions it specifies that trailing zeroes should not be removed."

What you want is:
format %#4.4x 0x23
->0x0023
But caution with:
format %#4.4x 012
->0x000a
(012 is the octal of 10)

More on format:
3 - string trimleft use

From the description of string:

"string trim string ?chars?
Returns a value equal to string except that any leading or trailing characters from the set given by chars are removed."

You get the same result (123) from
[string trimleft 000xxxx123 0x] or
[string trimleft x000123 0x]
In other words: the last pattern of trimleft is a set of chars to be deleted from the front of the value.

More on string:
Hope to help

ulis
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top