Parrot - exemplos de programação
A programação do Parrot é semelhante à programação em linguagem assembly e você tem a chance de trabalhar em um nível inferior. Aqui está a lista de exemplos de programação para torná-lo ciente dos vários aspectos da Programação do Parrot.
Olá, mundo clássico!
Usando registros
Somando quadrados
Números de Fibonacci
Fatorial de computação
Compilando para PBC
PIR vs. PASM
Olá, mundo clássico!
Crie um arquivo chamado hello.pir que contém o seguinte código:
.sub _main
print "Hello world!\n"
end
.end
Em seguida, execute-o digitando:
parrot hello.pir
Como esperado, isso exibirá o texto 'Olá, mundo!' no console, seguido por uma nova linha (devido ao \ n).
Neste exemplo acima, '.sub _main' afirma que as instruções que seguem formam uma sub-rotina chamada '_main', até que um '.end' seja encontrado. A segunda linha contém a instrução de impressão. Nesse caso, estamos chamando a variante da instrução que aceita uma string constante. O montador se encarrega de decidir qual variante da instrução usar para nós. A terceira linha contém a instrução 'fim', que faz com que o interpretador termine.
Usando registros
Podemos modificar hello.pir para primeiro armazenar a string Hello world! \ N em um registro e então usar esse registro com a instrução de impressão.
.sub _main
set S1, "Hello world!\n"
print S1
end
.end
Aqui declaramos exatamente qual registro usar. No entanto, ao substituir S1 por $ S1, podemos delegar ao Parrot a escolha de qual registro usar. Também é possível usar uma notação = em vez de escrever a instrução de conjunto.
.sub _main
$S0 = "Hello world!\n"
print $S0
end
.end
Para tornar o PIR ainda mais legível, podem ser usados registradores nomeados. Estes são posteriormente mapeados para registros reais numerados.
.sub _main
.local string hello
hello = "Hello world!\n"
print hello
end
.end
A diretiva '.local' indica que o registro nomeado só é necessário dentro da unidade de compilação atual (ou seja, entre .sub e .end). Seguir '.local' é um tipo. Isso pode ser int (para registros I), float (para registros N), string (para registros S), pmc (para registros P) ou o nome de um tipo PMC.
Somando quadrados
Este exemplo apresenta mais algumas instruções e sintaxe PIR. As linhas que começam com # são comentários.
.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
O PIR fornece um pouco de açúcar sintático que o faz parecer mais de alto nível do que o assembly. Por exemplo:
temp = i * i
É apenas outra forma de escrever, mais assembly:
mul temp, i, i
E:
if i <= maxnum goto loop
É o mesmo que:
le i, maxnum, loop
E:
total += temp
É o mesmo que:
add total, temp
Via de regra, sempre que uma instrução Parrot modifica o conteúdo de um registro, este será o primeiro registro ao escrever a instrução na forma de montagem.
Como é usual em linguagens assembly, loops e seleções são implementados em termos de declarações e rótulos de desvio condicional, conforme mostrado acima. A programação de montagem é um lugar onde usar goto não é uma forma ruim!
Números de Fibonacci
A série de Fibonacci é definida assim: pegue dois números, 1 e 1. Em seguida, some repetidamente os dois últimos números da série para fazer o próximo: 1, 1, 2, 3, 5, 8, 13 e assim por diante . O número de Fibonacci fib (n) é o enésimo número da série. Aqui está um programa montador Parrot simples que encontra os primeiros 20 números de 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
Este é o código equivalente em 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:Como um bom ponto de interesse, uma das maneiras mais curtas e certamente as mais bonitas de imprimir uma série de Fibonacci em Perl é perl -le '$ b = 1; print $ a + = $ b enquanto print $ b + = $ a '.
Computando recursivamente o fatorial
Neste exemplo, definimos uma função fatorial e a chamamos recursivamente para calcular o fatorial.
.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
Vejamos primeiro o subfacto _fact. Um ponto que foi esquecido anteriormente é porque os nomes das sub-rotinas começam todos com um sublinhado! Isso é feito simplesmente como uma forma de mostrar que o rótulo é global, em vez de ter como escopo uma sub-rotina específica. Isso é significativo porque o rótulo fica então visível para outras sub-rotinas.
A primeira linha, .param int n, especifica que esta sub-rotina leva um parâmetro inteiro e que gostaríamos de nos referir ao registro que foi passado pelo nome n para o resto da sub.
Muito do que se segue foi visto em exemplos anteriores, além da leitura de linha:
result = _fact($I0)
Esta única linha de PIR realmente representa algumas linhas de PASM. Primeiro, o valor no registro $ I0 é movido para o registro apropriado para ser recebido como um parâmetro inteiro pela função _fact. Outros registros relacionados à chamada são configurados, seguidos por _fact sendo invocado. Então, uma vez que _fact retorna, o valor retornado por _fact é colocado no registro dado o nome resultado.
Logo antes do final do sub _fact, uma diretiva .return é usada para garantir o valor mantido no registro; o resultado nomeado é colocado no registro correto para ser visto como um valor de retorno pelo código que chama o sub.
A chamada para _fact em main funciona da mesma maneira que a chamada recursiva para _fact dentro do próprio sub _fact. O único pedaço restante da nova sintaxe é: main, escrito após .sub _main. Por padrão, o PIR assume que a execução começa com a primeira sub-rotina no arquivo. Este comportamento pode ser alterado marcando o sub para começar com: principal.
Compilando para PBC
Para compilar PIR para bytecode, use o sinalizador -o e especifique um arquivo de saída com a extensão .pbc.
parrot -o factorial.pbc factorial.pir
PIR vs. PASM
PIR pode ser transformado em PASM executando:
parrot -o hello.pasm hello.pir
O PASM para o exemplo final se parece com isto:
_main:
set S30, "Hello world!\n"
print S30
end
O PASM não controla a alocação de registros nem fornece suporte para registros nomeados. Ele também não tem as diretivas .sub e .end, substituindo-as por um rótulo no início das instruções.