Domanda

articolo intitolato "Perl non può essere analizzato, una prova formale" sta facendo il giro. Quindi, non Perl decide il significato del suo codice analizzato in "tempo di esecuzione-" o "a tempo di compilazione"?

In alcune discussioni che ho letto, ho l'impressione gli argomenti derivano dalla terminologia imprecisa, quindi per favore cercate di definire i termini tecnici nella vostra risposta. Non ho volutamente definito "run-time", "staticamente" o "analizzato" in modo che posso ottenere prospettive da persone che forse definire questi termini in modo diverso per me.

Modifica:

Non si tratta di analisi statica. La sua una questione teorica sul comportamento di Perl.

È stato utile?

Soluzione

Perl ha una fase ben definita "tempo di compilazione", che è seguita da una fase "runtime" ben definito. Tuttavia, ci sono modi di passaggio da uno all'altro. Molti linguaggi dinamici hanno costrutti eval che permettono la compilazione del nuovo codice in fase di esecuzione; in Perl l'inverso è possibile, come pure - e comune. Blocchi BEGIN (ed il blocco BEGIN implicito causata da use) invocano una fase runtime temporaneo durante tempo di compilazione. Un blocco BEGIN viene eseguito non appena viene compilato, invece di attendere per il resto della unità di compilazione (cioè file corrente o eval corrente) per compilare. Dal momento che BEGINs eseguire prima del codice che segue li viene compilato, possono influenzare la compilazione del seguente codice praticamente in qualsiasi modo (anche se in pratica le principali cose che fanno sono di importare o definire subroutine, o per abilitare il rigore o avvisi).

Un use Foo; è sostanzialmente equivalente a BEGIN { require foo; foo->import(); }, con bisogno di essere (come eval STRING) uno dei modi per richiamare di compilazione dal runtime, il che significa che siamo ormai a tempo di compilazione all'interno di runtime all'interno di tempo di compilazione e il tutto è ricorsiva.

In ogni caso, ciò che si riduce per la decidibilità di parsing Perl è che, poiché la compilazione di un bit di codice può essere influenzato dalla esecuzione di un pezzo di codice precedente (che può in teoria fare niente ), noi stessi abbiamo un tipo di situazione di arresto-problema; l'unico modo per analizzare correttamente il file di un determinato Perl in generale è la sua esecuzione.

Altri suggerimenti

Perl ha BEGIN blocchi, che corre utente codice Perl a tempo di compilazione. Questo codice può influenzare il significato di altro codice da compilare, rendendo così "impossibile" per analizzare Perl.

Per esempio, il codice:

sub foo { return "OH HAI" }

è "realmente":

BEGIN {
    *{"${package}::foo"} = sub { return "OH HAI" };
}

Ciò significa che qualcuno possa scrivere Perl come:

BEGIN {
    print "Hi user, type the code for foo: ";
    my $code = <>;
    *{"${package}::foo"} = eval $code;
}

Ovviamente, nessuno strumento di analisi statica può indovinare quale codice che l'utente sta per digitare qui. (E se l'utente dice sub ($) {} invece di sub {}, sarà anche influenzare il modo chiama a foo vengono interpretati in tutto il resto del programma, potenzialmente gettando via il parsing.)

La buona notizia è che i casi impossibili sono molto angolo-casey; tecnicamente possibile, ma quasi certamente inutile nel codice reale. Quindi, se si sta scrivendo uno strumento di analisi statica, questo sarà probabilmente causare problemi.

Per essere onesti, ogni linguaggio degno ha questo problema, o qualcosa di simile. A titolo di esempio, lanciare il codice preferito walker a questo codice Lisp:

(iter (for i from 1 to 10) (collect i))

Probabilmente Non è possibile prevedere che questo è un ciclo che produce una lista, perché la macro iter è opaco e richiederebbe particolari conoscenze per capire. La realtà è che questo è fastidioso, in teoria (non riesco a capire il mio codice senza eseguirlo, o per lo meno l'esecuzione della macro iter, che non può mai smettere di correre con questo ingresso), ma molto utili nella pratica (iterazione è facile per al programmatore di scrivere e il programmatore futuro per leggere).

Infine, un sacco di gente pensa che Perl manca di strumenti di analisi e di refactoring statici, come Java ha, a causa della relativa difficoltà di analizzarlo. Dubito che questo è vero, penso solo che il bisogno non c'è e nessuno si è preso la briga di scriverlo. (Le persone hanno bisogno di un "residuo di stoffa", quindi non c'è Perl :: Critic, per esempio.)

Ogni analisi statica ho avuto bisogno di fare di Perl per generare il codice (alcune emacs macro per il mantenimento banchi di prova e Makefile.PL) ha lavorato bene. Potrebbero casi d'angolo strani buttare fuori il mio codice? Certo, ma io non andare fuori dal mio modo di scrivere codice che è impossibile da mantenere, anche se ho potuto.

La gente ha usato un sacco di parole per spiegare varie fasi, ma è davvero una questione semplice. Durante la compilazione sorgente Perl, l'intrepreter perl può finire codice che cambia come il resto del codice analizzerà in esecuzione. Analisi statica, che viene eseguito alcun codice, mancherà questo.

In quel post PerlMonks, Jeffrey parla dei suoi articoli in The Perl Review che vanno in modo molto più dettagliato, tra cui un programma di esempio che non analizza nello stesso modo ogni volta che si esegue.

C ++ ha un problema simile nel suo modello di sistema, ma che non si ferma compilatori da compilarlo. Saranno solo uscire o eseguire sempre sui casi d'angolo in cui si applicherebbe questo tipo di argomento.

Perl ha una fase di compilazione, ma è diverso dalla maggior parte normali fasi di compilazione quando si tratta di codice. lexer di Perl trasforma il codice in token, quindi un parser analizza gettoni in modo da formare un albero di op. Tuttavia, BEGIN {} blocchi può interrompere questo processo e consentono di eseguire il codice. Quando si effettua una use. Tutti i blocchi BEGIN eseguire prima di ogni altra cosa, dandovi un modo per impostare i moduli e gli spazi dei nomi. Durante la "compilazione" nel complesso di uno script, è molto probabile che verrà utilizzato Perl per determinare come il modulo Perl dovrebbe apparire quando è fatto. sub, nuda, implica aggiungendolo al glob per il pacchetto, ma non è necessario. Ad esempio, si tratta di una (seppur, dispari) modo di creare metodi in un modulo:

package Foo;

use strict;
use warnings;
use List::Util qw/shuffle/;

my @names = qw(foo bar baz bill barn);
my @subs = (
    sub { print "baz!" },
    sub { die; },
    sub { return sub { die } },
);
@names = shuffle @names;
foreach my $index (0..$#subs) {
   no strict 'refs';
   *{$names[$index]} = $subs[$index];
}

1;

sono per interpretare questo sapere anche quello che fa! Non è molto utile, ma non è qualcosa che si può determinare prima del tempo. Ma è al 100% perl valido. Anche se questa funzione può essere abusato, può anche fare grandi compiti, come sottomarini costruire complicato che sembrano tutti molto simili, programmaticamente. Inoltre lo rende difficile sapere, con certezza, tutto ciò che fa.

Questo non vuol dire che uno script Perl non può essere 'compilato' - in Perl, compilazione è semplicemente la determinazione, ciò che in quel momento, il modulo dovrebbe essere simile. Potete farlo con un

perl -c myscript.pl

e vi dirà se sia o non può arrivare al punto in cui inizierà l'esecuzione del modulo principale. Non ci si può solo sapere da guardarla 'statico'.

Tuttavia, come PPI dimostra, possiamo avvicinarci. Veramente vicino. Chiudere abbastanza per fare cose molto interessanti, come (quasi statica) analisi del codice.

"Run Time", quindi, diventa ciò che accade dopo che tutti i blocchi BEGIN hanno eseguito. (Questa è una semplificazione, c'è molto di più a questa Vedere perlmod di più..) è ancora codice perl in esecuzione, ma è una fase separata di esecuzione, fatta dopo che tutti i blocchi di priorità superiore hanno eseguito.

cromatica ha alcuni messaggi dettagliati sul suo blog Moderna :: Perl:

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top