I have a following long tcl list:
>> puts $res
{ banner { } } { report { { name {{ columns { {head1} {head2} {head3} {head4} {pin} } } { { row_1 { { } {-0.008} { } {r1} {hier1} } } { row_2 { {1000} {-0.009} {-0.000} {r2} {hier2} } } } } } } }
I need to access the value 1000 in row_2.
Though I am able to get the value using the following, I was wondering if there was a better way to do this rather than specifying lindex so many times.
>> set val [lindex [lindex [lindex [lindex [lindex [lindex [lindex [lindex $res 1] 1] 0] 1] 1] 1] 1] 0]
>> 1000
The lindex command can take multiple indexes, so you can do:
set val [lindex $res 1 1 0 1 1 1 1 0]
Related
I'm reading a large file and I'm only interested in small part of the file as shown below.
TC.0.Type = Bob 1
TC.1.Type = Mark 1
TC.2.Type =
TC.3.Type =
TC.4.Type = Fred 1
TC.5.Type =
TC.6.Type =
TC.7.Type =
TC.8.Type =
TC.9.Type = Fred 1
I've created a variable that is now holds this information
data = "{Bob 1} {Mark 1} {} {} {Fred 1} {} {} {} {} {Fred 1}"
TC is always between 0-9, so length is known.
What I would like to do is:
1) If there are multiple instances of "Fred 1" and delete it.
2) Find the first empty slot and determine the index.
Question 1)
Is it typical to get brackets when using lappend? I expected this to be only in the case of empty fields
set data ""
for {set j 0} {$j < 10} {incr j} {
lappend data $fromfile
}
puts "Data in list = $data"
Question 2) I've even tried using regexp to pick out empty but don't seem to be successful.
Find empty field {}
set j 0
for {set i 0} {$i < $ldata} {incr i} {
# set nline [split $data "\s"]
# puts "data ($i) = $nline"
if {[regexp {\{.*\}} $data]} {
puts " Found {}"
incr j
puts "j = $j"
}
}
Find field with name e.g. Bob 1
for {set i 0} {$i < $ldata} {incr i} {
if {[regexp {\{.*[a-zA-Z0-9]\}} $data]} {
puts " Found something with names"
}
}
Would appreciate if someone can advice and guide.
The lsearch command is going to be tremendously useful for what you are doing, especially with the -all option.
set data "{Bob 1} {Mark 1} {} {} {Fred 1} {} {} {} {} {Fred 1}"
puts [lsearch -all -exact $data "Fred 1"]
# ==> 4 9
We can also use it to remove specific values:
puts [lsearch -all -inline -exact -not $data "Fred 1"]
# ==> {Bob 1} {Mark 1} {} {} {} {} {} {}
To find the first empty slot, we just do:
puts [lsearch -exact $data ""]
# ==> 2
We most definitely would expect braces back from list operations; that's how empty list elements are expressed.
I am a beginner in TCL scripting. I am working on parsing the data from a Verilog file to a xls file as below.
Input Verilog file contains following data:
Inferred components
Operator Signedness Inputs Outputs CellArea Line Col Filename
=====================================================================================================
apn
u_apn_ttp_logic
abc
u_apn_wgt_op_rd_u_apn_sca_u_part_sel1_sl_69_33
module:shift_right_vlog_unsigned_4662_7709
very_fast/barrel >> x 25x5 25 223.02 69 33 part_select.v
=====================================================================================================
apn
u_apn_ttp_logic
u_apn_wgt_op_rd_u_apn_scale_u_part_sel1_sub00283545
module:sub_signed_4513_5538
very_fast - signed 11x24 25 152.80 0 0 a
=====================================================================================================
(This is a long file…)
The parsing will end after the last section:
=====================================================================================================
apn
u_apn_start_ctrl_final_adder_add_212_41
module:add_unsigned_carry_882
very_fast + unsigned 32x32x1 32 120.39 212 41 feu_start_ctrl.v
=====================================================================================================
I want to extract the data as below , consider first section
Top name=apn
Instance=u_apn_ttp_logic/abc/u_apn_wgt_op_rd_u_feu_scale_u_part_select1_srl_69_33
Module = shift_right_vlog_unsigned_4662_7709
Architecture=very_fast/barrel
Operator = >>
Sign=x
Input Size = 25x5
Output = 25
Area = 223.02
Column = 69
Row = 33
File Name = part_select.v
I am stucked at a point while implementing this.
below is my approach for the same:
set fd "[open "path_data.v" r]"
set flag 0
while {[gets $fd line] !=-1} {
if {[regexp "\===*" $line]} {
while {[gets $fd line] >= 0} {
append data "$line "
if {[regexp "\====*" $line]} {
break
} }
set topname [lindex $data 0]
regexp {(^[a-z]*) (.*) (.*module)} $data match topname instance sub3
puts "top name :$topname "
puts "instance: $instance"
}
close $fd
I am able to output topname and instance name only, not other values
Also please help me extract these values.
With this sort of task, it really helps if you put parts of the task into procedures that do just a simpler bit. For example, suppose we were to split the processing of a single section into its own procedure. Since it is only going to do one section (presumably a lot shorter than the overall file), it can work on a string (or list of strings) instead of having to process by lines, which will make things greatly easier to comprehend.
For example, it would handle just this input text:
apn
u_apn_ttp_logic
abc
u_apn_wgt_op_rd_u_apn_sca_u_part_sel1_sl_69_33
module:shift_right_vlog_unsigned_4662_7709
very_fast/barrel >> x 25x5 25 223.02 69 33 part_select.v
We might handle that like this:
proc processSection {sectionText} {
set top ""
set instance ""
set module ""
set other {}
foreach line [split $sectionText "\n"] {
if {$top eq ""} {
set top [string trim $line]
continue
}
if {$module eq ""} {
# This regular expression matches lines starting with “module:” and
# extracts the rest of the line
if {[regexp {^module:(.*)} $line -> tail]} {
set module [string trim $tail]
} else {
append instance [string trim $line] "/"
}
continue
}
# This regular expression matches a sequence of non-space characters, and
# the -all -inline options make regexp return a list of all such matches.
lappend other {*}[regexp -all -inline {\S+} $line]
}
# Remember to remove trailing “/” character of the instance
set instance [string trimright $instance /]
# Note that this splits apart the list in $other
outputSectionInfo $top $instance $module {*}$other
}
We also need something to produce the output. I've split it into its own procedure as it is often nice to keep parsing separate from output.
proc outputSectionInfo {top instance module arch op sgn in out area col row file} {
# Output the variables
foreach {label varname} {
"Top name" top
"Instance" instance
"Module" module
"Architecture" arch
"Operator" op
"Sign" sgn
"Input Size" in
"Output" out
"Area" area
"Column" col
"Row" row
"File Name" file
} {
# Normally, best to avoid using variables whose names are in variables, but
# for printing out like this, this is actually really convenient.
puts "$label = [set $varname]"
}
}
Now that we've got a section handler and output generator (and you can verify for yourself that these do sensible things, as they're quite a bit simpler than what you were trying to do in one go), we just need to feed the sections from the file into it, skipping over the header. The code does that, and just does that.
set fd [open "path_data.v"]
set flag 0
while {[gets $fd line] >= 0} {
if {[regexp {^=====+$} $line]} {
if {$flag} {
processSection [string trimright $accumulator "\n"]
}
set flag 1
set accumulator ""
} else {
append accumulator $line "\n"
}
}
close $fd
Your immediate problem was that your code was closing the channel too early, but that was in turn caused by your confusion over indentation, and that was in turn caused by you trying to do too much in one place. Splitting things up to make the code more comprehensible is the fix for this sort of issue, as it makes it much easier to tell that the code is definitely correct (or definitely wrong).
I worked on above script, and here is my code for the same. This code won't work if there is an empty line after "========" line
But I would like to explore your code as it is well organised.
set fd "[open "path_data.v" r]"
set fd1 "[open ./data_path_rpt.xls w+]"
puts $fd1 "Top Name\tInstance\tModule\tArchitecture\tOperator\tSign\tInput Size\tOutput size\tArea\tLine number\tColumn number\tFile Name"
set data {}
while {[gets $fd line] !=-1} {
if {[regexp "\===*" $line]} {
set data {}; set flag 0
while {[gets $fd line] >= 0} { append data "$line "
if {[regexp {[a-z]\.v} $line]} { set flag 1;break} }
puts "$data\n\n"
if {$flag} {
set topname [lindex $data 0]
regexp {(^[a-z]*) (.*) (.*module\:)(.*)} $data match topname instance sub3 sub4
set inst_name {} ;
foreach txt $instance {
append inst_name "$txt\/"
}
set instance [string trim $inst_name "\/"]
set module [lindex $sub4 0]
set architecture [lindex $sub4 1]
set operator [lindex $sub4 2]
set sign [lindex $sub4 3]
set input_size [lindex $sub4 4]
set output_size [lindex $sub4 5]
set area [lindex $sub4 6]
set linenum [lindex $sub4 7]
set col_num [lindex $sub4 8]
set file_name [lindex $sub4 9]
puts $fd1 "$topname\t$instance\t$module\t$architecture\t$operator\t$sign\t$input_size\t$output_size\t$area\t$linenum\t$col_num\t$file_name"
set data {} ; set flag 0
}}
}
close $fd1
close $fd
I need to be able tp read in file by line and find a set of strings (from another file) that starts with the strings plus set of characters like ({ somedata }) then append the line with a ## to that block.
Here is what I have so far:
set mydir <path to my dir>
#file name file.txt with content:
~>cat file.txt
Strng00 {
some data
}
Strng021 {
some data
}
Strng02 {
some data
}
Strng03 {
some data
}
Strng_dt {
some data
}
Strng01 {
some data
}
Strng02 {
some data
}
Strng03 {
some data
}
Strng_dt {
some data
}
Strng42 {
some data
}
Strng412
--
set list { Strng01 Strng02 Strng03 Strng_dt Strng42 } # May be read in the list from another file which needs to be matched
set fileIn [lindex $argv 0]
set fileInId [open $mydir/file.txt r]
set appendLine 0
foreach item $list {
set j 0
while {[gets $fileInId line ] != -1} {
if [regexp -all -line $item $line] { set appendLine 1 }
if $appendLine {
if [regexp {^\s*\}\s*$} $line] { set appendLine 0 }
set line "## $line"
}
puts $line
}
set j 1
}
The result only shows the first entry of the list:
Strng00
Strng021
Strng02
Strng03
Strng_dt
##Strng01 {
## some data
##}
Strng02
Strng03
Strng_dt
Strng42
Strng412
I'd like to get ## after each of the items listed..
Thanks in advance.`
Here's another take:
while {[gets $fh line] != -1} {
set first [lindex [split [string trimleft $line]] 0]
puts [format "%s%s" [expr {$first in $list ? "##" : ""}] $line]
}
That finds the first word in the line, and checks if it is an element in $list.
Does this solve your problem?
set list { Strng01 Strng02 Strng03 Strng_dt Strng42 }
set fileInId [open $mydir/file.txt r]
while {[gets $fileInId line ] != -1} {
if {[regexp -line [join $list |] $line]} {
set line "## $line"
}
puts $line
}
edit: dealing with the updated specification.
This is one way to do it; it takes advantage of the fact that the lines in the file match Tcl command invocation syntax.
proc unknown {cmd args} {
set list { Strng01 Strng02 Strng03 Strng_dt Strng42 }
if {$cmd in $list} {
foreach line [split [info level 0] \n] {
puts "## $line"
}
} else {
puts [info level 0]
}
}
source file.txt
It works like this: using source on the file means that the Tcl interpreter tries to use the keywords on each line as command names, with the { ... } parts as arguments. Since the keywords aren't existing commands, the interpreter hands the invocations over to the unknown command, which we have redefined to recognize the relevant keywords and print the complete invocation ([info level 0]) with a ## prefix if they are in the list, or else just print the invocation as it is.
Documentation: chan, foreach, if, info, join, open, proc, puts, regexp, set, source, split, unknown, while
contents of vlan_outcome
show int description | include Vl[0-9]+
Vl1 admin down down
Vl150 up up
how can i put the follwing regex into variables?
var1 = Vl1 var2 = admin down var3 = down
war2 = Vl150 war3 = up war3 = up
to be more correct i want the assignment to happen(var1 = Vl1 or war1 = Vl150) only when their status shows up for both columns
so far i am trying and trying with failure :
foreach element [ split $vlan_outcome "\n" ] {
#regexp (Vl\[0-9\]+)(\[\[:blank:\]\]+)(.*)(\[\[:blank:\]\]+)(.*) ignore vlan state1 state2
puts $element
}
Your question should be how to Splitting a String Into Words.
So if you loop the data line by line, just split the line into words with regex as followings:
set data [regexp -inline -all -- {\S+} $line]
if {[lindex $data 1] == "up" && [lindex $data 2] == "up"} {
set value [lindex $data 0]
}
I have this kind of list :
{ A D C } { D S D } { A S D } { Y D D }
I want to list all the index that have duplicates in the same index of the sublist.
For example if I want to serach every "D" at index 2 in sublist, I want to know the index of the list (here 0 and 3)
here is the code :
proc findElement {lst idx value} {
set i 0
foreach sublist $lst {
if {[string equal [lindex $sublist $idx] $value]} {
return $i
}
incr i
}
return -1
}
When i call it findElement $toto 1 D
it returns only 0 !
Why ?
Because you have a return statement when it finds a match when $i = 0.
Try the following which instead returns a list of all the matching indexes
proc findElement {lst idx value} {
set i 0
set return_list [list]
foreach sublist $lst {
puts "i=$i sublist=$sublist"
if {[string equal [lindex $sublist $idx] $value]} {
puts "Found $i"
lappend return_list $i
}
incr i
}
return $return_list
}
You can do a shorter and faster version with lsearch -all -exact -index.
proc findElement {lst idx value} {
return [lsearch -all -exact -index $idx $lst $value]
}