#!/usr/bin/perl -w # php indenter # Reformats your php source code # # $Id: phpindent,v 1.1 2002/05/20 12:54:03 weasel Exp $ # # # Depends: Parse::RecDescent # # # (c) 2002 Florian Reitmeir <squat@riot.org> # Peter Palfrader <peter@palfrader.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # # Usage: phpindent < orig.php > new.php # # # - It might corrupt your code # - It might not even work at all # - It does not understand all of php # - It's damn slow (several hours for one of our 800 line examples on a gHz # CPU) # - But it was fun to write and merely to test out Parse::RecDescent # # It was just a proof of concept - don't use it in production # # Did I mention it was slow? # # # Q: So why did you write it? # A: It was an ad hoc quick hack which went out of control but was # real fun. # # Q: So are there any real php indenters? # A: We didn't find any which is why we tried this nonsense at all. # If you find any just let us know and we will link to them. # # Q: Will you improve this indenter/fix bugs I report? # A: Probably not. But if you happen to have lots of time we would # welcome a patch or two. # # Q: You know your grammar sucks? # A: yepp. # ####################################################################### package MyToken; sub new { my ($class, %args) = @_; bless \%args, $class; } ####################################################################### package MyBinaryOperator; @ISA = qw( MyToken ); sub reprint { my ($self) = @_; return sprintf "%s %s %s", $self->{left}->reprint(), $self->{'operator'}, $self->{right}->reprint(); }; ####################################################################### package MyPostOperator; @ISA = qw( MyToken ); sub reprint { my ($self) = @_; return sprintf "%s %s", $self->{left}->reprint(), $self->{'operator'}; }; ####################################################################### package MyPreOperator; @ISA = qw( MyToken ); sub reprint { my ($self) = @_; return sprintf "%s %s", $self->{'operator'}, $self->{right}->reprint(); }; ####################################################################### package MyAtom; @ISA = qw( MyToken ); sub reprint { my ($self) = @_; return sprintf "%s", $self->{'value'}; }; ####################################################################### package MyStatement; @ISA = qw( MyToken ); sub reprint { my ($self) = @_; return sprintf "%s;\n", $self->{'value'}; }; ####################################################################### package MyComment; @ISA = qw( MyToken ); sub reprint { my ($self, %args) = @_; my $il = $args{'indent'} || 1; my $i = "\t" x ($il -1 ); my $value = $self->{'value'}; $value =~ s,#\s*,# ,; $value =~ s,//\s*,\n\n$i// ,; return $value."\n"; }; ####################################################################### package MyCommentMultiLine; @ISA = qw( MyToken ); sub reprint { my ($self, %args) = @_; my $il = $args{'indent'}-1 || 0; my $i = "\t" x $il; my $value = $self->{'value'}; $value =~ s,/\*\s*\n?,,; $value =~ s,\n?\s*\*/,,; my @lines = map { s/^\s*\*?[ \t]*/$i * /; $_ } split /\n/, $value; unshift @lines, "/*"; push @lines, "$i */"; return sprintf "%s\n", join ("\n", @lines) }; ####################################################################### package MyCommentDoc; @ISA = qw( MyToken ); sub reprint { my ($self, %args) = @_; my $il = $args{'indent'}-1 || 0; my $i = "\t" x $il; my $value = $self->{'value'}; $value =~ s,^/\*\*[ \t]*\n?,,; $value =~ s,\n?[ \t]*\*/$,,; $value =~ s/\t/ /g; my @lines = split /\n/, $value; my $maxwhitespace; for (@lines) { my ($leadwhitespace) = ($_ =~ /^(\s*)/); $maxwhitespace = (!defined ($maxwhitespace) || length($leadwhitespace) < $maxwhitespace) ? length($leadwhitespace) : $maxwhitespace; }; my $leadwhitespace = " " x $maxwhitespace; @lines = map { s/^$leadwhitespace//; s/^/$i /; $_ } @lines; unshift @lines, "/**"; push @lines, "$i */"; return sprintf "%s\n", join ("\n", @lines) }; ####################################################################### package MyConditionalBlock; @ISA = qw( MyToken ); sub reprint { my ($self, %args) = @_; my $il = $args{'indent'} || 0; my $i = "\t" x $il; my $block = $self->{block}->reprint(indent => $il, noend=>1); if ($block =~ /^\s*\{/) { $block = "\t" x ($il-1) . $block; $isblock = 1; } else { $block = "\t" x ($il) . $block; $isblock = 0; }; my $elseblock; if ($self->{type} eq 'ifelse') { $elseblock = $self->{elseblock}->reprint(indent => $il, noend=>1); if ($elseblock =~ /^\s*\{/) { $elseblock = "\t" x ($il-1) . $elseblock; $elseisblock = 1; } else { $elseblock = "\t" x ($il) . $elseblock; $elseisblock = 0; }; }; if ($self->{type} eq 'if' || $self->{type} eq 'while') { return $self->{type}. " (". $self->{condition}->reprint(). ")\n". $block. ($isblock ? ";\n" : ""); } elsif ($self->{type} eq 'ifelse') { return "if". " (". $self->{condition}->reprint(). ")\n". $block. ($elseisblock ? "\n" : ""). "\t"x($il-1) . "else\n". $elseblock. ($elseisblock ? ";\n" : ""); } elsif ($self->{type} eq 'dowhile') { return "do\n". $block. " while (". $self->{condition}->reprint(). ");\n"; } elsif ($self->{type} eq 'for') { return $self->{type}. " (". $self->{part1}->reprint(). "; ". $self->{part2}->reprint(). "; ". $self->{part3}->reprint(). ")\n". $block. ($isblock ? ";\n" : ""); } elsif ($self->{type} eq 'foreach') { return $self->{type}. " (". $self->{part1}->reprint(). " as ". $self->{part2}->reprint(). ")\n". $block. ($isblock ? ";\n" : ""); }; }; ####################################################################### package MyParam; @ISA = qw( MyToken ); sub reprint { my ($self, %args) = @_; my $il = $args{'indent'} || 1; my $i = "\t" x $il; my $statements = join (", ", map { $_->reprint() } @{$self->{'statements'}}); return $statements; }; ####################################################################### package MyFunction; @ISA = qw( MyToken ); sub reprint { my ($self, %args) = @_; my $il = $args{'indent'} || 1; my $i = "\t" x $il; return $self->{name}->reprint(). " (". ((defined $self->{params}) ? $self->{params}->reprint() : ""). ")"; }; ####################################################################### package MyBlock; @ISA = qw( MyToken ); sub reprint { my ($self, %args) = @_; my $il = $args{'indent'} || 1; my $i = "\t" x $il; my $statements = join ($i, map { $_->reprint(indent => $il + 1) } @{$self->{'statements'}}); $statements = $i.$statements if ( substr($statements,0,1) ne "\n" ); return ((defined $args{'nobraces'}) ? "" : "{\n"). $statements. "\t" x ($il-1). ((defined $args{'nobraces'}) ? "" : "}"). ((defined $args{'noend'}) ? "" : ";\n"); }; ####################################################################### package MyFunctionDefinition; @ISA = qw( MyToken ); sub reprint { my ($self, %args) = @_; my $il = $args{'indent'} || 1; my $i = "\t" x ($il-1); return "\n". $i."function ". $self->{'header'}->reprint(). "\n". $i.$self->{'block'}->reprint(indent => $il)."\n"; }; ####################################################################### package main; use strict; use Parse::RecDescent; my $input; local $/ = undef; $input = <>; my $grammar = q { Script: /<\?(php)?/ Block '?>' { MyAtom->new ( value => "<?\n" . $item[2]->reprint(nobraces=>1) ."?>\n" ) } GroupedBlock: '{' Block '}' /;?/ { MyBlock->new ( statements => [ @{$item[2]->{'statements'}} ] ) } Block: Statement Block { MyBlock->new ( statements => [ $item[1], @{$item[2]->{'statements'}} ] ) } | Statement { MyBlock->new ( statements => [ $item[1] ] ) } Statement: Expression ';' { MyStatement->new ( value => $item[1]->reprint() ) } | ';' { MyStatement->new ( value => '' ) } | GroupedBlock ';' { $item[1] } | GroupedBlock | Comment | IfThenElseBlock | IfBlock | WhileBlock | DoWhileBlock | ForLoop | ForEachLoop | FunctionDefinition FunctionDefinition: "function" FunctionCall GroupedBlock { MyFunctionDefinition->new ( header => $item[2], block => $item[3] ) } IfThenElseBlock: "if" "(" Expression ")" Statement "else" Statement { MyConditionalBlock->new ( type => 'ifelse', condition => $item[3], block => $item[5], elseblock => $item[7] ) } IfBlock: "if" "(" Expression ")" Statement { MyConditionalBlock->new ( type => 'if', condition => $item[3], block => $item[5] ) } WhileBlock: "while" "(" Expression ")" Statement { MyConditionalBlock->new ( type => 'while', condition => $item[3], block => $item[5] ) } DoWhileBlock: "do" GroupedBlock "while" "(" Expression ")" ";" { MyConditionalBlock->new ( type => 'dowhile', condition => $item[5], block => $item[2] ) } ForLoop: "for" "(" Expression ";" Expression ";" Expression ")" Statement { MyConditionalBlock->new ( type => 'for', part1 => $item[3], part2 => $item[5], part3 => $item[7], block => $item[9] ) } ForEachLoop: "foreach" "(" Expression "as" Expression ")" Statement { MyConditionalBlock->new ( type => 'foreach', part1 => $item[3], part2 => $item[5], block => $item[7] ) } Expression: "(" Expression ")" { $item[2] } | FunctionCall Operator Expression { MyBinaryOperator->new ( left=>$item[1], operator=>$item[2], right=>$item[3] ) } | Atom Operator Expression { MyBinaryOperator->new ( left=>$item[1], operator=>$item[2], right=>$item[3] ) } | Atom Operator { MyPostOperator->new ( left=>$item[1], operator=>$item[2] ) } | PreOperator Expression { MyPreOperator->new ( right=>$item[2], operator=>$item[1] ) } | FunctionCall | Atom Comment: CommentHash | CommentSlash | CommentDoc | CommentMultiLine CommentHash: /#.*?$/m { MyComment->new ( value => $item[1] ) } CommentSlash: /\/\/.*?$/m { MyComment->new ( value => $item[1] ) } CommentDoc: /\/\*\*.*?\*\//s { MyCommentDoc->new ( value => $item[1] ) } CommentMultiLine: /\/\*.*?\*\//s { MyCommentMultiLine->new ( value => $item[1] ) } Operator: '===' | '!==' | '+=' | '-=' | '==' | '!=' | '=>' | '<=' | '>=' | '++' | '--' | '&&' | '.=' | '||' | '&' | '|' | '>' | '<' | '=' | '/' | '+' | '-' | '*' | '.' | ':' | '?' PreOperator: "new" | "print" | "return" | "echo" | "not" | "++" | "--" | "-" | "+" | "1" | "!" FunctionCall: Atom "(" FunctionParameter ")" { MyFunction->new ( name => $item[1], params => $item[3] ) } | Atom "(" ")" { MyFunction->new ( name => $item[1] ) } Atom: Variable | String | Identifier | Numerical FunctionParameter: Expression "," FunctionParameter { MyParam->new ( statements => [ $item[1], @{$item[3]->{'statements'}} ] ) } | Expression { MyParam->new ( statements => [ $item[1] ] ) } Variable: '$' Identifier Array { MyAtom->new ( value => '$'.$item[2]->reprint() . $item[3]->reprint() ) } | '$' Identifier { MyAtom->new ( value => '$'.$item[2]->reprint() ) } Array: "[" "]" Array { MyAtom->new ( value => '[]'.$item[3]->reprint() ) } | "[" Expression "]" Array { MyAtom->new ( value => '['.$item[2]->reprint().']'.$item[4]->reprint() ) } | "[" Expression "]" { MyAtom->new ( value => '['.$item[2]->reprint().']' ) } | "[" "]" { MyAtom->new ( value => '[]' ) } Identifier: SimpleIdentifier "->" Identifier { MyAtom->new ( value => $item[1]->reprint() ."->" .$item[3]->reprint() ) } | SimpleIdentifier SimpleIdentifier: /[a-zA-Z][a-zA-Z0-9_]*/ { MyAtom->new ( value => $item[1] ) } Numerical: /[\d]+(\.[\d+])?/ { MyAtom->new ( value => $item[1] ) } String: EmptyDQString | NotEmptyDQString | EmptySQString | NotEmptySQString NotEmptyDQString: /".*?[^\\\\]"/s { MyAtom->new ( value => $item[1] ) } EmptyDQString: '""' { MyAtom->new ( value => '""' ) } NotEmptySQString: /'.*?[^\\\\]'/s { MyAtom->new ( value => $item[1] ) } EmptySQString: "''" { MyAtom->new ( value => "''" ) } }; #$::RD_HINT = 1; my $parser = new Parse::RecDescent ($grammar); my $tree = $parser->Script($input); print $tree->reprint(); # vim:set ts=4: # vim:set shiftwidth=4:
syntax highlighted by Code2HTML, v. 0.9.1