evaluating an expression to true in a if statement with Tcl - 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."}]

Related

How to convert script into a hard coded version of it in Tcl language?

I am looking for a way to convert a script into its fully hard-coded version. I even don't know how this kind of function is named in software industry.. so I even don't know how to google it. Here are some examples of what I want. I don't want to use any conditional or iterative function for the outputs.
Example 1) foreach case
foreach x [list 1 2 3] {
puts "x=$x"
}
I'd like to convert this script to have:
puts "x=1" ; puts "x=2" ; puts "x=3"
Example 2)
set A 1
if { $A == "1" } {
puts "A is 1"
} else {
puts "A is not 1"
}
I want this to be :
set A 1
puts "A is 1"
Example 3) If I face comment line or unknown command or procedure, I just want to pass it to the output.
set argument 1000
UnknownProcedure $argement
This now should be :
set argument 100
UnknownProcedure 100
Thanks in advance.
You could try trace add execution ... to commands. This is used to execute a body of code before the command itself if executed. If you can pass the arguments of your command to your trace function and also use info frame to get the proc/command name being executed, then you could use the trace function to write out a hardcoded version of the command.
See here:
https://www.tcl-lang.org/man/tcl8.6/TclCmd/trace.htm
https://wiki.tcl-lang.org/page/info+frame

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

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.

Need info about return -<options> in TCL

What are the options are available in TCL return command ? i could see -code -errorcode -errorinfo as options for return command.. but couldn't understand the difference between them.
proc new {} {
set name george
puts "Hello $name"
return -code ok
}
puts [new]
Here i didn't get any output, it is like normal return command. But could get more option for -code in return command like break , continue, error and also retun... What is the use of this options.
And also below code makes me confuse ..
proc new {} {
set name george
puts "Hello $name"
return -code return -code return -code return
}
like that we can give more , it didn't get errored , what is the use of this option..
The options for return are listed on the man page: return.
-errorcode, -errorinfo, -errorstack, and -level are used to describe aspects of an exception: -options allows one to pass a dictionary of exception data to the command. Neither of these is likely to be useful to you until you have a thorough understanding of the language and are writing advanced exception handling code.
The -code option is used to specify what kind of handling surrounding code will need to perform. ok means that no special handling is necessary and that the result of the command is valid data; error means that a catch or try handler needs to be in place or the program will end, and also that the result of the command is an error message. break and continue need an enclosing looping command (or a catch / try command with an appropriate handler clause). return makes the command where it is used behave like a return command. You probably won't need to deal with this option unless you are writing your own control structures.
The -level option also lets the return command return to somewhere else than in the caller (but still in the call stack). If the value is 0, the return action is "in place": return will return from itself without terminating the script it is part of. The first line of
set foo [return -level 0 bar]
puts {I'm still alive}
will be executed as set foo bar, and evaluation will continue with the puts command. -level 1 means return to the caller (the usual behavior), -level 2 means return to the caller's caller, and so on.
The return command accepts just about anything in its argument list, and like many commands won't complain about arguments being repeated. return -code return -code return -code return does the same thing as return -code return.

Why doesn't tcl's "if" return values?

Why doesn't this work like I would think?
set x [if {1} {return 2} {return 3}]
The command output shows 2, but when you check the value of x, it is found to be undefined..!
I have read a workaround, ...
proc give {x} {return $x}
set x [if {1} {give 2} {give 3}]
...but I don't understand why the first one doesn't work.
From the tcl 8.5 documentation for if:
"The return value from the command is the result of the body script that was executed, or an empty string if none of the expressions was non-zero and there was no bodyN."
Questions:
Why doesn't set x [if {1} {return 2} {return 3}] set a value for x?
Given that set x [if {1} {return 2} {return 3}] doesn't work, how does tclsh display "2" on the interactive shell?
Given that tclsh displays "2" on the interactive shell, how is it that set x remains undefined?
See http://www.tcl.tk/man/tcl8.6/TclCmd/return.htm
Return codes are used in Tcl to control program flow. A Tcl script is a sequence of Tcl commands. So long as each command evaluation returns a return code of TCL_OK, evaluation will continue to the next command in the script. Any exceptional return code (non-TCL_OK) returned by a command evaluation causes the flow on to the next command to be interrupted. Script evaluation ceases, and the exceptional return code from the command becomes the return code of the full script evaluation. This is the mechanism by which errors during script evaluation cause an interruption and unwinding of the call stack. It is also the mechanism by which commands like break, continue, and return cause script evaluation to terminate without evaluating all commands in sequence.
So the evaluation of the script is aborted, x does not get set, and 2 is returned and printed as a result.
The expr command returns a value, so
set x [if 1 {expr 2} {expr 3}]
But if you're doing that, you might as well use the ternary operator
set x [expr {1 ? 2 : 3}]
The reason that set x [if {1} {return 2} {return 3}] doesn't work the way you expect, is, as already pointed out, the fact that return is a command that causes script evaluation to terminate. The "workaround", using a give procedure, demonstrates the fact that by default, the return command terminates 1 level of the call stack.
However, the "workaround" really is a pointless exercise in masochism: The return command allows you to specify where to return to, using the -level option introduced in Tcl 8.5. Try this, see how it works for you.
set x [if {1} {return -level 0 2} {return -level 0 3}]

Expecting an error in TCL for the code for non initialization of variable

I have the below small code I am expecting the TCL interpreter to throw error for the variable not initialized but it not throwing an error
set v1 "test"
if { ($v1 != "test") && ($v2 == "3") } {
puts "fun"
}
#v2 is not initialized
No error is being thrown by interpreter for v2 , as it not initialized
First, the sublanguage of expr in TCL, (which is also used in if, while, for) works in another way than TCL itself. In this sublanguage, $ doesn't mean variable substitution, but a variable reference. Variables are accessed when their containing subexpressions are evaluated. And logical operations are short-circuiting, evaluating operands from left to right until the result is known, as in C language.
That's why $v2 == 3 is not evaluated in your example, and nonexistent $v2 is not an error. This behavior is commonly used with code like this:
if {[info exists myvar] && $myvar} { .... }