Commit | Line | Data |
---|---|---|
a6540423 H |
1 | #!/usr/bin/perl -w |
2 | ||
3 | # php indenter | |
4 | # Reformats your php source code | |
5 | # | |
6 | # $Id: phpindent,v 1.1 2002/05/20 12:54:03 weasel Exp $ | |
7 | # | |
8 | # | |
9 | # Depends: Parse::RecDescent | |
10 | # | |
11 | # | |
12 | # (c) 2002 Florian Reitmeir <squat@riot.org> | |
13 | # Peter Palfrader <peter@palfrader.org> | |
14 | # | |
15 | # This program is free software; you can redistribute it and/or modify | |
16 | # it under the terms of the GNU General Public License as published by | |
17 | # the Free Software Foundation; either version 2 of the License, or | |
18 | # (at your option) any later version. | |
19 | # | |
20 | # This program is distributed in the hope that it will be useful, | |
21 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
22 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
23 | # GNU General Public License for more details. | |
24 | # | |
25 | # You should have received a copy of the GNU General Public License | |
26 | # along with this program; if not, write to the Free Software | |
27 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
28 | # | |
29 | # | |
30 | # | |
31 | # Usage: phpindent < orig.php > new.php | |
32 | # | |
33 | # | |
34 | # - It might corrupt your code | |
35 | # - It might not even work at all | |
36 | # - It does not understand all of php | |
37 | # - It's damn slow (several hours for one of our 800 line examples on a gHz | |
38 | # CPU) | |
39 | # - But it was fun to write and merely to test out Parse::RecDescent | |
40 | # | |
41 | # It was just a proof of concept - don't use it in production | |
42 | # | |
43 | # Did I mention it was slow? | |
44 | # | |
45 | # | |
46 | # Q: So why did you write it? | |
47 | # A: It was an ad hoc quick hack which went out of control but was | |
48 | # real fun. | |
49 | # | |
50 | # Q: So are there any real php indenters? | |
51 | # A: We didn't find any which is why we tried this nonsense at all. | |
52 | # If you find any just let us know and we will link to them. | |
53 | # | |
54 | # Q: Will you improve this indenter/fix bugs I report? | |
55 | # A: Probably not. But if you happen to have lots of time we would | |
56 | # welcome a patch or two. | |
57 | # | |
58 | # Q: You know your grammar sucks? | |
59 | # A: yepp. | |
60 | # | |
61 | ||
62 | ####################################################################### | |
63 | package MyToken; | |
64 | ||
65 | sub new | |
66 | { | |
67 | my ($class, %args) = @_; | |
68 | bless \%args, $class; | |
69 | } | |
70 | ||
71 | ####################################################################### | |
72 | package MyBinaryOperator; | |
73 | @ISA = qw( MyToken ); | |
74 | ||
75 | sub reprint | |
76 | { | |
77 | my ($self) = @_; | |
78 | return | |
79 | sprintf "%s %s %s", | |
80 | $self->{left}->reprint(), | |
81 | $self->{'operator'}, | |
82 | $self->{right}->reprint(); | |
83 | }; | |
84 | ||
85 | ####################################################################### | |
86 | package MyPostOperator; | |
87 | @ISA = qw( MyToken ); | |
88 | ||
89 | sub reprint | |
90 | { | |
91 | my ($self) = @_; | |
92 | return | |
93 | sprintf "%s %s", | |
94 | $self->{left}->reprint(), | |
95 | $self->{'operator'}; | |
96 | }; | |
97 | ||
98 | ####################################################################### | |
99 | package MyPreOperator; | |
100 | @ISA = qw( MyToken ); | |
101 | ||
102 | sub reprint | |
103 | { | |
104 | my ($self) = @_; | |
105 | return | |
106 | sprintf "%s %s", | |
107 | $self->{'operator'}, | |
108 | $self->{right}->reprint(); | |
109 | }; | |
110 | ||
111 | ####################################################################### | |
112 | package MyAtom; | |
113 | @ISA = qw( MyToken ); | |
114 | ||
115 | sub reprint | |
116 | { | |
117 | my ($self) = @_; | |
118 | ||
119 | return | |
120 | sprintf "%s", | |
121 | $self->{'value'}; | |
122 | }; | |
123 | ||
124 | ####################################################################### | |
125 | package MyStatement; | |
126 | @ISA = qw( MyToken ); | |
127 | ||
128 | sub reprint | |
129 | { | |
130 | my ($self) = @_; | |
131 | ||
132 | return | |
133 | sprintf "%s;\n", | |
134 | $self->{'value'}; | |
135 | }; | |
136 | ||
137 | ####################################################################### | |
138 | package MyComment; | |
139 | @ISA = qw( MyToken ); | |
140 | ||
141 | sub reprint | |
142 | { | |
143 | my ($self, %args) = @_; | |
144 | my $il = $args{'indent'} || 1; | |
145 | my $i = "\t" x ($il -1 ); | |
146 | ||
147 | my $value = $self->{'value'}; | |
148 | $value =~ s,#\s*,# ,; | |
149 | $value =~ s,//\s*,\n\n$i// ,; | |
150 | return | |
151 | $value."\n"; | |
152 | }; | |
153 | ||
154 | ####################################################################### | |
155 | package MyCommentMultiLine; | |
156 | @ISA = qw( MyToken ); | |
157 | ||
158 | sub reprint | |
159 | { | |
160 | my ($self, %args) = @_; | |
161 | my $il = $args{'indent'}-1 || 0; | |
162 | my $i = "\t" x $il; | |
163 | ||
164 | my $value = $self->{'value'}; | |
165 | $value =~ s,/\*\s*\n?,,; | |
166 | $value =~ s,\n?\s*\*/,,; | |
167 | my @lines = map { s/^\s*\*?[ \t]*/$i * /; $_ } split /\n/, $value; | |
168 | unshift @lines, "/*"; | |
169 | push @lines, "$i */"; | |
170 | ||
171 | return | |
172 | sprintf "%s\n", | |
173 | join ("\n", @lines) | |
174 | }; | |
175 | ||
176 | ####################################################################### | |
177 | package MyCommentDoc; | |
178 | @ISA = qw( MyToken ); | |
179 | ||
180 | sub reprint | |
181 | { | |
182 | my ($self, %args) = @_; | |
183 | my $il = $args{'indent'}-1 || 0; | |
184 | my $i = "\t" x $il; | |
185 | ||
186 | my $value = $self->{'value'}; | |
187 | $value =~ s,^/\*\*[ \t]*\n?,,; | |
188 | $value =~ s,\n?[ \t]*\*/$,,; | |
189 | $value =~ s/\t/ /g; | |
190 | my @lines = split /\n/, $value; | |
191 | ||
192 | my $maxwhitespace; | |
193 | for (@lines) { | |
194 | my ($leadwhitespace) = ($_ =~ /^(\s*)/); | |
195 | $maxwhitespace = (!defined ($maxwhitespace) || length($leadwhitespace) < $maxwhitespace) ? | |
196 | length($leadwhitespace) : | |
197 | $maxwhitespace; | |
198 | }; | |
199 | my $leadwhitespace = " " x $maxwhitespace; | |
200 | @lines = map { s/^$leadwhitespace//; s/^/$i /; $_ } @lines; | |
201 | ||
202 | unshift @lines, "/**"; | |
203 | push @lines, "$i */"; | |
204 | ||
205 | return | |
206 | sprintf "%s\n", | |
207 | join ("\n", @lines) | |
208 | }; | |
209 | ||
210 | ####################################################################### | |
211 | package MyConditionalBlock; | |
212 | @ISA = qw( MyToken ); | |
213 | ||
214 | sub reprint | |
215 | { | |
216 | my ($self, %args) = @_; | |
217 | my $il = $args{'indent'} || 0; | |
218 | my $i = "\t" x $il; | |
219 | ||
220 | my $block = $self->{block}->reprint(indent => $il, noend=>1); | |
221 | if ($block =~ /^\s*\{/) { | |
222 | $block = "\t" x ($il-1) . $block; | |
223 | $isblock = 1; | |
224 | } else { | |
225 | $block = "\t" x ($il) . $block; | |
226 | $isblock = 0; | |
227 | }; | |
228 | ||
229 | ||
230 | my $elseblock; | |
231 | if ($self->{type} eq 'ifelse') { | |
232 | $elseblock = $self->{elseblock}->reprint(indent => $il, noend=>1); | |
233 | if ($elseblock =~ /^\s*\{/) { | |
234 | $elseblock = "\t" x ($il-1) . $elseblock; | |
235 | $elseisblock = 1; | |
236 | } else { | |
237 | $elseblock = "\t" x ($il) . $elseblock; | |
238 | $elseisblock = 0; | |
239 | }; | |
240 | }; | |
241 | ||
242 | if ($self->{type} eq 'if' || $self->{type} eq 'while') { | |
243 | return | |
244 | $self->{type}. | |
245 | " (". | |
246 | $self->{condition}->reprint(). | |
247 | ")\n". | |
248 | $block. | |
249 | ($isblock ? ";\n" : ""); | |
250 | } elsif ($self->{type} eq 'ifelse') { | |
251 | return | |
252 | "if". | |
253 | " (". | |
254 | $self->{condition}->reprint(). | |
255 | ")\n". | |
256 | $block. | |
257 | ($elseisblock ? "\n" : ""). | |
258 | "\t"x($il-1) . "else\n". | |
259 | $elseblock. | |
260 | ($elseisblock ? ";\n" : ""); | |
261 | } elsif ($self->{type} eq 'dowhile') { | |
262 | return | |
263 | "do\n". | |
264 | $block. | |
265 | " while (". | |
266 | $self->{condition}->reprint(). | |
267 | ");\n"; | |
268 | } elsif ($self->{type} eq 'for') { | |
269 | return | |
270 | $self->{type}. | |
271 | " (". | |
272 | $self->{part1}->reprint(). | |
273 | "; ". | |
274 | $self->{part2}->reprint(). | |
275 | "; ". | |
276 | $self->{part3}->reprint(). | |
277 | ")\n". | |
278 | $block. | |
279 | ($isblock ? ";\n" : ""); | |
280 | } elsif ($self->{type} eq 'foreach') { | |
281 | return | |
282 | $self->{type}. | |
283 | " (". | |
284 | $self->{part1}->reprint(). | |
285 | " as ". | |
286 | $self->{part2}->reprint(). | |
287 | ")\n". | |
288 | $block. | |
289 | ($isblock ? ";\n" : ""); | |
290 | }; | |
291 | ||
292 | }; | |
293 | ||
294 | ####################################################################### | |
295 | package MyParam; | |
296 | @ISA = qw( MyToken ); | |
297 | ||
298 | sub reprint | |
299 | { | |
300 | my ($self, %args) = @_; | |
301 | my $il = $args{'indent'} || 1; | |
302 | my $i = "\t" x $il; | |
303 | ||
304 | my $statements = join (", ", map { $_->reprint() } @{$self->{'statements'}}); | |
305 | ||
306 | return | |
307 | $statements; | |
308 | }; | |
309 | ||
310 | ####################################################################### | |
311 | package MyFunction; | |
312 | @ISA = qw( MyToken ); | |
313 | ||
314 | sub reprint | |
315 | { | |
316 | my ($self, %args) = @_; | |
317 | my $il = $args{'indent'} || 1; | |
318 | my $i = "\t" x $il; | |
319 | ||
320 | return | |
321 | $self->{name}->reprint(). | |
322 | " (". | |
323 | ((defined $self->{params}) ? $self->{params}->reprint() : ""). | |
324 | ")"; | |
325 | }; | |
326 | ||
327 | ####################################################################### | |
328 | package MyBlock; | |
329 | @ISA = qw( MyToken ); | |
330 | ||
331 | sub reprint | |
332 | { | |
333 | my ($self, %args) = @_; | |
334 | my $il = $args{'indent'} || 1; | |
335 | my $i = "\t" x $il; | |
336 | ||
337 | my $statements = join ($i, map { $_->reprint(indent => $il + 1) } @{$self->{'statements'}}); | |
338 | ||
339 | $statements = $i.$statements if ( substr($statements,0,1) ne "\n" ); | |
340 | ||
341 | return | |
342 | ((defined $args{'nobraces'}) ? "" : "{\n"). | |
343 | $statements. | |
344 | "\t" x ($il-1). | |
345 | ((defined $args{'nobraces'}) ? "" : "}"). | |
346 | ((defined $args{'noend'}) ? "" : ";\n"); | |
347 | }; | |
348 | ||
349 | ####################################################################### | |
350 | package MyFunctionDefinition; | |
351 | @ISA = qw( MyToken ); | |
352 | ||
353 | sub reprint | |
354 | { | |
355 | my ($self, %args) = @_; | |
356 | ||
357 | my $il = $args{'indent'} || 1; | |
358 | my $i = "\t" x ($il-1); | |
359 | ||
360 | return | |
361 | "\n". | |
362 | $i."function ". | |
363 | $self->{'header'}->reprint(). "\n". | |
364 | $i.$self->{'block'}->reprint(indent => $il)."\n"; | |
365 | }; | |
366 | ||
367 | ||
368 | ||
369 | ||
370 | ####################################################################### | |
371 | package main; | |
372 | ||
373 | use strict; | |
374 | use Parse::RecDescent; | |
375 | ||
376 | ||
377 | my $input; | |
378 | local $/ = undef; | |
379 | $input = <>; | |
380 | ||
381 | my $grammar = q | |
382 | { | |
383 | Script: /<\?(php)?/ Block '?>' { MyAtom->new ( value => "<?\n" . $item[2]->reprint(nobraces=>1) ."?>\n" ) } | |
384 | ||
385 | GroupedBlock: '{' Block '}' /;?/ { MyBlock->new ( statements => [ @{$item[2]->{'statements'}} ] ) } | |
386 | ||
387 | Block: Statement Block { MyBlock->new ( statements => [ $item[1], @{$item[2]->{'statements'}} ] ) } | |
388 | | Statement { MyBlock->new ( statements => [ $item[1] ] ) } | |
389 | ||
390 | Statement: Expression ';' { MyStatement->new ( value => $item[1]->reprint() ) } | |
391 | | ';' { MyStatement->new ( value => '' ) } | |
392 | | GroupedBlock ';' { $item[1] } | |
393 | | GroupedBlock | |
394 | | Comment | |
395 | | IfThenElseBlock | |
396 | | IfBlock | |
397 | | WhileBlock | |
398 | | DoWhileBlock | |
399 | | ForLoop | |
400 | | ForEachLoop | |
401 | | FunctionDefinition | |
402 | ||
403 | FunctionDefinition: "function" FunctionCall GroupedBlock | |
404 | { MyFunctionDefinition->new ( header => $item[2], block => $item[3] ) } | |
405 | ||
406 | ||
407 | IfThenElseBlock: "if" "(" Expression ")" Statement "else" Statement | |
408 | { MyConditionalBlock->new ( type => 'ifelse', | |
409 | condition => $item[3], | |
410 | block => $item[5], | |
411 | elseblock => $item[7] ) } | |
412 | IfBlock: "if" "(" Expression ")" Statement | |
413 | { MyConditionalBlock->new ( type => 'if', | |
414 | condition => $item[3], | |
415 | block => $item[5] ) } | |
416 | WhileBlock: "while" "(" Expression ")" Statement | |
417 | { MyConditionalBlock->new ( type => 'while', | |
418 | condition => $item[3], | |
419 | block => $item[5] ) } | |
420 | DoWhileBlock: "do" GroupedBlock "while" "(" Expression ")" ";" | |
421 | { MyConditionalBlock->new ( type => 'dowhile', | |
422 | condition => $item[5], | |
423 | block => $item[2] ) } | |
424 | ForLoop: "for" "(" Expression ";" Expression ";" Expression ")" Statement | |
425 | { MyConditionalBlock->new ( type => 'for', | |
426 | part1 => $item[3], | |
427 | part2 => $item[5], | |
428 | part3 => $item[7], | |
429 | block => $item[9] ) } | |
430 | ForEachLoop: "foreach" "(" Expression "as" Expression ")" Statement | |
431 | { MyConditionalBlock->new ( type => 'foreach', | |
432 | part1 => $item[3], | |
433 | part2 => $item[5], | |
434 | block => $item[7] ) } | |
435 | Expression: "(" Expression ")" { $item[2] } | |
436 | | FunctionCall Operator Expression | |
437 | { MyBinaryOperator->new ( left=>$item[1], operator=>$item[2], right=>$item[3] ) } | |
438 | | Atom Operator Expression | |
439 | { MyBinaryOperator->new ( left=>$item[1], operator=>$item[2], right=>$item[3] ) } | |
440 | | Atom Operator { MyPostOperator->new ( left=>$item[1], operator=>$item[2] ) } | |
441 | | PreOperator Expression { MyPreOperator->new ( right=>$item[2], operator=>$item[1] ) } | |
442 | | FunctionCall | |
443 | | Atom | |
444 | ||
445 | ||
446 | Comment: CommentHash | |
447 | | CommentSlash | |
448 | | CommentDoc | |
449 | | CommentMultiLine | |
450 | ||
451 | CommentHash: /#.*?$/m { MyComment->new ( value => $item[1] ) } | |
452 | CommentSlash: /\/\/.*?$/m { MyComment->new ( value => $item[1] ) } | |
453 | CommentDoc: /\/\*\*.*?\*\//s { MyCommentDoc->new ( value => $item[1] ) } | |
454 | CommentMultiLine: /\/\*.*?\*\//s { MyCommentMultiLine->new ( value => $item[1] ) } | |
455 | ||
456 | Operator: '===' | |
457 | | '!==' | |
458 | | '+=' | |
459 | | '-=' | |
460 | | '==' | |
461 | | '!=' | |
462 | | '=>' | |
463 | | '<=' | |
464 | | '>=' | |
465 | | '++' | |
466 | | '--' | |
467 | | '&&' | |
468 | | '.=' | |
469 | | '||' | |
470 | | '&' | |
471 | | '|' | |
472 | | '>' | |
473 | | '<' | |
474 | | '=' | |
475 | | '/' | |
476 | | '+' | |
477 | | '-' | |
478 | | '*' | |
479 | | '.' | |
480 | | ':' | |
481 | | '?' | |
482 | ||
483 | PreOperator: "new" | |
484 | | "print" | |
485 | | "return" | |
486 | | "echo" | |
487 | | "not" | |
488 | | "++" | |
489 | | "--" | |
490 | | "-" | |
491 | | "+" | |
492 | | "1" | |
493 | | "!" | |
494 | ||
495 | ||
496 | ||
497 | FunctionCall: Atom "(" FunctionParameter ")" | |
498 | { MyFunction->new ( name => $item[1], params => $item[3] ) } | |
499 | | Atom "(" ")" | |
500 | { MyFunction->new ( name => $item[1] ) } | |
501 | ||
502 | Atom: Variable | |
503 | | String | |
504 | | Identifier | |
505 | | Numerical | |
506 | ||
507 | ||
508 | FunctionParameter: Expression "," FunctionParameter | |
509 | { MyParam->new ( statements => [ $item[1], @{$item[3]->{'statements'}} ] ) } | |
510 | | Expression { MyParam->new ( statements => [ $item[1] ] ) } | |
511 | ||
512 | ||
513 | Variable: '$' Identifier Array | |
514 | { MyAtom->new ( value => '$'.$item[2]->reprint() . $item[3]->reprint() ) } | |
515 | | '$' Identifier { MyAtom->new ( value => '$'.$item[2]->reprint() ) } | |
516 | ||
517 | ||
518 | Array: "[" "]" Array { MyAtom->new ( value => '[]'.$item[3]->reprint() ) } | |
519 | | "[" Expression "]" Array | |
520 | { MyAtom->new ( value => '['.$item[2]->reprint().']'.$item[4]->reprint() ) } | |
521 | | "[" Expression "]" { MyAtom->new ( value => '['.$item[2]->reprint().']' ) } | |
522 | | "[" "]" { MyAtom->new ( value => '[]' ) } | |
523 | ||
524 | ||
525 | Identifier: SimpleIdentifier "->" Identifier | |
526 | { MyAtom->new ( value => $item[1]->reprint() ."->" .$item[3]->reprint() ) } | |
527 | | SimpleIdentifier | |
528 | ||
529 | SimpleIdentifier: /[a-zA-Z][a-zA-Z0-9_]*/ { MyAtom->new ( value => $item[1] ) } | |
530 | ||
531 | Numerical: /[\d]+(\.[\d+])?/ { MyAtom->new ( value => $item[1] ) } | |
532 | ||
533 | String: EmptyDQString | |
534 | | NotEmptyDQString | |
535 | | EmptySQString | |
536 | | NotEmptySQString | |
537 | NotEmptyDQString: /".*?[^\\\\]"/s { MyAtom->new ( value => $item[1] ) } | |
538 | EmptyDQString: '""' { MyAtom->new ( value => '""' ) } | |
539 | NotEmptySQString: /'.*?[^\\\\]'/s { MyAtom->new ( value => $item[1] ) } | |
540 | EmptySQString: "''" { MyAtom->new ( value => "''" ) } | |
541 | }; | |
542 | ||
543 | #$::RD_HINT = 1; | |
544 | my $parser = new Parse::RecDescent ($grammar); | |
545 | my $tree = $parser->Script($input); | |
546 | ||
547 | print $tree->reprint(); | |
548 | ||
549 | ||
550 | ||
551 | # vim:set ts=4: | |
552 | # vim:set shiftwidth=4: |