f3601b93d7e7f55339280893ea06ba657cfb0f5c
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;
197 for(i=keys.begin(); i!=keys.end(); i++) {
200 my ($type,$name,$default,$cmt) = @
$v;
202 if(*i == "$name") continue;
206 DAI_THROWE(UNKNOWN_PROPERTY_TYPE, "$class: Unknown property " + *i);
210 my ($type,$name,$default,$cmt) = @
$v;
211 if(!defined $default) {
213 if(!opts.hasKey("$name"))
214 DAI_THROWE(NOT_ALL_PROPERTIES_SPECIFIED,"$class: Missing property \\\"$name\\\" for method \\\"\Q$class\E\\\"");
222 my ($type,$name,$default,$cmt) = @
$v;
223 if(defined $default) {
225 if(opts.hasKey("$name")) {
226 $name = opts.getStringAs<$type>("$name");
233 $name = opts.getStringAs<$type>("$name");
240 PropertySet ${class}::Properties::get() const {
245 my ($type,$name,$default,$cmt) = @
$v;
246 # $text .= qq{opts.set("$name", $name);};
248 opts.Set("$name", $name);
255 string ${class}::Properties::toString() const {
256 stringstream s(stringstream::out);
262 my ($type,$name,$default,$cmt) = @
$v;
264 my $c = ($i<@vars)?
" << \",\"":"";
266 s << "$name=" << $name$c;
274 } // end of namespace dai
276 return ($gen_code_start.$text.$gen_code_end, $gen_code_start.$stext.$gen_code_end);
279 # ----------------------------------------------------------------
282 $source_buffer = process_file
{ return 0; } $source_file;
283 $source_buffer =~ s/\n+$//s;
285 $header_buffer = process_file
{
286 if (/$props_start_pat/) {
287 # when we see something resembling properties, record it, and when we
288 # get to the end, process and emit the generated code
295 $buffer .= $props_text;
297 my ($htext, $stext) = process_properties
($header_file, $start_line, $props_text);
299 $source_buffer .= "\n\n\n".$stext;
305 # Write new contents of files to temporary locations
306 my $header_tmp = "$header_file.new";
307 my $source_tmp = "$source_file.new";
308 writefile
($header_buffer, $header_tmp);
309 writefile
($source_buffer, $source_tmp);
311 # Get a diff of changes, show it to the user, and ask for confirmation
313 $diff = getdiff
($header_file, $header_tmp);
314 $diff .= getdiff
($source_file, $source_tmp);
317 warn "No differences\n";
319 my $pager = $ENV{PAGER
} || "less";
320 open PAGER
, "|$pager";
324 print STDERR
"Replace old with new version? (y|N) ";
327 myrename
($header_tmp, $header_file);
328 myrename
($source_tmp, $source_file);