How to retrieve the last command's return value in TCL interpreter? - tcl

In Tcl, if I evaluate a command, and my expression failed to store the result in a variable, other than reevaluating the command, is there a way to retrieve the last command's return value in a TCL interpreter? For example, in some lisps, the variable * is bound to the result of the last evaluation, is there something like that in TCL?

The Tcl interpreter loop — which is pretty simple minded, with very few hidden features — doesn't save the last result value for you; it assumes that if you want to save that for later, you'll do so manually (or you'll repeat the command and get the result anew so you can save it then).
If you want to save a value for later, use set. You can use * as the name of the variable (it is legal) but it's annoying in practice because the $var syntax shortcut doesn't work with it and instead you'd need to do:
% set * [expr {2**2**2**2}]; # Or whatever...
65536
% puts "the value was ${*}... this time!"
the value was 65536... this time!
% puts "an alternative is to do [set *]"
an alternative is to do 65536
This will probably be a bit annoying when typing; use a name like it instead.
% set it [expr {2**2**2**2}]; # Or whatever...
65536
% puts [string length [expr {123 << $it}]]
19731
(That last number has rather a lot of digits in it, more than I expected…)

For example, in some lisps, the variable * is bound to the result of
the last evaluation, is there something like that in TCL?
It depends, but in the non-error case, catch can be used to, well, catch the result of the last successful evaluation within the enclosed script:
proc foo {} {return FOO}
proc bar {} {return BAR}
if {![catch {
if {rand() < 0.5} {foo} else {bar}
} * opts]} {
# handle result of last cmd evaluation
puts ${*}
} else {
# handle an error
puts [dict get $opts -errorinfo]
}
If you happen to know the command names in advance, command traces might be another option.

Related

How to limit standard output at tclsh command line?

When running commands interactively at the tclsh command line, is there a way to truncate how much of a return value gets printed to stdout?
For example, this will take a very long time because the return value will print to stdout.
tclsh> set a [lrepeat 500000000 x]
I know I can add a dummy command in the same line, but this is an ad hoc solution. Is there some I could set in my ~/.tclshrc to truncate stdout to a finite length?
tclsh> set a [lrepeat 500000000 x] ; puts ""
Maybe this is an XY-problem (as turning off or swallowing prints to stdout seems to satisfy the OP), but the actual question was:
Is there some I could set in my ~/.tclshrc to truncate stdout to a
finite length?
You can use an interceptor on stdout (and/ or, stderr) to cap strings to a default limit:
oo::class create capped {
variable max
constructor {m} {
set max $m
}
method initialize {handle mode} {
if {$mode ne "write"} {error "can't handle reading"}
return {finalize initialize write}
}
method finalize {handle} {
# NOOP
}
method write {handle bytes} {
if {[string length $bytes] > $max} {
set enc [encoding system]
set str [encoding convertfrom $enc $bytes]
set newStr [string range $str 0 $max-1]
if {[string index $str end] eq "\n"} {
append newStr "\n"
}
set bytes [encoding convertto $enc $newStr]
}
return $bytes
}
}
Using chan push and chan pop you may turn on/off capping to, e.g., 30 characters:
% chan push stdout [capped new 30]
serial1
% puts [string repeat € 35]
€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€
% chan pop stdout
% puts [string repeat € 35]
€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€
Some remarks:
You can use an object, a namespace, or a proc offering the required interface of channel interceptors (initialize, write, ...); I prefer objects.
Ad write: You want to cap based on a character-based limit, not a byte-level one. However, write receives a string of bytes, not a string of characters. So, you need to be careful when enforcing the limit (back-transform the byte string into a char string, and vice versa, using encoding convertfrom and encoding convertto).
Similar, whether certain values of max might not be a good choice or the value range should be restricted. E.g., a max of 1 or 0 will turn off the basic REPL (the prompt % ), effectively.
As for tclshrc: You may want place the interceptor definition and chan push call therein, to enable capping per default?
tclsh is a REPL, and the "P" there is what you're seeing. Without digging into the source, I don't know that there's a simple way to accomplish exactly what you're asking.
If I remember to do it, the list command is useful to provide no output
set a [lrepeat 500000000 x]; list
or perhaps something informative
set a [lrepeat 500000000 x]; llength $a
If you want to get programmy:
proc i {val} {set ::tcl_interactive $val}
Then do i off or i 0 or i false to turn off interactivity and then execute the commands with large results. Going non-interactive silences the printing of command results, but it also turns off the prompt which could be confusing. Restore interactivity with i on or i 1 or i true

Tcl - Differentiate between list/dict and anonymous proc

I wrote the following proc, which simulates the filter function in Lodash (javascript library) (https://lodash.com/docs/4.17.4#filter). You can call it in 3.5 basic formats, seen in the examples section. For the latter three calling options I would like to get rid of the the requirement to send in -s (shorthand). In order to do that I need to differentiate between an anonymous proc and a list/dict/string.
I tried looking at string is, but there isn't a string is proc. In researching here: http://wiki.tcl.tk/10166 I found they recommend info complete, however in most cases the parameters would pass that test regardless of the type of parameter.
Does anyone know of a way to reliable test this? I know I could leave it or change the proc definition, but I'm trying to stay as true as possible to Lodash.
Examples:
set users [list \
[dict create user barney age 36 active true] \
[dict create user fred age 40 active false] \
]
1. set result [_filter [list 1 2 3 4] {x {return true}}]
2. set result [_filter $users -s [dict create age 36 active true]]
3. set result [_filter $users -s [list age 36]]
4. set result [_filter $users -s "active"]
Proc Code:
proc _filter {collection predicate args} {
# They want to use shorthand syntax
if {$predicate=="-s"} {
# They passed a list/dict
if {[_dictIs {*}$args]} {
set predicate {x {
upvar args args
set truthy 1
dict for {k v} {*}$args {
if {[dict get $x $k]!=$v} {
set truthy false
break
}
}
return $truthy
}}
# They passed just an individual string
} else {
set predicate {x {
upvar args args;
if {[dict get $x $args]} {
return true;
}
return false;
}}
}
}
# Start the result list and the index (which may not be used)
set result {}
set i -1
# For each item in collection apply the iteratee.
# Dynamically pass the correct parameters.
set paramLen [llength [lindex $predicate 0]]
foreach item $collection {
set param [list $item]
if {$paramLen>=2} {lappend param [incr i];}
if {$paramLen>=3} {lappend param $collection;}
if {[apply $predicate {*}$param]} {
lappend result $item
}
}
return $result
}
Is x {return true} a string, a list, a dictionary or a lambda term (the correct name for an anonymous proc)?
The truth is that it may be all of them; it would be correct to say it was a value that was a member of any of the mentioned types. You need to describe your intent more precisely and explicitly rather than hiding it inside some sort of type magic. That greater precision may be achieved by using an option like -s or by different main command names, but it is still necessary either way. You cannot correctly and safely do what you seek to do.
In a little more depth…
All Tcl values are valid as strings.
Lists have a defined syntax and are properly subtypes of strings. (They're implemented differently internally, but you are supposed to ignore such details.)
Dictionaries have a syntax that is equivalent to lists with even numbers of elements where the elements at the even indices are all unique from each other.
Lambda terms are lists with two or three elements (the third element is the name of the context namespace, and defaults to the global namespace if it is absent). The first element of the list needs to be a valid list as well.
A two-element list matches the requirements for all the above. In Tcl's actual type logic, it is simultaneously all of the above. A particular instantiation of the value might have a particular implementation representation under the covers, but that is a transient thing that does not reflect the true type of the value.
Tcl's type system is different to that of many other languages.

What purpose does upvar serve?

In the TCL code that I currently work on, the arguments in each procedure is upvar'ed to a local variable so to speak and then used. Something like this:
proc configure_XXXX { params_name_abc params_name_xyz} {
upvar $params_name_abc abc
upvar $params_name_xyz xyz
}
From here on, abc and xyz will be used to do whatever. I read the upvar TCL wiki but could not understand the advantages. I mean why cant we just use the variables that have been received as the arguments in the procedure. Could anybody please elaborate?
I mean why cant we just use the variables that have been received as the arguments in the procedure.
You can. It just gets annoying.
Typically, when you pass the name of a variable to a command, it is so that command can modify that variable. The classic examples of this are the set and incr commands, both of which take the name of a variable as their first argument.
set thisVariable $thisValue
You can do this with procedures too, but then you need to access the variable from the context of the procedure when it is a variable that is defined in the context of the caller of the procedure, which might be a namespace or might be a different local variable frame. To do that, we usually use upvar, which makes an alias from a local variable to a variable in the other context.
For example, here's a reimplementation of incr:
proc myIncr {variable {increment 1}} {
upvar 1 $variable v
set v [expr {$v + $increment}]
}
Why does writing to the local variable v cause the variable in the caller's context to be updated? Because we've aliased it (internally, it set up via a pointer to the other variable's storage structure; it's very fast once the upvar has been done). The same underlying mechanism is used for global and variable; they're all boiled down to fast variable aliases.
You could do it without, provided you use uplevel instead, but that gets rather more annoying:
proc myIncr {variable {increment 1}} {
set v [uplevel 1 [list set $variable]]
set v [expr {$v + $increment}]
uplevel 1 [list set $variable $v]
}
That's pretty nasty!
Alternatively, supposing we didn't do this at all. Then we'd need to pass the variable in by its value and then assign the result afterwards:
proc myIncr {v {increment 1}} {
set v [expr {$v + $increment}]
return $v
}
# Called like this
set foo [myIncr $foo]
Sometimes the right thing, but a totally different way of working!
One of the core principles of Tcl is that pretty much anything you can do with a standard library command (such as if or puts or incr) could also be done with a command that you wrote yourself. There are no keywords. Naturally there might be some efficiency concerns and some of the commands might need to be done in another language such as C to work right, but the semantics don't make any command special. They all just plain commands.
The upvar command will allow you to modify a variable in a block and make this modification visible from parent block.
Try this:
# a function that will modify the variable passed
proc set_upvar { varname } {
upvar 1 $varname var
puts "var was $var\n"
set var 5
puts "var is now $var\n"
}
# a function that will use the variable but that will not change it
proc set_no_upvar { var } {
puts "var was $var\n"
set var 6
puts "var is now $var\n"
}
set foo 10
# note the lack of '$' here
set_upvar foo
puts "foo is $foo\n"
set_no_upvar $foo
puts "foo is $foo\n"
As it was mentioned in comment above, it is often used for passing function arguments by reference (call by reference). A picture costs a thousand words:
proc f1 {x} {
upvar $x value
set value 0
}
proc f2 {x} {
set x 0
}
set x 1
f1 x
puts $x
set x 1
f2 x
puts $x
will result in:
$ ./call-by-ref.tcl
0
1
With upvar we changed variable x outside of function (from 1 to 0), without upvar we didn't.

"can't read:variable is array" error in ns2

In ns2, I declared a simple array using
array set ktree {}
then I tried to use it as a GOD variable as
create-god $ktree
but this gives the error
can't read "ktree": variable is array
while executing
"create-god $ktree {}"
Any help is greatly appreciated.
In Tcl, $varName means “read from the variable called varName” and is not a general reference to the variable (unlike some other languages, notably Perl and PHP, which do rather different things). Reading from a whole array, instead of an element of that array, is always an error in Tcl.
To pass an array to a command, you pass the name of that array in. It's then up to that command to access it as it sees fit. For procedures and methods written in Tcl, it'll typically involve upvar to link the array into a local view. (Things written directly in C or C++ have far fewer restrictions as they don't automatically push a Tcl stack frame.)
Note however that the command must be expecting the name of an array when you pass that name in. (Good programmers will document this fact, of course.) Whether create-god does, I really have no idea; it's not a general Tcl command but rather something that's more specific. (Part of ns2? Or maybe your own code.)
Example of passing in an array
An example of passing in an array by name is the parray command that should be part of every Tcl distribution. It's a procedure that prints an array out. Here's the source code without a few boiler-plate comments:
proc parray {a {pattern *}} {
upvar 1 $a array
if {![array exists array]} {
error "\"$a\" isn't an array"
}
set maxl 0
set names [lsort [array names array $pattern]]
foreach name $names {
if {[string length $name] > $maxl} {
set maxl [string length $name]
}
}
set maxl [expr {$maxl + [string length $a] + 2}]
foreach name $names {
set nameString [format %s(%s) $a $name]
puts stdout [format "%-*s = %s" $maxl $nameString $array($name)]
}
}
The key thing here is that we first see upvar 1 to bind the named variable in the caller to a local variable, and a test with array exists to see if the user really passed in an array (so as to give a good error message rather than a rubbishy one). Everything else then is just the implementation of how to actually pretty-print an associative array (finding out the max key length and doing some formatted output); it's just plain Tcl code.

evaluating an expression to true in a if statement with Tcl

I'm having serious biggies trying to figure out how to evaluate an expression in a If statement using Tcl. This is what I have:
#!/bin/sh
#The next line executes wish - wherever it is \
exec wish "$0" "$#"
set apid "/var/run/apache2.pid"
set apsta [file exist $apid]
if { $apsta == 1 }
{
set result ":)"
}
else
{
set result ":("
}
label .status -text $result
pack .status
and this is what I get from the terminal:
# wan27
Error in startup script: wrong # args: no script following " $apsta == 1 " argument
while executing
"if { $apsta == 1 }"
(file "/usr/bin/wan27" line 9)
I'm just trying to output a happy smiley if Apache is running, a sad one if Apache is stopped - based upon the boolean condition whether or not the file "/var/run/apache2.pid" exists ...
The syntax of a Tcl if statement is
if condition statement else statement
which must be on one line. Braces allow continuation across lines and their placement is mandatory:
if { $apsta == 1 } {
set result ":)"
} else {
set result ":("
}
As you have it written, Tcl sees
if { $apsta == 1 }
and stops there yielding a syntax error.
MSW have already given you the correct answer but I think a little bit more explanation is needed to clear up some other confusions you are having based on your comments.
I will first explain things using non-tcl terminology since I think it is less confusing that way.
In tcl, if is not a statement. if is a function. That is the reason why the opening brace need to be on the same line: because a newline terminates the list of arguments to a function. For example, in the following code:
a b c d
e f
the Tcl interpreter will see two function calls. The first to function a with arguments b, c and d and the second to function e with a single argumrnt f. Similarly, in the following:
if a
b
Tcl sees a call to the function if with a single argument. Since if expects at least two arguments it (not the interpreter itself) throws an error complaining about wrong number of arguments.
This also explains why there must be a space between if and its first argument. It's just because in tcl names of variables and functions are literally allowed to contain almost anything including spaces, commas and non-printing characters like NUL. For example, you can define a function called a{b}:
proc a{b} {} {puts HA!}
a{b} ;# <-- prints out HA!
So if you do something like:
if{x} {y}
tcl will complain that the function if{x} is not defined.
if is not the only thing that works like this. Tcl doesn't really have keywords, just a standard library of built-in functions like for, foreach and while. The same rules apply to all of them.
not really important:
On a side, the if function in tcl works like the ternary operator in C: it returns a value. In fact you can do the following:
# It is unfortunate that this "give" function is not built in.
# We use it to return something from if without actually
# calling return:
proc give {x} {return $x}
set something [if {$x} {give "Very true indeed."} {give "Unfortunately false."}]