how to set text indices via a variable - tcl

i'm trying to implement a simple line highlighting mechanism in my tcl/tk text widget.
For this I would like to assign all characters marked with one tag to another tag.
as in
.window.text insert end "one line\n" line1
.window.text insert end "a chunk spanning\nmultiple lines" line2
.window.text insert end "thats all\n" line3
# get all text that is tagged as 'line2'
set selected [ .window.text tag ranges line2 ]
# and apply the 'highlighed' tag to it:
.window.text tag add highlighted $selected
Unfortunately this does notwork, as it gives me
bad text index "2.0 4.0"
Using the indices literally works fine:
.window.text tag add highlighted 2.0 4.0
But is not what i want. (I don't know anything about the tagged chunks apart from their tag)
So it seems that I cannot store the list of indices in a variable and use that with tag add (or tag remove for that matter).
Any hints how I can add a tag to an already tagged text?

Solution (in Tcl 8.5 and later):
.window.text tag add highlighted {*}$selected
If command A has given you a list of items to feed to command B, but command B expects each item to appear as an argument in its invocation, the list of items needs to be spliced, or expanded into separate arguments. In Tcl 8.5, this was facilitated by introducing a new syntactic rule that allowed the number of arguments provided to a command to be increased by expanding one of the existing arguments.
To borrow an example, the destroy ?window window ...? command cannot work with the list of windows returned by winfo children ., since each window path needs to be a separate argument. Writing
destroy [winfo children .]
would be evaluated as (say) destroy {.foo .bar .baz}, which won't work. However, using the new expansion prefix {*}
destroy {*}[winfo children .]
the line will be evaluated as destroy .foo .bar .baz, which will work.
One way to understand it is by thinking of the invocation as a list consisting of the command name and the arguments, and that the {*} is an instruction to splice the value of the following argument into that list at that point in the list.
Documentation: {*}

Related

Use of brackets and space character in Tcl

I am still confused about the usage of the bracket i.e () [] and {} use in Tcl. I always get caught out using the wrong bracket, having missed brackets when it was required to use them or having used too many of them. Besides this, I am also getting confused by Tcl giving me different result depending on presence or absence of space character (in math expression) and also if I have used more than one space character in succession.
Can someone please give me the basic rules that I must keep in mind to get out of this mess. Brackets have always been simple to use in C and some other languages but here they are totally different.
At the level you're looking at, Tcl is very different to any other language you've ever worked with. The heart of Tcl is defined by the Tcl(n) manual page, which states that (among other things):
Whitespace separates words. Every command takes its arguments as a sequence of words. Newlines and semicolons separate command calls; they're totally equivalent, but good style is to use a newline instead of a semicolon.
{braces} are used mainly for quoting text so that it is passed to commands with no substitutions or word separation performed on it. They nest properly. Braces are also used after $ to do variable substitution in a few cases: that's a rare use.
"double quotes" are used for quoting text so that it is passed to commands with substitutions applied, but no word separation.
[brackets] are a command substitution. They are replaced with the result of running the script inside the bracket. The script is usually a single command.
(parentheses) only have one base language use: for (associative) array elements. Thus, $a(b) is a variable substitution that will use the value of the b element in the a array.
The rest of what people call Tcl is really just a standard library, a set of commands to get you started. Some are fundamental. For example:
if is a conditional command, evaluating a branch (a script) if a condition is true. In order for this to be meaningful, the branch has to be not evaluated until the condition has been evaluated and tested; that pretty much requires putting it in braces.
while is a looping command, and not only do you want to brace its body (that's probably going to be evaluated over and over) but you also want to put the condition expression in braces as well as you definitely want that to be reevaluated each time round the loop.
proc is a command that makes your own custom commands. The body of the procedure definitely is something you want to evaluate later; it goes in braces.
expr is a general expression evaluation command. Under all normal circumstances, you'll want to put its expression in braces so that the code can be compiled and won't have double substitution problems. Note that expressions often make heavy use of parentheses: they have additional meanings in expression syntax. In particular, apart from being array element lookups, they're also used for function calls and grouping.
Note that if and while also use that same expression evaluation engine. They just use the result of the expression to decide what to do.
Scoping is a matter for commands to decide. The usual commands for dealing with introducing a scope are proc and namespace eval. This is nothing like C, C++, Java, C#, or Javascript; they have different rules. Variables are local to their procedure unless you explicitly say otherwise.
The community practice is to do calls like this:
if { $foo(bar) > (17 + $grill) * 7 } {
# This is a comment; it lasts to the end of the line
puts "the foobar $foo(bar) is too large"
set foo(bar) [ComputeSmallerValue $grill]
}
That is, barewords (if and puts) are unquoted, expressions and inner scripts are brace-quoted, parentheses are used where meaningful but most for arrays and expressions, whitespace separates all words, inner scripts are indented (usually by 4) for clarity (it doesn't have semantic meaning, but it sure helps with reading), and “blocks” use egyptian braces so that you don't have to add backslashes all over the place.
You don't have to follow these rules (they're guidelines, not the law) but they make your life easier if you do. Sometimes you do need to break the rules, but then you should know to be careful.
You cannot compare Tcl to C. In C, {} defines scope. In Tcl, {} is a grouping operator.
In Tcl, {} may group a string:
{hello world}
Or a list:
{a b c d e f g h}
Or a script:
{
puts -nonewline {hello }
puts world\n
}
Every command is simply a series of groups (which may be a word, a list,
an expression or a script):
{if} {true} { puts "hello\n" }
Of course, you don't need to put braces around every word,
but you do need braces to enclose a script:
if true { puts hello\n }
Generally, for the if statement, not bracing the expression is a bad idea,
so this is better:
if { true } { puts hello\n }
This simple rule creates Tcl's remarkably simple syntax. Every command is simply
a series of groups, whether a word, an expression, a list or script:
if expr script
while expr script
proc name argument-list script
puts string
for initialization condition nextloop script
The one important thing to remember is whenever an expression is wanted, it
should be enclosed within braces in order to prevent early substitution. e.g.:
set i 0
while { $i < 10 } {
incr i
}
The square brackets, [], are replaced with the output of a command enclosed
by the square brackets:
set output [expr {2**5}]
Parentheses are used within expressions as usual:
set output [expr {(2**5)+2}]
And for arrays:
set i 0
while { $i < 5 } {
set output($i) [expr {2**$i}]
incr i
}
parray output

In ack, can I define a type that is restricted by both extension and first line (two filtertypes)?

If I understand correctly (from Defining your own types in ack --man), when I use --type-set in .ackrc (or at the command line) I can only use one "filtertype". In my case, I'd like to match files with a particular extension that also have a pattern present in the first line. I imagined it would look like this (perhaps without the --type-set in the middle:
--type-set
mytype:firstlinematch:my.regex.pattern...
--type-set
mytype:ext:myextension
If I do that, though, the linematch is ignored, because the type is replaced by the 2nd entry.*
I know multiple entries are allowed for --type-add, but that means this OR that condition must be true, rather than restricting to when both are true.
Is there a way to narrow using two filter types for a case like this?
(If I'm right about that, then a warning would help when a definition is clobbered later in .ackrc.)
Using --type-add as the second definition is not restrictive. If one defines foo file types as follows:
ack --type-set foo:.txt --foo bar
This finds all txt files with bar somewhere in the file. If one adds to this file type set a first line match:
ack --type-set foo:.txt --type-add foo:firstlinematch:/pr.*t/ --foo bar
This redefines the file type as all txt files AND any file containing the regex /pr.*t/ in its first line. Accordingly, it finds all bars in that set of files. In other words, type-add is adds to the definition of files. The man file explicitly says:
"Type specifications can be repeated and are ORed together."

Extract the value of a variable that matches a pattern in TCL

I have a list called parameters and the content of this list can be different but it will look something like:
var1=2;
var2=2'h2;
var3=2'h0;
....
This list comes from reading a file and done some preformating already. I just want to grab the value of var1 and store it in a variable. Eg whatever is in between '=' sign and ';' sign but only for var1 (in this case number 2). Equally I can remove all the lines that are not matching 'var1'.
Assuming your parameters list is already set, you can do something like:
foreach item $parameters {
if {[regexp "var1\\s*=\\s*(\\w+);" $item wholeMatch myVal]} {
break
}
}
puts "value is '$myVal'"
The regular expression I use allows for optional spaces before and after the equal sign. Take a look at Tcl's regex syntax and adjust as necessary.
It might be easier to just do a regex search through your whole file using, rather than parsing your file into a list. But again, take a look at Tcl's documentation.

Printing variables including functions from Makefile and/or variable introspection

If you iterate over .VARIABLES and print each, any true variable can be printed correctly with the following rule:
print_variables: $(foreach V,$(.VARIABLES),print-$(V)) .phony_explicit
print-%: .phony_explicit; #echo "$* = \"$($*)\""
.PHONY: .phony_explicit ...
A 0- or 1-line function will still work, but any more will result in Syntax error: Unterminated quote string. Just one multiline function will break the entire print_variables rule. As a workaround, I have added ;\ to each line in my function definitions, but that won't fix existing multiline functions (either via includes from this makefile or via other makefiles including this one.) What can I do? Is there a container of just function variables, or a way to test if a variable is a function definition?
A simple minimal example would be easier to understand; this has nothing to do with .VARIABLES, pattern rules, etc. (and I'm not sure what the point of the .phony_explicit prereq is..)
define F
foo
bar
endef
print: ; echo "F = $(F)"
will show the problem:
echo "F = foo
/bin/sh: 1: Syntax error: Unterminated quoted string
This is because when make sees a variable that contains newlines in a recipe, it assumes that the newlines mean you want the lines of the variable to become lines in the recipe.
First in general you should use single-quotes around strings you send to the shell, unless you need the shell to expand them; it won't help in this situation but in general it's much safer.
There's no way to undo that, really. You have a number of options.
The first is to not use echo but instead use the make function info:
print-F: ; $(info F = "$(F)")
yields:
F = "foo
bar"
Another option is to use subst to replace the newlines with some other value. The new value cannot itself contain explicit newlines, but you can ask the shell to print a newline for you:
# Create a variable containing a single newline
# Note this must contain TWO newlines!
define NL
endef
print-F: printf 'F = "$(subst %,%%,$(subst $(NL),\n,$(F))"\n'
Yields:
printf 'F = "foo\nbar"\n'
F = "foo
bar"
One final option is to convert your makefile to use the .ONESHELL feature, but I assume that's a step too far just to get this debugging output available :).

TCL: How to create and use variable with names containing curly braces?

Some variable names in my program are constructed from inputs of user so they may contain any symbols. Some symbols are treated as special by interpreter e.g. $,#, .... The problems concerning that symbols were solved by adding open brace on the beginning of constructed variable name and close brace at the end of it. But now another problem arises when the name of variable contains curly closing brace.
set "a{}" text
puts $a{}
puts ${a{}}
None of tow puts work here. How can I print the value of variable a{} and is there any known method for dealing with special symbols in TCL?
From the manual:
Note that variables may contain character sequences other than those listed above, but in that case other mechanisms must be used to access them (e.g., via the set command's single-argument form).
Use set
puts [set "a{}"]
the $ way is restricted, set is not