| 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: |