Этот Perl-скрипт выполняет свою работу, делая некоторые предположения о структуре исходного файла.(Например: .hs
файл (не .lhs
), подписи находятся в строке, непосредственно предшествующей определениям, определения находятся на левом поле и т. Д.)
Он пытается обработать (пропустить) комментарии,Определения в стиле уравнений (с повторяющимися левыми сторонами) и типы, которые генерируют многострочный вывод в ghci
.
Без сомнения, многие интересные допустимые случаи не обрабатываются должным образом.Скрипт не близок к соблюдению фактического синтаксиса Haskell.
Он невероятно медленный, так как запускает сеанс ghci
для каждой функции, для которой требуется подпись.Он создает файл резервной копии File.hs.bak
, печатает найденные функции в stderr, а также подписи для функций, у которых отсутствуют подписи, и записывает обновленный исходный код в File.hs
.Он использует промежуточный файл File.hs.new
и имеет несколько проверок безопасности, чтобы избежать перезаписи вашего контента мусором.
ИСПОЛЬЗОВАТЬ НА СВОЙ РИСК.
Этот скрипт может отформатировать ваш жесткийездить, сжигать свой дом, unsafePerformIO и иметь другие нечистые побочные эффекты.Фактически, это, вероятно, будет.
Я чувствую себя таким грязным.
Протестировано на Mac OS X 10.6 Snow Leopard с парой моих собственных .hs
исходных файлов.
#!/usr/bin/env perl
use warnings;
use strict;
my $sig=0;
my $file;
my %funcs_seen = ();
my %keywords = ();
for my $kw qw(type newtype data class) { $keywords{$kw} = 1;}
foreach $file (@ARGV)
{
if ($file =~ /\.lhs$/)
{
print STDERR "$file: .lhs is not supported. Skipping.";
next;
}
if ($file !~ /\.hs$/)
{
print STDERR "$file is not a .hs file. Skipping.";
next;
}
my $ghciPreTest = `echo 1 | ghci $file`;
if ($ghciPreTest !~ /Ok, modules loaded: /)
{
print STDERR $ghciPreTest;
print STDERR "$file is not valid Haskell source file. Skipping.";
next;
}
my $module = $file;
$module =~ s/\.hs$//;
my $backup = "$file.bak";
my $new = "$module.New.hs";
-e $backup and die "Backup $backup file exists. Refusing to overwrite. Quitting";
open OLD, $file;
open NEW, ">$new";
print STDERR "Functions in $file:\n";
my $block_comment = 0;
while (<OLD>)
{
my $original_line = $_;
my $line = $_;
my $skip = 0;
$line =~ s/--.*//;
if ($line =~ /{-/) { $block_comment = 1;} # start block comment
$line =~ s/{-.*//;
if ($block_comment and $line =~ /-}/) { $block_comment=0; $skip=1} # end block comment
if ($line =~ /^ *$/) { $skip=1; } # comment/blank
if ($block_comment) { $skip = 1};
if (!$skip)
{
if (/^(('|\w)+)( +(('|\w)+))* *=/ )
{
my $object = $1;
if ((! $keywords{$object}) and !($funcs_seen{$object}))
{
$funcs_seen{$object} = 1;
print STDERR "$object\n";
my $dec=`echo ":t $1" | ghci $file | grep -A100 "^[^>]*$module>" | grep -v "Leaving GHCi\." | sed -e "s/^[^>]*$module> //"`;
unless ($sig)
{
print NEW $dec;
print STDERR $dec;
}
}
}
$sig = /^(('|\w)+) *::/;
}
print NEW $original_line;
}
close OLD;
close NEW;
my $ghciPostTest = `echo 1 | ghci $new`;
if ($ghciPostTest !~ /Ok, modules loaded: /)
{
print $ghciPostTest;
print STDERR "$new is not valid Haskell source file. Will not replace original (but you might find it useful)";
next;
} else {
rename ($file, $backup) or die "Could not make backup of $file -> $backup";
rename ($new, $file) or die "Could not make new file $new";
}
}