- Parrot 教程
- Parrot - 首页
- Parrot - 概述
- Parrot - 安装
- Parrot - 指南
- Parrot - 垃圾回收
- Parrot - 数据类型
- Parrot - 寄存器
- Parrot - 操作
- Parrot - 分支
- Parrot 示例
- Parrot - 示例
- Parrot 资源
- Parrot - 快速指南
- Parrot - 有用资源
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 指令,而是用指令开头的标签替换它们。