Parrot - Ejemplos de programación
La programación de Parrot es similar a la programación en lenguaje ensamblador y tiene la oportunidad de trabajar en un nivel inferior. Aquí está la lista de ejemplos de programación para que conozca los distintos aspectos de la programación Parrot.
¡Hola mundo clásico!
Cree un archivo llamado hello.pir que contenga el siguiente código:
.sub _main
print "Hello world!\n"
end
.end
Luego ejecútelo escribiendo:
parrot hello.pir
Como se esperaba, esto mostrará el texto '¡Hola mundo!' en la consola, seguida de una nueva línea (debido al \ n).
En este ejemplo anterior, '.sub _main' indica que las instrucciones que siguen forman una subrutina llamada '_main', hasta que se encuentra un '.end'. La segunda línea contiene la instrucción de impresión. En este caso, llamamos a la variante de la instrucción que acepta una cadena constante. El ensamblador se encarga de decidir qué variante de la instrucción utilizar para nosotros. La tercera línea contiene la instrucción 'end', que hace que el intérprete termine.
Usando registros
Podemos modificar hello.pir para almacenar primero la cadena ¡Hola mundo! \ N en un registro y luego usar ese registro con la instrucción de impresión.
.sub _main
set S1, "Hello world!\n"
print S1
end
.end
Aquí hemos indicado exactamente qué registro utilizar. Sin embargo, reemplazando S1 con $ S1 podemos delegar la elección de qué registro usar a Parrot. También es posible utilizar una notación = en lugar de escribir la instrucción establecida.
.sub _main
$S0 = "Hello world!\n"
print $S0
end
.end
Para que PIR sea aún más legible, se pueden utilizar registros con nombre. Estos se asignan más tarde a registros numerados reales.
.sub _main
.local string hello
hello = "Hello world!\n"
print hello
end
.end
La directiva '.local' indica que el registro nombrado solo se necesita dentro de la unidad de compilación actual (es decir, entre .sub y .end). Después de '.local' es un tipo. Puede ser int (para registros I), float (para registros N), string (para registros S), pmc (para registros P) o el nombre de un tipo de PMC.
Sumar cuadrados
Este ejemplo presenta más instrucciones y sintaxis PIR. Las líneas que comienzan con # son comentarios.
.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
PIR proporciona un poco de azúcar sintáctico que lo hace parecer de más alto nivel que el ensamblaje. Por ejemplo:
temp = i * i
Es solo otra forma de escribir más ensamblado:
mul temp, i, i
Y:
if i <= maxnum goto loop
Es lo mismo que:
le i, maxnum, loop
Y:
total += temp
Es lo mismo que:
add total, temp
Como regla general, siempre que una instrucción Parrot modifique el contenido de un registro, ese será el primer registro al escribir la instrucción en forma ensamblada.
Como es habitual en los lenguajes ensambladores, los bucles y las selecciones se implementan en términos de declaraciones y etiquetas de ramas condicionales, como se muestra arriba. ¡La programación de ensamblaje es un lugar donde usar goto no es una mala forma!
Números de Fibonacci
La serie de Fibonacci se define así: tome dos números, 1 y 1. Luego sume repetidamente los dos últimos números de la serie para hacer el siguiente: 1, 1, 2, 3, 5, 8, 13, y así sucesivamente. . El número de Fibonacci fib (n) es el número n de la serie. Aquí hay un programa ensamblador de Parrot simple que encuentra los primeros 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 es el código equivalente en 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 buen punto de interés, una de las formas más cortas y ciertamente más hermosas de imprimir una serie de Fibonacci en Perl es perl -le '$ b = 1; imprimir $ a + = $ b mientras imprime $ b + = $ a '.
Calculando factorial recursivamente
En este ejemplo definimos una función factorial y la llamamos recursivamente para calcular factorial.
.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
Veamos primero el sub _fact. Un punto que se pasó por alto anteriormente es por qué los nombres de las subrutinas, ¡todos comienzan con un guión bajo! Esto se hace simplemente como una forma de mostrar que la etiqueta es global en lugar de tener el alcance de una subrutina en particular. Esto es importante ya que la etiqueta es visible para otras subrutinas.
La primera línea, .param int n, especifica que esta subrutina toma un parámetro entero y que nos gustaría hacer referencia al registro que se le pasó con el nombre n para el resto del sub.
Mucho de lo que sigue se ha visto en ejemplos anteriores, además de la lectura de líneas:
result = _fact($I0)
Esta única línea de PIR en realidad representa bastantes líneas de PASM. Primero, el valor en el registro $ I0 se mueve al registro apropiado para que la función _fact lo reciba como un parámetro entero. Luego se configuran otros registros relacionados con la llamada, seguidos de la invocación de _fact. Luego, una vez que _fact regresa, el valor devuelto por _fact se coloca en el registro dado el nombre resultado.
Justo antes del .end del sub _fact, se usa una directiva .return para asegurar el valor que se mantiene en el registro; El resultado con nombre se coloca en el registro correcto para que el código que llama al sub lo vea como un valor de retorno.
La llamada a _fact en main funciona de la misma manera que la llamada recursiva a _fact dentro del sub _fact mismo. El único bit restante de nueva sintaxis es: main, escrito después de .sub _main. Por defecto, PIR asume que la ejecución comienza con el primer sub del archivo. Este comportamiento se puede cambiar marcando el sub para comenzar con: main.
Compilando a PBC
Para compilar PIR en bytecode, use la marca -o y especifique un archivo de salida con la extensión .pbc.
parrot -o factorial.pbc factorial.pir
PIR frente a PASM
PIR se puede convertir en PASM ejecutando:
parrot -o hello.pasm hello.pir
El PASM para el ejemplo final se ve así:
_main:
set S30, "Hello world!\n"
print S30
end
PASM no maneja la asignación de registros ni brinda soporte para registros con nombre. Tampoco tiene las directivas .sub y .end, sino que las reemplaza con una etiqueta al comienzo de las instrucciones.