I am writing a utility script to fix a Verilog code using sed
. What I'm trying to do is converting code as follows
input file
my_instance_name
(
.port1 (port1 )
.port2 (port2 ) // comment for port line 2
.port3 (port3 )
.port4 (port4 ) // comment for port line 4
);
What I want
my_instance_name
(
.port1 (port1 ),
.port2 (port2 ), // comment for port line 2
.port3 (port3 ),
.port4 (port4 ) // comment for port line 4
);
The points are
- Append comma just after the closing parenthesis of each 'port' lines
- Do not append comma for the last 'port'
- Preserve the comments if exist (comment starts with double-slash like C)
- Whitespaces (except the line breaks) are not important. it is only for human readability.
- There can be multiple linebreaks after the last port line
If the sed
script becomes too complicated, it is also OK to just prepend the comma just before the dot, except the first port line. i.e, the following code is OK although it is not pretty.
my_instance_name
(
.port1 (port1 )
,.port2 (port2 ) // comment for port line 2
,.port3 (port3 )
,.port4 (port4 ) // comment for port line 4
);
I could only manage appending commas to all 'port' lines via the following two line
sed -i /^\s*my_instance_name.*/,/);/{s/^\(\s*\..*)\)\(\s*\/\/.*\)/\1,\2/} my_verilog.txt
sed -i /^\s*my_instance_name.*/,/);/{s/^\(\s*\..*)\)\s*\$/\1,/} my_verilog.txt
It detects the code scope from "my_instance_name" to ");". The first line insert comma between the closing parenthesis and comment. The second line is for port lines without comments.
But I have no idea how to exclude the last line.
CodePudding user response:
This would probably be significantly easier in Awk.
awk '/my_instance_name/ { p=1 }
p && /\);/ { if (prev) print prev; print; p=0; next }
p && /port/ { if (prev) { sub(/\)/, "),", prev); print prev; }
prev = $0; next } 1' my_verilog.txt
Standard Awk does not have an option like the -i
option of sed
; this will simply print the output to standard output, which you can redirect to a temporary file and then move back on top of the original. Or if you have GNU Awk, it has -i inplace
which works like sed
's -i
.
CodePudding user response:
Using GNU sed
$ sed -Ezi.bak 's/\([^)]*\)/&,/g;s/(\([^)]*\)),([^\n]*\n\);)/\1\2/g' input_file
my_instance_name
(
.port1 (port1 ),
.port2 (port2 ), // comment for port line 2
.port3 (port3 ),
.port4 (port4 ) // comment for port line 4
);
CodePudding user response:
You may use this gnu-awk
solution using custom RS
to split shown block on header and footer:
awk -i inplace -v RS='my_instance_name\\s*\\(\\s*' '
NF {
$0 = gensub(/(\n\s*)([^)])/, "\\1,\\2", "g")
}
{ORS=RT} 1' file
cat file
my_instance_name
(
.port1 (port1 )
,.port2 (port2 ) // comment for port line 2
,.port3 (port3 )
,.port4 (port4 ) // comment for port line 4
);
Note that due to use of -i inplace
changes will be saved inline in same input file.
Breakdown:
-v RS='my_instance_name\\s*\\(\\s*'
splits input data bymy_instance_name
followed by 0 or more whitespaces then(
and again followed by 0 or more whitespaces.gensub
function matches newline followed by 0 or more whitespaces and captures in a group #1. It matches any character that's not)
(to avoid matching);
line) and captures in a group #2. In replacement we insert a comma between these 2 values.{ORS=RT}
populatesORS
same as the text matched byRS
pattern i.e.RT
1
prints all records
For appending commas in the end use similar awk:
awk -v RS='my_instance_name\\s*\\(\\s*' '
NF {
$0 = gensub(/\)([^\n]*\n[^)])/, "),\\1", "g")
}
{ORS=RT} 1' file
my_instance_name
(
.port1 (port1 ),
.port2 (port2 ), // comment for port line 2
.port3 (port3 ),
.port4 (port4 ) // comment for port line 4
);