What does it mean proc argument is variable - tcl

I wonder if it is right semantics to have a variable as an argument, something like this:
proc p1 {$aa} {}
I tried it on tclsh, there is no complaint, but the following experiment fails:
% set aa bb
bb
% set bb 200
200
% proc p1 {$aa} {puts $bb}
% p1 bb
can't read "bb": no such variable
Do you see what is wrong?
[UPDATE - after seeing Peter's answer]
I know the upvar semantic, thanks.
My main curiosity is still around using variable as proc argument. I know it is not common, but just cannot help musing what it really can do if the language syntax allows it.
Yes, your upvar example is exactly what I want to explore using a variable as proc argument, but my exploration so far tells me, really, there is no way we can do this because "$" is interpreted as a plain char.
Do debunk me please if I am wrong.

Tcl does not support reference arguments as such: the usual pass-by-reference semantics is too static for Tcl. Instead, the logic of the command can, by use of upvar, dynamically create reference parameters including indirect reference parameters and calculated reference parameters, and also retarget the local name to another external variable. The upvar mechanism may look ungainly, but is very powerful indeed.
(The (edited) remains of my original answer follows:)
The usual idiom for doing this is
proc p varName {
upvar 1 $varName var
puts $var
}
The upvar command looks into another stack frame (in this case 1, which is the caller's stack frame) and makes a variable named $varName (i.e. the variable's name is the value of varName) in that stack frame and a variable named "var" in the command's stack frame refer to the same data object.
I won't explain this further since this is not useful to the asker.
Documentation: proc, puts, upvar

There are cases where it makes sense for the arguments in a procedure creation call to be supplied from a variable. The main example is where you are creating procedures dynamically, calculating the arguments you want to use as you go along.
That's actually a use-case that isn't done very frequently! It's not particularly easy to use well. But I have done it. (OK, that's a method call, but the syntax of formal arguments is shared.)
The main reason that the capability is there is that it's part of Tcl's general syntax. Tcl tries very hard to not have special cases in how it parses things (other than in how a command parses the strings passed into it) and this includes in things that would be very special cases in the enormous majority of other programming languages, such as formal parameter lists to procedures. In Tcl, these are just ordinary values and can be produced using any technique that gives ordinary values.
The usual thing with putting them in braces is just how you do it reliably and is easy to teach. It's also overwhelmingly what people want to do.
All this is independent of the facts that Tcl's commands (including its procedures) can handle variable numbers of arguments (check out the special args parameter and how to specify default values) and that $aa is a legal (but strange) name for a local variable.

Related

Understanding the use of Tcl namespace variable

I have a namespace variable which is defined as below:
namespace eval ::SMB::{
variable SmbInfo
------
------
proc ::SMB::SmbCreate {name dutport} {
variable SmbInfo
global DutPorts DutPort_2 DutPorts_3 smb
------
------
if{"" != [info command SMB::$name]} {
return -code error "command name \"$name\" already exists"
}
set SmbInfo($name -DutPort) $dutport
I am new to Tcl and trying to understand the above piece of code. Few questions, please correct me if I am wrong at any point:
The variable SmbInfo defined on top in namespace is getting overridden by the one declared in the procedure SmbCreate. I am unable to figure out what is the objective of the line:
set SmbInfo($name -DutPort) $dutport
I can see that 'DutPorts' is defined as global but I could not find 'DutPort'. I have not executed the code yet. Could it be an error?
Is ($name - DutPort) creating an array index for the variable SmbInfo and the value of $dutport is being set to that particular array variable?
I have similar code structures in the file like below
set SmbInfo($name - SmbSetDmac) [BuildMac1 $SmbInfo($from_name-DutPort)]
Where BuildMac1 is a procedure. A bit explanation of the above code might also make the thing clear.
If anything I missed to post in the question, kindly point me, I will edit my question.
Thanks in advance.
The second declaration doesn't override, it's the same variable in both cases.
The command is a syntax error because of the space after $name. The intent seems to be to assign the value of $dutport to the member of SmbInfo that has the name "$name -DutPort" (where $name is replaced by the variable value).
A similar assignment, but here the value comes from the result of the command.
There are a few syntax errors in the code, too many or too few spaces here and there. It seems unlikely this code has ever been executed.
The purpose of the smb::SmbCreate command would seem to be to 1) create a new command in the SMB namespace named by the first parameter (unless such a command already exists) and 2) store metadata in the SmbInfo namespace variable, indexed by a) the name parameter and b) a keyword such as -DutPort or -SmbSetDmac.
Code like this essentially implements an ad-hoc object-oriented interface. If the whitespace issues are resolved, it should work fine.
You have many syntactic problems that are going to cause you much grief. Tcl cares very much about its syntax level, which includes exactly where the spaces and newlines are, and whether there are {braces} and [brackets] as expected. You must get these things right.
Looking at the specific code you're having problems with, this line:
set SmbInfo($name -DutPort) $dutport
would appear to be highly unlikely, as it is passing three arguments to the set command when that only takes one or two. I'd guess that you've got a command that you're calling to obtain a key for an array, and that the code therefore ought to be this:
set SmbInfo([$name -DutPort]) $dutport
See those [brackets]? They matter here, as they say “run my contents as a little subscript and use the result”. With that sorted out, there's also the question of whether $name -DutPort works at all, but you'll just have to be guided by the error messages there. Tcl usually gives very good error messages, though sometimes you have to think about why the code got in the state where it is giving that message in order to figure out what the actual problem is. You know, usual debugging…
I would expect similar problems with:
set SmbInfo($name - SmbSetDmac) [BuildMac1 $SmbInfo($from_name-DutPort)]
and would guess that it is actually supposed to be:
set SmbInfo([$name -SmbSetDmac]) [BuildMac1 $SmbInfo([$from_name -DutPort])]
Note again that I have modified the spaces to follow the existing pattern (which I'm guessing is a property access; it looks like it's OTcl or XOTcl) and added brackets.
Finally, this line:
if{"" != [info command SMB::$name]} {
is also syntactically wrong, and should instead be:
if {"" != [info command SMB::$name]} {
That extra space matters, because it separates the word that is the command name (if) from the word that is the condition expression. The remainder of the line is probably correct (the SMB::$name might be suspicious, except you're using it in info command, but then you probably only need info command $name as it already knows about what namespace you're working in and you're using the unqualified name elsewhere).

extend tcl operator : is there a direct operator extension interface

If I want to introduce some new expression operator, e.g. "%%" to deal with some custom data structure, I will need to re-define "expr" command to recognize it. Is this right?
Basically TCL extension is at the command level, there is no interface to directly add new operator.
Could you confirm my understanding?
The operators in Tcl are implemented directly in Tcl's bytecode engine; you can't extend them or modify their semantics. Not without editing the expression parser (written in C) or the bytecode engine itself (a compiler and an execution engine, both written in C). Extending this part of Tcl directly is rather difficult. (You're looking at tclParseExpr.c, tclCompExpr.c and tclExecute.c at least to make things work, and quite possibly the other tclParse* and tclComp* files too, as well as the assembler and maybe the disassembler. Not to be taken on lightly!)
However…
You can write your own command that interprets a string as anything you want, including an expression involving whatever new operators you desire. This is what VecTcl does. Under the covers, it converts its expression language into calls to Tcl commands that implement the various operators (some of which are implemented in C, but as a normal extension package, so that's nothing unusual).
Effectively, it converts an invoke of one of its expressions:
vexpr {3*x}
into something a bit like this:
numarray::* 3 [set x]
which it then can execute using Tcl's bytecode engine (frankly, a trivial execution, but it makes more sense in more complex examples; see the VecTcl tutorial, which is a bit too long to reproduce here).
The mathop manual page calls out explicitly that while you can access the expr operators using the ::tcl::mathop namespace you cannot add new ones nor redefine the existing ones there. You can add you expr functions using the tcl::mathfunc namespace as documented in the expr man page so if a function will do that is the simplest method to do what you want.
Otherwise you could intercept the expr command itself. If you rename the expr command and define your own replacement you can do some expression parsing and replace your custom operator with a function call or pass the expression on to the real implementation. Below is a trivial example.
rename expr _expr
proc expr {args} {puts "called expr $args"; _expr {*}$args}
In short, your understanding is correct. You cannot inject new operators into the expr expression parser.

Manipulation of variables inside a proc with bind

Sorry for the title couldn't think of anything better.
Heres my question:
Im trying to change a variable inside a proc only when the user hits space. the proc loops itself with after so if the user wish to hit space more then once the variable will increment.
Heres what I know:
There are many ways to go about this. You can pass variable inside the proc, you can link the variable with global or upvar and/or if your in a namespace then you can use variable. but the only one that seem to work with my is global. I'm getting a feeling it's because global makes a link but if thats true then variable should work too, right?
Here my test code:
proc test1 {} {
global testing
bind . <Key-a> {incr testing}
puts $testing
puts "test2"
after 100 test2
}
namespace eval test2 {
variable testing 0
namespace export {[a-z]*}
proc of1 {} {
variable testing
bind . <Key-a> {incr testing}
puts $testing
after 100 test3::of1
}
}
proc test3 {testing} {
bind . <Key-a> {incr testing}
puts $testing
puts "test4"
after 100 test4 $testing
}
set testing 0
#test1
test2::of1
#test3 0
grid .c
side question:
Why is it that in the global namespace we use set and global while in namespace we use variable (that seem to set and do global in one command). they seem to do the same job in different namespaces?
Variables in Callbacks
The scripts registered by the bind command — also in things like after events and fileevent callbacks — are evaluated in the global scope because they may be called long after the procedure that defined them returns; Tcl does not do scope capture (and that's actually a really complicated feature, so it isn't likely to come soon unless someone writes lots of code). This means that the variable that you want your procedure to notice changes to must also have global scope.
However, namespaced variables count just fine as global variables for the purpose of this discussion as they're nameable from a global context (real local variables are not). That means that we can do several ways to build a script that accesses a namespace variable from a bind-defined callback. Here's one of the nicer ones:
bind . <Key-a> [namespace code {incr testing}]
That's effectively the same as this:
bind . <Key-a> [list namespace eval [namespace current] {incr testing}]
(There are some strict differences that don't matter in this example.)
Another way to do the callback is this:
bind . <Key-a> [list incr [namespace which -variable testing]]
Which in this case is going to be much like:
bind . <Key-a> [list incr [namespace current]::testing]
If things are getting any more complicated than this toy example, it's time to stop updating variables directly in a binding script and instead write a helper procedure. That always simplifies things a lot. Or to use a class/object to encapsulate the details.
The variable Command: Why and Where to Use It
Why is it that in the global namespace we use set and global while in namespace we use variable (that seem to set and do global in one command). they seem to do the same job in different namespaces?
That's a good question. In fact, what global does is very much like upvar #0 (with the variable names doubled up), and set is a fundamental variable access command. They're commands that you can use regularly wherever you want their behaviour.
The variable command is rather stranger. What it does is three-fold:
If called in a namespace context and the variable does not exist in that namespace, it creates that variable in a present-but-unset state.
If called in a context with local variables, it links a local variable with the name (after stripping everything up to the last namespace separator) to a namespace variable with the name (using the whole supplied name if there are qualifiers, and resolving non-absolute names with respect to the current context namespace). This also forces the namespace variable to exist in the present-but-unset state.
If a value is given, the namespace variable is set to the value. This gets rid of the present-but-unset-ness.
The important behaviour is actually the creating of that present-but-unset state, since otherwise you can end up with a set (or array set) in the namespace escaping that namespace and instead working with a global variable, but not always. It all depends on the exact behaviour of the code that resolves variables, and that's deeply tricky. This is hard to explain properly, and ridiculously hard to justify. It's been the cause of quite a few outright bugs, and is definitely no better than a horrible misfeature.
The setting of the initial value is little more than a lollipop; you could instead put set straight afterwards without ill-effect. What's more, it inhibits using variable to pull in multiple variables from a namespace except in the case where you're setting them to a known value; great for initialisation, but sucky for other uses. (In case you hadn't guessed, I think this is an area of Tcl where the interface was got rather badly wrong back when it was introduced, back in Tcl 8.0. Not one bit of this is nice.)
The key take-away is this though:
Always use variable in your namespaces to declare variables as this is the only way you can be sure that the semantics is predictable. You can then initialise them any way you want. (You have to do it this way if you're creating arrays.)
Fully-qualified variable names have no craziness associated with them. Tcl always knows exactly how to look up the thing you're naming in that case.

Why is 'bicrement' not possible like this?

Why is this not possible?
int c = 0;
++c++;
Or in PHP:
$c = 0;
++$c++;
I would expect it to increment the variable c by 2, or perhaps do something weird, but instead it gives an error while compiling. I've tried to come up with a reason but got nothing really... My reasoning was this:
The compiler reads ++
It reads the variable
It does whatever it does to make the value of the variable increment and then get returned when executing the application
It encounters another ++
It uses the previous variable in order to return it before incrementing the value
This is where it gets confusing: does it use the variable c, or does it try to read the value that (++c) returned? Or, since you can only do varname++ (and not 2++), does it see (++c) as a pointer and then tries to read that memory location? Whatever it does, why would it give a compile error? Or is the error preventive, because the compiler's programmer knew it wouldn't do anything useful?
It's not really that I would want to use this, and certainly not for code that is not one-time use only, but I'm just curious.
For the same reason you can't do:
c++ = 5;
It returns a value, which cannot be modified nor assigned to. That's not a runtime error, either - that's a compilation error. (Like this one.)
Returning a reference wouldn't make sense either, because then:
$a = 1;
$b = $a++; // How can it be a reference if b should be 1 and a should be 2?
This isn't necessarily language-agnostic, although I'd be a little surprised to see a language in which it was different.
In C (and hence I assume in all non-annoying languages based on C), the operator precedence means your expression is equivalent to ++(c++). Based on your step-by-step, you were expecting it to be equivalent to (++c)++, but it isn't. Postfix ++ "binds more tightly" than prefix ++.
Like everyone says, the expression c++ results in a value, not a modifiable object (an expression in C that refers to an object is called an lvalue). This is necessary because the object c no longer holds the value that the expression evaluates to -- there is no object that c++ could refer to.
In C++ (not to be confused with c++!), ++c is an lvalue. It refers to the object c, which now has the new value.
So in C++ (++c)++ is syntactically correct. It happens to have undefined behavior if c is of type int (at least, it did in C++03: C++11 made some things defined that used to be undefined and I'm not up to date on those changes).
So, if you imagine (or go ahead and invent) a C++-like language in which the operator precedence is what you expected, then you could arrange for ++c++ to be valid. Presumably it would increment c twice, and evaluate to the value in between the old and new values. This imagined language would be "annoying" in the sense that it's only subtly different from C++, which would tend to be confusing.
++c++ is also valid in C++ if c is an instance of a user-defined type that overloads both increment operators. The reason is that an rvalue of user-defined type is an object (a "temporary object"), and can be modified. But as soon as the expression has been evaluated, the temporary is destroyed and the modification is lost.
Another way to Explain this is that the ++ operator only operates on an lvalue. But when you combine them, it's parsed as either ++(c++) or (++c)++ -- in either case, the parameter to the operator outside the parentheses is an rvalue (c's value before or after the increment), not an lvalue.

Adding help to the user defined functions in TCL

how can i add some kind of help to the user defined functions in TCL
Supposing if i have a function called runtest {ip_address test_time},
How to describe what the test or procedure is about in the TCL_shell?
How can i specify the information to the user, if he types in function_name --help in the TCL shell the user should be able to know what the function does and what exactly are the parameters.
how can i do this?
While it is true that it is not part of the language, it is fairly easy to implement something that adds this functionality to pre-existing functions. The only caveat being that the function then cannot take the string --help as a valid argument since that argument will trigger the feature.
Here's one simple implementation:
# Lets call the feature "document". As in, add documentation:
proc document {procname text} {
rename $procname __$procname
proc $procname args [string map [list %TEXT% $text %PROC% $procname] {
if {$args == "--help"} {
puts {%TEXT%}
} else {
set script [linsert $args 0 __%PROC%]
return [uplevel 1 $script]
}
}]
}
What this does is to override the function (by renaming and then declaring another function of the same name) and see if the function is called with the argument --help. If it is it prints the documentation otherwise it executes the original function. Just be careful not to call this twice on the same function (it can be modified for it to work though).
So you can do things like:
proc foo {} {puts 2}
document foo {Prints the number 2.}
Now if you call:
foo --help
and it would output:
Prints the number 2.
You don't have to touch the existing procedures:
proc help {procname} {
puts $::helptext($procname)
}
proc addhelp {procname text} {
set ::helptext($procname) $text
}
addhelp foo "this is the help text for procedure foo"
help foo
Without redefining the proc command, you cannot. Ie, that functionality is not built into the language, but would be possible to add if you so wished.
I will note that adding the capability yourself, while possible, is probably beyond the difficulty where it's worth doing, especially for someone that isn't intimately familiar with the language. That being said, you can check the tclers wiki for a preexisting implementation.
I tend to prefer to put help in a separate file (e.g., as HTML) which lets me browse it in another window. There are so many ways you can do that while still keeping the docs with the code, such as through doxygen.
There are also a number of ways of doing interactive documentation, several of which are described in the Tcler's Wiki; it would seem to me that some of the techniques mentioned on that page (together with #slebetman's answer) would give you what you wanted. (I think it would be easier to have a separate help command as that would avoid having the help syntax interfere with the command, but that's your call.)