Parrot - Esempi di programmazione
La programmazione di Parrot è simile alla programmazione in linguaggio assembly e hai la possibilità di lavorare a un livello inferiore. Di seguito è riportato un elenco di esempi di programmazione per farti conoscere i vari aspetti della programmazione Parrot.
Ciao mondo classico!
Crea un file chiamato hello.pir che contiene il codice seguente:
.sub _main
print "Hello world!\n"
end
.end
Quindi eseguilo digitando:
parrot hello.pir
Come previsto, verrà visualizzato il testo "Hello world!" sulla console, seguito da una nuova riga (a causa del \ n).
In questo esempio precedente, ".sub _main" afferma che le istruzioni che seguono costituiscono una subroutine denominata "_main", finché non viene incontrato un ".end". La seconda riga contiene l'istruzione di stampa. In questo caso, stiamo chiamando la variante dell'istruzione che accetta una stringa costante. L'assemblatore si occupa di decidere quale variante dell'istruzione utilizzare per noi. La terza riga contiene l'istruzione "end", che fa terminare l'interprete.
Utilizzo dei registri
Possiamo modificare hello.pir per memorizzare prima la stringa Hello world! \ N in un registro e quindi utilizzare quel registro con l'istruzione di stampa.
.sub _main
set S1, "Hello world!\n"
print S1
end
.end
Qui abbiamo indicato esattamente quale registro utilizzare. Tuttavia, sostituendo S1 con $ S1 possiamo delegare a Parrot la scelta di quale registro utilizzare. È anche possibile utilizzare una notazione = invece di scrivere l'istruzione set.
.sub _main
$S0 = "Hello world!\n"
print $S0
end
.end
Per rendere il PIR ancora più leggibile, è possibile utilizzare registri con nome. Questi vengono successivamente mappati su registri numerati reali.
.sub _main
.local string hello
hello = "Hello world!\n"
print hello
end
.end
La direttiva ".local" indica che il registro denominato è necessario solo all'interno dell'unità di compilazione corrente (ovvero, tra .sub e .end). Il seguente ".local" è un tipo. Questo può essere int (per i registri I), float (per N registri), string (per i registri S), pmc (per i registri P) o il nome di un tipo PMC.
Somma dei quadrati
Questo esempio introduce altre istruzioni e la sintassi PIR. Le righe che iniziano con un # sono commenti.
.sub _main
# State the number of squares to sum.
.local int maxnum
maxnum = 10
# Some named registers we'll use.
# Note how we can declare many
# registers of the same type on one line.
.local int i, total, temp
total = 0
# Loop to do the sum.
i = 1
loop:
temp = i * i
total += temp
inc i
if i <= maxnum goto loop
# Output result.
print "The sum of the first "
print maxnum
print " squares is "
print total
print ".\n"
end
.end
Il PIR fornisce un po 'di zucchero sintattico che lo fa sembrare di livello più alto rispetto all'assemblaggio. Per esempio:
temp = i * i
È solo un altro modo di scrivere più assembly-ish:
mul temp, i, i
E:
if i <= maxnum goto loop
Equivale a:
le i, maxnum, loop
E:
total += temp
Equivale a:
add total, temp
Di norma, ogni volta che un'istruzione Parrot modifica il contenuto di un registro, quello sarà il primo registro quando si scrive l'istruzione in forma di assemblaggio.
Come di consueto nei linguaggi assembly, i cicli e le selezioni sono implementati in termini di istruzioni ed etichette di rami condizionali, come mostrato sopra. La programmazione in assembly è un posto in cui usare goto non è una cattiva forma!
Numeri di Fibonacci
La serie di Fibonacci è definita in questo modo: prendi due numeri, 1 e 1. Quindi somma ripetutamente gli ultimi due numeri della serie per formare il successivo: 1, 1, 2, 3, 5, 8, 13 e così via . Il numero di Fibonacci fib (n) è l'ennesimo numero della serie. Ecco un semplice programma di assemblaggio Parrot che trova i primi 20 numeri di Fibonacci:
# Some simple code to print some Fibonacci numbers
print "The first 20 fibonacci numbers are:\n"
set I1, 0
set I2, 20
set I3, 1
set I4, 1
REDO: eq I1, I2, DONE, NEXT
NEXT: set I5, I4
add I4, I3, I4
set I3, I5
print I3
print "\n"
inc I1
branch REDO
DONE: end
Questo è il codice equivalente in Perl:
print "The first 20 fibonacci numbers are:\n";
my $i = 0;
my $target = 20;
my $a = 1;
my $b = 1;
until ($i == $target) {
my $num = $b;
$b += $a;
$a = $num;
print $a,"\n";
$i++;
}
NOTE:Un bel punto di interesse, uno dei modi più brevi e certamente più belli per stampare una serie di Fibonacci in Perl è perl -le '$ b = 1; print $ a + = $ b mentre print $ b + = $ a '.
Calcolo ricorsivo fattoriale
In questo esempio definiamo una funzione fattoriale e la chiamiamo ricorsivamente per calcolare fattoriale.
.sub _fact
# Get input parameter.
.param int n
# return (n > 1 ? n * _fact(n - 1) : 1)
.local int result
if n > 1 goto recurse
result = 1
goto return
recurse:
$I0 = n - 1
result = _fact($I0)
result *= n
return:
.return (result)
.end
.sub _main :main
.local int f, i
# We'll do factorial 0 to 10.
i = 0
loop:
f = _fact(i)
print "Factorial of "
print i
print " is "
print f
print ".\n"
inc i
if i <= 10 goto loop
# That's it.
end
.end
Diamo prima un'occhiata al sub _fact. Un punto che è stato sorvolato in precedenza è il motivo per cui i nomi delle subroutine iniziano tutti con un trattino basso! Questo viene fatto semplicemente come un modo per mostrare che l'etichetta è globale piuttosto che limitata a una particolare subroutine. Ciò è significativo in quanto l'etichetta è quindi visibile ad altre subroutine.
La prima riga, .param int n, specifica che questa subroutine accetta un parametro intero e che vorremmo fare riferimento al registro in cui è stato passato con il nome n per il resto del sub.
Molto di ciò che segue è stato visto negli esempi precedenti, a parte la lettura della riga:
result = _fact($I0)
Questa singola riga di PIR in realtà rappresenta alcune righe di PASM. Per prima cosa, il valore nel registro $ I0 viene spostato nel registro appropriato per essere ricevuto come parametro intero dalla funzione _fact. Vengono quindi impostati altri registri relativi alle chiamate, seguiti dall'invocazione di _fact. Quindi, una volta che _fact ritorna, il valore restituito da _fact viene inserito nel registro dato il nome risultato.
Subito prima del .end del sub _fact, viene utilizzata una direttiva .return per garantire il valore contenuto nel registro; Il risultato denominato viene inserito nel registro corretto in modo che possa essere visto come valore di ritorno dal codice che chiama il sub.
La chiamata a _fact in main funziona esattamente allo stesso modo della chiamata ricorsiva a _fact all'interno del sub _fact stesso. L'unico bit rimanente della nuova sintassi è: main, scritto dopo .sub _main. Per impostazione predefinita, PIR presuppone che l'esecuzione inizi con il primo sub nel file. Questo comportamento può essere modificato contrassegnando il sottotitolo per iniziare con: main.
Compilazione su PBC
Per compilare PIR in bytecode, utilizzare il flag -o e specificare un file di output con estensione .pbc.
parrot -o factorial.pbc factorial.pir
PIR contro PASM
PIR può essere trasformato in PASM eseguendo:
parrot -o hello.pasm hello.pir
Il PASM per l'esempio finale ha questo aspetto:
_main:
set S30, "Hello world!\n"
print S30
end
PASM non gestisce l'allocazione dei registri né fornisce supporto per i registri denominati. Inoltre non ha le direttive .sub e .end, sostituendole invece con un'etichetta all'inizio delle istruzioni.