tcl - write out new lines/values between specified points (modification to Brad Lanam's code)

StackOverflow https://stackoverflow.com/questions/23161375

質問

Context: I've received some remarkable help from Brad Lanam and Glenn Jackman who each developed scripts to convert 25x5 values to 5x5 values (in my .lib file - see below) based on a user defined value. Truly fantastic coding. Very thankful for their help.

Reference: tcl text processing - rearrange values in rows and columns based on user defined value

QUESTION: I'm attempting to tweak Brad's script (does not currently fully do what Glenn's script does) to overwrite 25x5 lines with the 5x5 lines I've achieved using his script (basically write out a new file with modified values at specified points). Here are the 5x5 lines

values ( \
"1.1, 2.1, 3.1, 4.1, 5.1", \
"6.1, 7.1, 8.1, 9.1, 10.1", \
"11.1, 12.1, 13.1, 14.1, 15.1", \
"16.1, 17.1, 18.1, 19.1, 20.1", \
"21.1, 22.1, 23.1, 24.1, 25.1", \
);

Code that was used in "PART I HAVE TROUBLE WITH" to achieve this

puts "values ( \\"
foreach elem $lines {
puts [format "\"%s\", \\" [join $elem {, }]] # Reference: Glenn Jackman
}
puts ");"

Now all I need to is to overwrite the above values in the 'values' portion of the following .lib file (library file format - used in chip design)

Snippet of .lib file (actual file has tons of lines)

timing () {
    related_pin : "clk";  
    timing_type : setup_rising;  
    rise_constraint (constraint_template_5X5) {
      index_1 ("0.01, 0.05, 0.12, 0.2, 0.4");  
      index_2 ("0.005, 0.025, 0.06, 0.1, 0.3");  
      index_3 ("0.084, 0.84, 3.36, 8.4, 13.44") ;
      values ( \  
        "1.1, 1.2, 1.3, 1.4, 1.5", \
        "2.1, 2.2, 2.3, 2.4, 2.5", \  
        "3.1, 3.2, 3.3, 3.4, 3.5", \
        "4.1, 4.2, 4.3, 4.4, 4.5", \
        "5.1, 5.2, 5.3, 5.4, 5.5", \
        "6.1, 6.2, 6.3, 6.4, 6.5", \  
        "7.1 ,7.2, 7.3, 7.4, 7.5", \
        "8.1, 8.2, 8.3, 8.4, 8.5", \  
        "9.1, 9.2, 9.3, 9.4, 9.5", \
        "10.1,10.2,10.3,10.4,10.5", \  
        "11.1,11.2,11.3,11.4,11.5", \
        "12.1,12.2,12.3,12.4,12.5", \  
        "13.1,13.2,13.3,13.4,13.5", \  
        "14.1,14.2,14.3,14.4,14.5", \
        "15.1,15.2,15.3,15.4,15.5", \
        "16.1,16.2,16.3,16.4,16.5", \  
        "17.1,17.2,17.3,17.4,17.5", \  
        "18.1,18.2,18.3,18.4,18.5", \  
        "19.1,19.2,19.3,19.4,19.5", \
        "20.1,20.2,20.3,20.4,20.5", \  
        "21.1,21.2,21.3,21.4,21.5", \  
        "22.1,22.2,22.3,22.4,22.5", \  
        "23.1,23.2,23.3,23.4,23.5", \  
        "24.1,24.2,24.3,24.4,24.5", \
        "25.1,25.2,25.3,25.4,25.5", \  
      );
    }
    }

I'm writing out the file as I traverse each line but can't seem to figure out how to exactly insert my 5x5 values right after values ( \ and end right before my ending syntaxes ) and } (and other lines of code in my original .lib file). This effectively 'overwrites' the 25x5 lines. My main issue is WHAT is the right end point (can't be just ); since there are other lines below it)

Here is my attempt so far using Brad's code (please skip to if { [regexp {values} )

set inFile [open "C:/Tcl/official/draft.lib" r]
set outFile [open "C:/Tcl/official/my_modified.lib" w] 

set inval false
set foundValues 0 # these are FLAGS for later
set extract1 0
set extract2 0

Text processing done by Brad (assume this is all correct - I got the right values)

while { [gets $inFile line] >= 0 } {
if { [regexp {\);} $line] } {
    if { $inval } {
        puts $outFile "End of values..." 
    }
    set inval false
}
  if { [regexp {setup_rising;} $line] } { set extract1 1 }
  if { $extract1 } {
  if { [regexp {rise_constraint} $line] } { set extract2 1 }
  if { $extract2 } {
  if { [regexp {index_(\d+)} $line all idx] } {
    regsub {^[^"]*"} $line {} d
    regsub {".*} $d {} d
    regsub -all {,} $d {} d
    dict set risedata constraints indexes $idx $d
  }
  }
  }
  if { $inval } {
    regsub {^[^"]*"} $line {} d
    regsub {".*} $d {} d
    regsub -all {[ ,]+} $d { } d
    set row [expr {$rcount / 5}]
    set column [expr {$rcount % 5}]
    set i 0
    foreach {v} [split $d { }] {
      set c [lindex [dict get $risedata constraints indexes 3] $i]
      dict set risedata constraints constraint $c $row $column $v
      incr i
    }
    incr rcount
  }

PART I HAVE TROUBLE WITH

 if { [regexp {values} $line] } {
    set inval true
    set row 0
    set rcount 0
  }

set c 0.084
set id0 "[dict values [dict get $risedata constraints constraint $c 0]]"
set id1 "[dict values [dict get $risedata constraints constraint $c 1]]"
set id2 "[dict values [dict get $risedata constraints constraint $c 2]]"
set id3 "[dict values [dict get $risedata constraints constraint $c 3]]"
set id4 "[dict values [dict get $risedata constraints constraint $c 4]]"

set lines [list $id0 $id1 $id2 $id3 $id4]

puts "values ( \\"
foreach elem $lines {
puts [format "\"%s\", \\" [join $elem {, }]]
}
puts ");"

############################# INSERTION POINT #########################################

if { [regexp {values} $line] } # find 'values' keyword in .lib as start point
{
   set foundValues 1 
}

if {$foundValues}
{
   if { [regexp {) *;} $line] } # find ' ); ' (end syntax) in .lib as end point 
   {
   set foundValues 0
   foreach elem $lines 
   {
   puts [format "\"%s\", \\" [join $elem {, }]]
   puts $outFile $elem
   }
   }
}
if {!$foundValues}
{
puts $outFile $line
}

close $inFile
close $outFile

Will GREATLY appreciate help in this! I'm almost done with this project thanks to the generous help of the stackoverflow community. I will be sure to cite whoever helps/advises.

役に立ちましたか?

解決

One obvious problem you've got that occurs just at the point where you say you've got a problem…

Tcl very strongly favours One True Brace style coding. Much more so than most languages. This is because newline is normally an end-of-command marker (and braces are really a kind of nestable no-substitution quoting). It also only treats # as indicating a comment at the point when a command name is expected (it's still a comment though). Thus, your

if { [regexp {values} $line] } # find 'values' keyword in .lib as start point
{
   set foundValues 1 
}

Is going to not work. It passes the #, find, 'values', keyword, … words as arguments to if; the # wasn't found as the first character in a command-name position, so it's an ordinary character. I'm pretty sure that's not what you want. What you really want is this:

if { [regexp {values} $line] } { # find 'values' keyword in .lib as start point
   set foundValues 1 
}

I've moved the brace to the previous line, before the start of the comment. Mind you, I'd normally write this instead:

if {[regexp {values} $line]} {
    # find 'values' keyword in .lib as start point
    set foundValues 1 
}

Putting the comment on the next line (or the preceding line) is clearer IMHO.

You could also write:

if { [regexp {values} $line] } \
{
    # find 'values' keyword in .lib as start point
    set foundValues 1 
}

But that's clunky! I think it's best to try to avoid backslashes where possible.


Further down, you write:

if {$foundValues}
{
   if { [regexp {) *;} $line] } # find ' ); ' (end syntax) in .lib as end point 
   {

That's the identical problem to above. Try this:

if {$foundValues} {
    # find ' ); ' (end syntax) in .lib as end point 
    if { [regexp {) *;} $line] } {

As a general point, if you've got a complicated regular expression, it can be easier to put the RE in a global variable and just use that; it's a simple way to get “named” regular expressions. I personally prefer to use all-caps variable names for such things, but that's entirely your call.

set DETECT_END_SYNTAX {) *;}

if {$foundValues} {
    if {[regexp $DETECT_END_SYNTAX $line]} {

Like that, it's a bit clearer what the intent of the RE matching is (as opposed to just what string it's technically trying to match). In this specific case, it's not too important, but it helps a lot with anything more complicated.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top