Parrot - Ví dụ về lập trình

Lập trình vẹt tương tự như lập trình hợp ngữ và bạn có cơ hội làm việc ở trình độ thấp hơn. Dưới đây là danh sách các ví dụ lập trình để giúp bạn biết các khía cạnh khác nhau của Lập trình vẹt.

  • Chào thế giới cổ điển!

  • Sử dụng sổ đăng ký

  • Tổng bình phương

  • Số Fibonacci

  • Tính giai thừa

  • Biên dịch sang PBC

  • PIR so với PASM

Chào thế giới cổ điển!

Tạo một tệp có tên hello.pir chứa mã sau:

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

Sau đó chạy nó bằng cách gõ:

parrot hello.pir

Như mong đợi, nó sẽ hiển thị dòng chữ 'Hello world!' trên bảng điều khiển, theo sau là một dòng mới (do \ n).

Trong ví dụ trên, '.sub _main' nói rằng các hướng dẫn theo sau tạo nên một chương trình con có tên '_main', cho đến khi gặp phải '.end'. Dòng thứ hai chứa hướng dẫn in. Trong trường hợp này, chúng tôi đang gọi biến thể của lệnh chấp nhận một chuỗi không đổi. Trình lắp ráp sẽ đảm nhận việc quyết định biến thể của hướng dẫn sẽ sử dụng cho chúng tôi. Dòng thứ ba chứa lệnh 'end', lệnh này khiến trình thông dịch kết thúc.

Sử dụng sổ đăng ký

Chúng ta có thể sửa đổi hello.pir để lưu trữ chuỗi Hello world! \ N trước tiên trong một thanh ghi và sau đó sử dụng thanh ghi đó với lệnh in.

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

Ở đây chúng tôi đã nêu chính xác đăng ký nào để sử dụng. Tuy nhiên, bằng cách thay thế S1 bằng $ S1, chúng ta có thể ủy quyền lựa chọn đăng ký nào để sử dụng cho Parrot. Cũng có thể sử dụng ký hiệu = thay vì viết lệnh thiết lập.

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

Để làm cho PIR dễ đọc hơn, có thể sử dụng các thanh ghi đã đặt tên. Sau đó chúng được ánh xạ tới các thanh ghi được đánh số thực.

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

Chỉ thị '.local' chỉ ra rằng thanh ghi được đặt tên chỉ cần thiết bên trong đơn vị biên dịch hiện tại (nghĩa là giữa .sub và .end). Theo sau '.local' là một kiểu. Đây có thể là int (đối với thanh ghi I), float (đối với thanh ghi N), chuỗi (đối với thanh ghi S), pmc (đối với thanh ghi P) hoặc tên của một loại PMC.

Tổng bình phương

Ví dụ này giới thiệu thêm một số hướng dẫn và cú pháp PIR. Các dòng bắt đầu bằng dấu # là nhận xét.

.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 cung cấp một chút cú pháp khiến nó trông cao cấp hơn so với lắp ráp. Ví dụ:

temp = i * i

Chỉ là một cách viết khác của assembly-ish:

mul temp, i, i

Và:

if i <= maxnum goto loop

Giống như:

le i, maxnum, loop

Và:

total += temp

Giống như:

add total, temp

Theo quy luật, bất cứ khi nào một lệnh Parrot sửa đổi nội dung của một thanh ghi, đó sẽ là thanh ghi đầu tiên khi viết lệnh ở dạng hợp ngữ.

Như thường lệ trong hợp ngữ, các vòng lặp và vùng chọn được thực hiện theo các câu lệnh và nhãn nhánh có điều kiện, như được hiển thị ở trên. Lập trình hợp ngữ là một nơi mà sử dụng goto không phải là một hình thức tồi!

Số Fibonacci

Dãy Fibonacci được định nghĩa như thế này: lấy hai số, 1 và 1. Sau đó cộng lại liên tục hai số cuối cùng với nhau để tạo thành dãy tiếp theo: 1, 1, 2, 3, 5, 8, 13, v.v. . Số Fibonacci fib (n) là số thứ n trong chuỗi. Đây là một chương trình hợp ngữ Parrot đơn giản để tìm 20 số Fibonacci đầu tiên:

# 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

Đây là mã tương đương trong 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:Như một điểm đáng quan tâm, một trong những cách ngắn nhất và chắc chắn đẹp nhất để in ra một chuỗi Fibonacci ở Perl là perl -le '$ b = 1; in $ a + = $ b trong khi in $ b + = $ a '.

Giai thừa tính toán đệ quy

Trong ví dụ này, chúng ta định nghĩa một hàm giai thừa và gọi một cách đệ quy nó để tính toán giai thừ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

Trước tiên hãy nhìn vào sub _fact. Một điểm đã được chú ý trước đó là tại sao tên của các chương trình con, tất cả đều bắt đầu bằng dấu gạch dưới! Điều này được thực hiện đơn giản như một cách để cho thấy rằng nhãn là toàn cục thay vì phạm vi đến một chương trình con cụ thể. Điều này rất quan trọng vì nhãn sau đó được hiển thị cho các chương trình con khác.

Dòng đầu tiên, .param int n, chỉ định rằng chương trình con này nhận một tham số số nguyên và chúng tôi muốn tham chiếu đến sổ đăng ký mà nó được chuyển vào bằng tên n cho phần còn lại của chương trình con.

Phần lớn những gì sau đây đã được nhìn thấy trong các ví dụ trước, ngoại trừ việc đọc dòng:

result = _fact($I0)

Dòng PIR đơn này thực sự đại diện cho khá nhiều dòng PASM. Đầu tiên, giá trị trong thanh ghi $ I0 được chuyển vào thanh ghi thích hợp để nó được hàm _fact nhận như một tham số nguyên. Các thanh ghi liên quan đến việc gọi khác sau đó được thiết lập, theo sau là _fact được gọi. Sau đó, khi _fact trả về, giá trị do _fact trả về sẽ được đưa vào thanh ghi với kết quả tên.

Ngay trước .end của _fact phụ, một chỉ thị .return được sử dụng để đảm bảo giá trị được giữ trong thanh ghi; kết quả được đặt tên được đặt vào đúng thanh ghi để nó được xem như một giá trị trả về bởi mã gọi sub.

Lệnh gọi tới _fact trong chính hoạt động giống như cách gọi đệ quy tới _fact bên trong chính _fact phụ. Một chút duy nhất còn lại của cú pháp mới là: main, được viết sau .sub _main. Theo mặc định, PIR giả định rằng việc thực thi bắt đầu với con đầu tiên trong tệp. Hành vi này có thể được thay đổi bằng cách đánh dấu phụ bắt đầu bằng: main.

Biên dịch sang PBC

Để biên dịch PIR sang bytecode, hãy sử dụng cờ -o và chỉ định tệp đầu ra có phần mở rộng .pbc.

parrot -o factorial.pbc factorial.pir

PIR so với PASM

PIR có thể được chuyển thành PASM bằng cách chạy:

parrot -o hello.pasm hello.pir

PASM cho ví dụ cuối cùng trông như thế này:

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

PASM không xử lý cấp phát thanh ghi hoặc cung cấp hỗ trợ cho các thanh ghi được đặt tên. Nó cũng không có các chỉ thị .sub và .end, thay vào đó hãy thay thế chúng bằng một nhãn ở đầu hướng dẫn.