2014-10-22 2 views
2

Я хотел бы отсортировать мои подпрограммы модуля в алфавитном порядке (у меня много подпрограмм, и я думаю, что будет легче редактировать файл, если подпрограммы упорядочены в файле). К примеру приведены A.pm:Подпрограммы модуля сортировки по алфавиту

package A; 
use warnings; 
use strict; 

sub subA { 
    print "A\n"; 
} 

sub subC { 
    print "C\n"; 
} 
sub subB { 
    print "B\n"; 
} 

1; 

Я хотел бы запустить sortSub A.pm дает:

package A; 
use warnings; 
use strict; 

sub subA { 
    print "A\n"; 
} 
sub subB { 
    print "B\n"; 
} 
sub subC { 
    print "C\n"; 
} 
1; 

Есть ли ресурс CPAN, который может помочь с этой задачей?

+4

Если некоторые из ваших субтитров призывают другие подводные лодки, то в некоторых случаях (если вы используете прототипы, или позвонив по телефону подлодки без скобок), порядок, в котором они может повлиять на то, как они интерпретируются. – tobyink

+0

@tobyink Спасибо за комментарий.Я не использую никаких прототипов. Почему вызов subs без круглых скобок является проблемой? Не могли бы вы привести пример? –

+0

@ HåkonHægland Скобки необязательны, если подпрограмма предварительно объявлена ​​или импортирована. 'perl -wE 'sub foo {say" foo "; } foo'' работает, 'perl -wE 'foo; sub foo {say "foo"; } ''не делает. – ThisSuitIsBlackNot

ответ

4

Для разбора и форматирования кода Perl, вы должны использовать PPI.

Это тот самый инструмент, который Perl::Critic и Perl::Tidy используют для совершения всех своих умений.

В этом случае я изучил код для PPI::Dumper, чтобы получить представление о том, как перемещаться по дереву документов, возвращаемому PPI.

Далее будет проанализирован исходный код и отдельные разделы, содержащие подпрограммы и комментарии. Он свяжет комментарии, pod и whitespace перед подпрограммой с ним, а затем он сортирует все соседние подмножества по их именам.

use strict; 
use warnings; 

use PPI; 
use Data::Dump; 

my $src = do { local $/; <DATA> }; 

# Load a document 
my $doc = PPI::Document->new(\$src); 

# Save Sub locations for later sorting 
my @group =(); 
my @subs =(); 

for my $i (0 .. $#{ $doc->{children} }) { 
    my $child = $doc->{children}[$i]; 

    my ($subtype, $subname) 
     = $child->isa('PPI::Statement::Sub') 
     ? grep { $_->isa('PPI::Token::Word') } @{ $child->{children} } 
     : ('', ''); 

    # Look for grouped subs, whitespace and comments. Sort each group separately. 
    my $is_related = ($subtype eq 'sub') || grep { $child->isa("PPI::Token::$_") } qw(Whitespace Comment Pod); 

    # State change or end of stream 
    if (my $range = $is_related .. (!$is_related || ($i == $#{ $doc->{children} }))) { 
     if ($is_related) { 
      push @group, $child; 

      if ($subtype) { 
       push @subs, { name => "$subname", children => [@group] }; 
       @group =(); 
      } 
     } 

     if ($range =~ /E/) { 
      @group =(); 

      if (@subs) { 
       # Sort and Flatten 
       my @sorted = map { @{ $_->{children} } } sort { $a->{name} cmp $b->{name} } @subs; 

       # Assign back to document, and then reset group 
       my $min_index = $i - $range + 1; 
       @{ $doc->{children} }[ $min_index .. $min_index + $#sorted ] = @sorted; 

       @subs =(); 
      } 
     } 
    } 
} 

print $doc->serialize; 

1; 

__DATA__ 
package A; 
use warnings; 
use strict; 

=comment 
Pod describing subC 
=cut 
sub subC { 
    print "C\n"; 
} 

INIT { 
    print "Hello World"; 
} 

sub subB { 
    print "B\n"; 
} 

# Hello subA comment 
sub subA { 
    print "A\n"; 
} 

1; 

Выход:

package A; 
use warnings; 
use strict; 

=comment 
Pod describing subC 
=cut 
sub subC { 
    print "C\n"; 
} 

INIT { 
    print "Hello World"; 
} 

# Hello subA comment 
sub subA { 
    print "A\n"; 
} 

sub subB { 
    print "B\n"; 
} 

1; 
+0

Спасибо! Я думаю, что это должно быть самое общее и надежное решение, и это также отлично работает! У меня все еще есть небольшая проблема: насколько мы можем быть уверены в том, что отсортированный файл представляет собой тот же Perl-код, что и исходный файл? (Документация для ИЦП говорит, что это «Безопасная поездка в оба конца», но это касается случая, когда вы не меняете дерево.) .. Есть ли способ программно проверить это утверждение? (Я посмотрел на 'B :: Deparse', но я не уверен, что это правильный инструмент.) –

+0

(.. contd ..) Это может быть критично или, по крайней мере, раздражать (если у вас все еще есть резервная копия когда вы обнаружили ошибку), если код каким-то образом перепутался) –

+2

'PPI' - очень простой модуль, поэтому я не ожидаю никаких проблем с ним. Кроме того, я тоже очень уверен в своем коде. Однако, как отметил @tobyink, порядок подсетей может повлиять на интерпретацию кода, если вы используете прототипы. Если вы никогда не используете те (чего я не знаю), то вы, вероятно, все в порядке. И для дополнительной безопасности вы можете легко реализовать это с помощью $ INPLACE_EDIT и резервных копий. – Miller

1

Во-первых, вот мое решение;

#!/bin/sh 
TOKEN=sub 

gsed -e ':a;N;$!ba;s/\n/__newline__/g' "$1" > "$1.out" 
gsed -i "s/__newline__\\s*$TOKEN\W/\\nsub /g" "$1.out" 
sort $1.out -o $1.out 
gsed -i 's/__newline__/\n/g' $1.out 

Использование: token_sort.sh myfile.pl

Вот как это работает;

  • Заменить все символы новой строки с заполнителем, __newline__
  • вырваться из всех $TOKENS, в этом случае подводные лодки, к их собственной линии
  • Сортировка линий с помощью UNIX сортировать
  • Заменить обратно все переводы строк
  • Теперь у вас должна быть отсортированная копия вашего файла в myfile.pl.out

Несколько оговорок;

  • Добавить комментарий «# Something» или «#!/Usr/bin/env perl» в начало файла; это обеспечит сортировку блока заголовка вверху.
  • Отсортированный блок будет началом текущего суб-комментария к следующим суб-комментариям выше, суб будет сортироваться с предыдущим под.
  • Вы должны использовать гну-СЭД для этой работы, на макинтош это означает делать «варево установить гну-СЭД»
+1

Спасибо, это был хитрый трюк! Обратите внимание, что 'sub' может возникать внутри других субтитров или внутри хэшей. Эти подписи не должны учитываться при сортировке. Например, запись 'my $ func = sub {print" Hello \ n "};' дает переменную, ссылающуюся на анонимную подпрограмму. –

+1

Что произойдет, если sub произойдет внутри строки? Например, 'my $ str =" Это подкатегория ";'? –

+1

@ Håkon Hægland; Короткий ответ - мое решение не справляется с этим очень хорошо. Миллер обеспечил отличное решение для perl. Мина менее надежна, но ее можно использовать в качестве общего случая на других языках программирования или в ситуациях, когда блок сортировки может быть назначен общим токеном. – harvey

Смежные вопросы