Parrot - 编程示例



Parrot 编程类似于汇编语言编程,您有机会在更低级别进行工作。以下是编程示例列表,让您了解 Parrot 编程的各个方面。

经典的 Hello world!

创建一个名为 hello.pir 的文件,其中包含以下代码

.sub _main
   print "Hello world!\n"
   end
.end

然后通过输入以下命令运行它

parrot hello.pir

如预期的那样,这将在控制台上显示文本“Hello world!”,后面跟着一个换行符(由于 \n)。

在上面的示例中,“.sub _main”表示后面的指令构成一个名为“_main”的子程序,直到遇到“.end”。第二行包含打印指令。在这种情况下,我们正在调用接受常量字符串的指令变体。汇编器负责为我们决定使用哪个变体的指令。第三行包含“end”指令,它导致解释器终止。

使用寄存器

我们可以修改 hello.pir 以首先将字符串 Hello world!\n 存储在寄存器中,然后使用该寄存器与打印指令。

.sub _main
   set S1, "Hello world!\n"
   print S1
   end
.end

在这里,我们准确地说明了要使用哪个寄存器。但是,通过用 $S1 替换 S1,我们可以将选择要使用哪个寄存器的任务委托给 Parrot。也可以使用 = 符号而不是编写 set 指令。

.sub _main
   $S0 = "Hello world!\n"
   print $S0
   end
.end

为了使 PIR 更具可读性,可以使用命名寄存器。这些稍后会映射到实际的编号寄存器。

.sub _main
   .local string hello
   hello = "Hello world!\n"
   print hello
   end
.end

“.local”指令指示命名寄存器仅在当前编译单元内需要(即,在 .sub 和 .end 之间)。“.local”后面跟着一个类型。这可以是 int(对于 I 寄存器)、float(对于 N 寄存器)、string(对于 S 寄存器)、pmc(对于 P 寄存器)或 PMC 类型的名称。

求平方和

此示例介绍了一些其他的指令和 PIR 语法。以 # 开头的行是注释。

.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 提供了一些语法糖,使其看起来比汇编更高级。例如

temp = i * i

只是另一种编写更类似汇编的

mul temp, i, i

以及

if i <= maxnum goto loop

与以下内容相同

le i, maxnum, loop

以及

total += temp

与以下内容相同

add total, temp

作为规则,每当 Parrot 指令修改寄存器的内容时,这将是使用汇编形式编写指令时的第一个寄存器。

与汇编语言中通常一样,循环和选择是根据条件分支语句和标签实现的,如上所示。汇编编程是一个使用 goto 不是坏形式的地方!

斐波那契数列

斐波那契数列定义如下:取两个数字,1 和 1。然后重复将数列中的最后两个数字加在一起以构成下一个数字:1、1、2、3、5、8、13,依此类推。斐波那契数 fib(n) 是数列中的第 n 个数字。这是一个简单的 Parrot 汇编程序,它查找前 20 个斐波那契数

# 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

这是 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++;
}

注意:作为有趣的一点,在 Perl 中打印斐波那契数列最短且肯定是最漂亮的方法之一是 perl -le '$b=1; print $a+=$b while print $b+=$a'。

递归计算阶乘

在此示例中,我们定义了一个阶乘函数并递归调用它来计算阶乘。

.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

让我们首先看一下 _fact 子程序。之前略过的一点是为什么子程序的名称都以下划线开头!这样做仅仅是为了表明标签是全局的而不是作用域到特定子程序。这一点很重要,因为该标签随后对其他子程序可见。

第一行 .param int n 指定此子程序接受一个整数参数,并且我们希望在子程序的其余部分中使用名称 n 来引用它传递的寄存器。

接下来的大部分内容都在之前的示例中见过,除了读取的行

result = _fact($I0)

这行 PIR 实际上表示相当多的 PASM 行。首先,寄存器 $I0 中的值被移动到相应的寄存器中,以便它作为整数参数被 _fact 函数接收。然后设置其他与调用相关的寄存器,然后调用 _fact。然后,一旦 _fact 返回,_fact 返回的值将放置到名为 result 的寄存器中。

_fact 子程序的 .end 之前,使用 .return 指令来确保寄存器中保存的值;名为 result 的值被放置到正确的寄存器中,以便它被调用子程序的代码视为返回值。

main 中对 _fact 的调用与 _fact 子程序本身内的递归调用 _fact 的方式相同。唯一剩下的新语法是 :main,它写在 .sub _main 之后。默认情况下,PIR 假设执行从文件中的第一个子程序开始。可以通过标记要从中开始的子程序来更改此行为,并使用 :main。

编译成 PBC

要将 PIR 编译成字节码,请使用 -o 标志并使用扩展名为 .pbc 的输出文件。

parrot -o factorial.pbc factorial.pir

PIR 与 PASM

可以通过运行以下命令将 PIR 转换为 PASM

parrot -o hello.pasm hello.pir

最终示例的 PASM 如下所示

_main:
   set S30, "Hello world!\n"
   print S30
end

PASM 不处理寄存器分配或提供对命名寄存器的支持。它也没有 .sub 和 .end 指令,而是用指令开头的标签替换它们。

广告