- 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 指令,而是用指令開頭的標籤替換它們。