1c3b45ac39877737a3a03335ed7b01eb57a6dfe6
7 @ARGV == 2 or die "Need 2 arguments";
9 my ($header_file,$source_file) = @ARGV;
12 # Regular expressions for nested brackets (uses perl 5.10 features)
13 my ($nested_angle_brackets) = qr/(<(?:[^<>]++|(?1))*>)/;
14 my ($nested_curly_brackets) = qr/({(?:[^{}]++|(?1))*})/;
15 my ($nested_parentheses) = qr/(\((?:[^()]++|(?1))*\))/;
17 # Patterns to match generated code blocks
18 my ($gen_code_start_pat, $gen_code_end_pat, $props_start_pat) =
19 (qr
(/\
*.*GENERATED CODE
: DO NOT EDIT
.*),
20 qr
(/\*.*END OF GENERATED CODE.*\*/),
22 # Actual delimiters to use for generated code blocks
23 my ($gen_code_start, $gen_code_end) =
24 ("/* {{{ GENERATED CODE: DO NOT EDIT. Created by \n".
25 " $0 $header_file $source_file \n*/\n",
26 "/* }}} END OF GENERATED CODE */\n");
28 # Strings to hold text of files
32 # ----------------------------------------------------------------
35 # remove leading and trailing whitespace
43 # split comments and non-comments, returning 2-element array
44 # of (uncommented part, comments)
45 my $comment_pat = qr
#(/\*[^*]*\*+(?:[^/*][^*]*\*+)*/|//[^\n]*)|("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|.[^/"'\\]*)#;
49 $u =~ s
#$comment_pat#defined $2 ? $2 : ""#gse;
50 $c =~ s
#$comment_pat#defined $1 ? $1 : ""#gse;
54 # Run diff, return output
58 open DIFF
, "diff -u \Q$a\E \Q$b\E |"
59 or die "Couldn't run diff";
70 rename($a, $b) or die "Couldn't rename $a to $b";
75 open OUT
, ">", $f or die "Couldn't open $f for writing";
82 # Step through file, appending lines to buffer
83 # - Lines which match generated code blocks are omitted
84 # - Other lines are passed to the subroutine. They are added to buffer
85 # unless the subroutine returns 1 (e.g. if it has already added them)
86 sub process_file
(&$) {
87 my ($sub, $file) = @_;
89 # step through lines of file, appending each one to buffer
90 open IN
, "<", $file or die "Couldn't open $file for reading";
92 # delete anything between GENERATED CODE etc.
93 if (/$gen_code_end_pat/) {
94 die "Unmatched generated code block end at $file line $.";
95 } elsif (/$gen_code_start_pat/) {
100 if (/$gen_code_end_pat/) {
105 die "Unterminated generated code block at $file line $s"
118 # Parse a PROPERTIES() block
119 sub process_properties
($$$) {
120 my ($file, $start_line, $props_text) = @_;
121 my ($errline)="$file:$start_line";
122 $props_text =~ s/^.*PROPERTIES\s*($nested_parentheses)//s
123 or die "Malformed PROPERTIES, $errline: expected PROPERTIES(args)";
127 my (@args) = split /,/, $args;
128 @args == 2 or die "PROPERTIES needs two arguments at $errline";
129 my ($struct_name, $class) = @args;
131 $props_text =~ m/^\s*($nested_curly_brackets)\s*$/s
132 or die "Malformed PROPERTIES, $errline: expected {} after PROPERTIES";
134 $body =~ s/^{(.*)}$/$1/s; # get rid of curly brackets
135 my (@stmts) = split /;\s*\n/s, $body; # split on ";"
136 my (@typedecls) = ();
138 foreach my $s (@stmts) {
139 my ($s, $cmt) = sepcomment
$s;
141 if($s =~ /^\s*$/s) { # extra ";"
143 } elsif($s =~ /DAI_ENUM|typedef/) {
144 push @typedecls, [stripws
$s, $cmt];
146 $s =~ /^\s*[a-zA-Z_][\w:]+\s*$nested_angle_brackets?/
147 or die "Couldn't match statement $s in PROPERTIES at $errline";
148 my $type = stripws
$&;
149 my $name = stripws
$';
151 if($name =~ /^(.*)=(.*)$/) {
153 $default = stripws $2;
155 push @vars, [$type, $name, $default, $cmt];
163 my $indent = (' 'x12
);
164 for my $d (@typedecls) {
165 my ($decl,$cmt) = @
$d;
167 $text .= join("\n", map { "$indent$_" } split /\n/s, $cmt)."\n";
169 $text .= "$indent$decl;\n"
172 my ($type,$name,$default,$cmt) = @
$v;
174 $text .= join("\n", map { "$indent$_" } split /\n/s, $cmt)."\n"
176 $text .= "$indent$type $name;\n";
181 /// Set members from PropertySet
182 void set(const PropertySet &opts);
183 /// Get members into PropertySet
184 PropertySet get() const;
185 /// Convert to a string which can be parsed as a PropertySet
186 std::string toString() const;
193 void ${class}::Properties::set(const PropertySet &opts)
195 const std::set<PropertyKey> &keys = opts.allKeys();
196 std::set<PropertyKey>::const_iterator i;
198 for(i=keys.begin(); i!=keys.end(); i++) {
201 my ($type,$name,$default,$cmt) = @
$v;
203 if(*i == "$name") continue;
207 cerr << "$class: Unknown property " << *i << endl;
211 DAI_THROW(UNKNOWN_PROPERTY_TYPE);
215 my ($type,$name,$default,$cmt) = @
$v;
216 if(!defined $default) {
218 if(!opts.hasKey("$name")) {
219 cerr << "$class: Missing property \\\"$name\\\" for method \\\"\Q$class\E\\\"" << endl;
228 DAI_THROW(NOT_ALL_PROPERTIES_SPECIFIED);
232 my ($type,$name,$default,$cmt) = @
$v;
233 if(defined $default) {
235 if(opts.hasKey("$name")) {
236 $name = opts.getStringAs<$type>("$name");
243 $name = opts.getStringAs<$type>("$name");
250 PropertySet ${class}::Properties::get() const {
255 my ($type,$name,$default,$cmt) = @
$v;
256 # $text .= qq{opts.set("$name", $name);};
258 opts.Set("$name", $name);
265 string ${class}::Properties::toString() const {
266 stringstream s(stringstream::out);
272 my ($type,$name,$default,$cmt) = @
$v;
274 my $c = ($i<@vars)?
" << \",\"":"";
276 s << "$name=" << $name$c;
284 } // end of namespace dai
286 return ($gen_code_start.$text.$gen_code_end, $gen_code_start.$stext.$gen_code_end);
289 # ----------------------------------------------------------------
292 $source_buffer = process_file
{ return 0; } $source_file;
293 $source_buffer =~ s/\n+$//s;
295 $header_buffer = process_file
{
296 if (/$props_start_pat/) {
297 # when we see something resembling properties, record it, and when we
298 # get to the end, process and emit the generated code
305 $buffer .= $props_text;
307 my ($htext, $stext) = process_properties
($header_file, $start_line, $props_text);
309 $source_buffer .= "\n\n\n".$stext;
315 # Write new contents of files to temporary locations
316 my $header_tmp = "$header_file.new";
317 my $source_tmp = "$source_file.new";
318 writefile
($header_buffer, $header_tmp);
319 writefile
($source_buffer, $source_tmp);
321 # Get a diff of changes, show it to the user, and ask for confirmation
323 $diff = getdiff
($header_file, $header_tmp);
324 $diff .= getdiff
($source_file, $source_tmp);
327 warn "No differences\n";
329 my $pager = $ENV{PAGER
} || "less";
330 open PAGER
, "|$pager";
334 print STDERR
"Replace old with new version? (y|N) ";
337 myrename
($header_tmp, $header_file);
338 myrename
($source_tmp, $source_file);