Parrot - Panduan Cepat

Ketika kita memasukkan program kita ke Perl konvensional, program ini pertama kali dikompilasi menjadi representasi internal, atau bytecode; bytecode ini kemudian dimasukkan ke dalam subsistem yang hampir terpisah di dalam Perl untuk diinterpretasikan. Jadi ada dua fase berbeda dari operasi Perl:

  • Kompilasi ke bytecode dan

  • Interpretasi dari bytecode.

Ini tidak unik untuk Perl. Bahasa lain yang mengikuti desain ini termasuk Python, Ruby, Tcl dan bahkan Java.

Kita juga tahu bahwa ada Java Virtual Machine (JVM) yang merupakan lingkungan eksekusi platform independen yang mengubah bytecode Java menjadi bahasa mesin dan menjalankannya. Jika Anda memahami konsep ini maka Anda akan memahami Parrot.

Parrotadalah mesin virtual yang dirancang untuk mengompilasi dan mengeksekusi bytecode secara efisien untuk bahasa yang ditafsirkan. Parrot adalah target untuk kompiler Perl 6 terakhir, dan digunakan sebagai backend untuk Pugs, serta berbagai bahasa lain seperti Tcl, Ruby, Python dll.

Burung beo telah ditulis menggunakan bahasa paling populer "C".

Sebelum kita mulai, mari unduh satu salinan Parrot terbaru dan instal di komputer kita.

Link download Parrot tersedia di Parrot CVS Snapshot . Unduh versi terbaru Parrot dan untuk menginstalnya ikuti langkah-langkah berikut:

  • Buka zip dan untar file yang diunduh.

  • Pastikan Anda sudah menginstal Perl 5 di mesin Anda.

  • Sekarang lakukan hal berikut:

% cd parrot
% perl Configure.pl
Parrot Configure
Copyright (C) 2001 Yet Another Society
Since you're running this script, you obviously have
Perl 5 -- I'll be pulling some defaults from its configuration.
...
  • Kemudian Anda akan ditanyai serangkaian pertanyaan tentang konfigurasi lokal Anda; Anda hampir selalu dapat menekan return / enter untuk masing-masing.

  • Terakhir, Anda akan diminta untuk mengetik - make test_prog, dan Parrot akan berhasil membuat juru bahasa pengujian.

  • Sekarang Anda harus menjalankan beberapa tes; jadi ketik 'buat tes' dan Anda akan melihat pembacaan seperti berikut:

perl t/harness
t/op/basic.....ok,1/2 skipped:label constants unimplemented in
assembler
t/op/string....ok, 1/4 skipped:  I'm unable to write it!
All tests successful, 2 subtests skipped.
Files=2, Tests=6,......

Pada saat Anda membaca ini, mungkin ada lebih banyak tes, dan beberapa tes yang dilewati mungkin tidak dapat dilewati, tetapi pastikan tidak ada tes yang gagal!

Setelah Anda menginstal burung beo yang dapat dieksekusi, Anda dapat melihat berbagai jenis contoh yang diberikan di bagian 'Contoh' Burung beo . Anda juga dapat melihat direktori contoh di repositori burung beo.

Burung beo saat ini dapat menerima instruksi untuk mengeksekusi dalam empat bentuk. PIR (Parrot Intermediate Representation) dirancang untuk ditulis oleh orang-orang dan dihasilkan oleh penyusun. Ini menyembunyikan beberapa detail level rendah, seperti cara parameter diteruskan ke fungsi.

PASM (Parrot Assembly) berada pada level di bawah PIR - masih dapat dibaca / ditulis oleh manusia dan dapat dihasilkan oleh kompiler, tetapi penulis harus menjaga detail seperti konvensi pemanggilan dan alokasi register. PAST (Parrot Abstract Syntax Tree) memungkinkan Parrot menerima input gaya pohon sintaks abstrak - berguna bagi mereka yang menulis kompiler.

Semua bentuk input di atas secara otomatis diubah di dalam Parrot ke PBC (Parrot Bytecode). Ini seperti kode mesin, tetapi dipahami oleh juru bahasa Parrot.

Ini tidak dimaksudkan agar dapat dibaca manusia atau dapat ditulis oleh manusia, tetapi tidak seperti bentuk lain, eksekusi dapat segera dimulai tanpa memerlukan fase perakitan. Bytecode parrot adalah platform independen.

Set instruksi

Set instruksi Parrot mencakup operator aritmatika dan logika, bandingkan dan branch / jump (untuk mengimplementasikan loop, if ... then konstruksi, dll.), Menemukan dan menyimpan variabel global dan leksikal, bekerja dengan kelas dan objek, memanggil subrutin dan metode bersama dengan parameternya, I / O, utas, dan lainnya.

Seperti Java Virtual Machine, Parrot juga membuat Anda bebas dari kekhawatiran tentang de-alokasi memori.

  • Burung beo menyediakan pengumpulan sampah.

  • Program burung beo tidak perlu mengosongkan memori secara eksplisit.

  • Memori yang dialokasikan akan dibebaskan jika tidak lagi digunakan, yaitu tidak lagi direferensikan.

  • Parrot Garbage Collector bekerja secara berkala untuk menjaga memori yang tidak diinginkan.

CPU Parrot memiliki empat tipe data dasar:

  • IV

    Jenis integer; dijamin cukup lebar untuk menampung penunjuk.

  • NV

    Jenis floating-point yang tidak bergantung arsitektur.

  • STRING

    Jenis string yang diabstraksi dan tidak bergantung pengkodean.

  • PMC

    Skalar.

Tiga jenis pertama cukup jelas; jenis terakhir - Parrot Magic Cookies, sedikit lebih sulit untuk dipahami.

Apa itu PMC?

PMC adalah singkatan dari Parrot Magic Cookie. PMC merepresentasikan struktur atau tipe data yang kompleks, termasuk tipe data agregat (array, tabel hash, dll.). PMC dapat mengimplementasikan perilakunya sendiri untuk operasi aritmatika, logika, dan string yang dilakukan di atasnya, memungkinkan perilaku khusus bahasa diperkenalkan. PMC dapat dibangun di Parrot yang dapat dieksekusi atau dimuat secara dinamis saat dibutuhkan.

Mesin virtual Perl 5 saat ini adalah mesin stack. Ini mengkomunikasikan nilai antar operasi dengan menyimpannya di tumpukan. Operasi memuat nilai ke tumpukan, melakukan apa pun yang perlu dilakukan, dan mengembalikan hasilnya ke tumpukan. Ini mudah dikerjakan, tetapi lambat.

Untuk menambahkan dua angka bersama-sama, Anda perlu melakukan tiga dorongan tumpukan dan dua ledakan tumpukan. Lebih buruk lagi, tumpukan harus bertambah saat runtime, dan itu berarti mengalokasikan memori tepat saat Anda tidak ingin mengalokasikannya.

Jadi Parrot akan mematahkan tradisi yang sudah mapan untuk mesin virtual, dan menggunakan arsitektur register, yang lebih mirip dengan arsitektur CPU perangkat keras yang sebenarnya. Ini memiliki keuntungan lain. Kita dapat menggunakan semua literatur yang ada tentang cara menulis kompiler dan pengoptimal untuk CPU berbasis register untuk CPU perangkat lunak kita!

Parrot memiliki register khusus untuk setiap jenis: 32 register IV, 32 register NV, 32 register string dan 32 register PMC. Di assembler Parrot, ini diberi nama masing-masing I1 ... I32, N1 ... N32, S1 ... S32, P1 ... P32.

Sekarang mari kita lihat beberapa assembler. Kita dapat mengatur register ini dengan operator set:

set I1, 10
	set N1, 3.1415
	set S1, "Hello, Parrot"

Semua operasi Parrot memiliki format yang sama: nama operator, register tujuan, dan kemudian operan.

Ada berbagai operasi yang dapat Anda lakukan. Misalnya, kita dapat mencetak isi register atau konstanta:

set I1, 10
print "The contents of register I1 is: "
print I1
print "\n"

Instruksi diatas akan menghasilkan Isi register I1 adalah: 10

Kami dapat melakukan operasi matematika pada register:

# Add the contents of I2 to the contents of I1
add I1, I1, I2
# Multiply I2 by I4 and store in I3
mul I3, I2, I4
# Increment I1 by one
inc I1
# Decrement N3 by 1.5
dec N3, 1.5

Kami bahkan dapat melakukan beberapa manipulasi string sederhana:

set S1, "fish"
set S2, "bone"
concat S1, S2       # S1 is now "fishbone"
set S3, "w"
substr S4, S1, 1, 7
concat S3, S4       # S3 is now "wishbone"
length I1, S3       # I1 is now 8

Kode menjadi sedikit membosankan tanpa kontrol aliran; sebagai permulaan, Parrot tahu tentang percabangan dan label. Operasi cabang setara dengan goto Perl:

branch TERRY
JOHN:    print "fjords\n"
         branch END
MICHAEL: print " pining"
         branch GRAHAM
TERRY:   print "It's"
         branch MICHAEL
GRAHAM:  print " for the "
         branch JOHN
END:     end

Itu juga dapat melakukan tes sederhana untuk melihat apakah register berisi nilai sebenarnya:

set I1, 12
         set I2, 5
         mod I3, I2, I2
         if I3, REMAIND, DIVISOR
REMAIND: print "5 divides 12 with remainder "
         print I3
         branch DONE
DIVISOR: print "5 is an integer divisor of 12"
DONE:    print "\n"
         end

Inilah yang akan terlihat seperti di Perl, untuk perbandingan:

$i1 = 12;
    $i2 = 5;
    $i3 = $i1 % $i2;
    if ($i3) {
      print "5 divides 12 with remainder ";
      print $i3;
    } else {
      print "5 is an integer divisor of 12";
    }
    print "\n";
    exit;

Operator Burung Beo

Kami memiliki berbagai macam pembanding numerik: eq, ne, lt, gt, le dan ge. Perhatikan bahwa Anda tidak dapat menggunakan operator ini pada argumen dengan tipe yang berbeda; Anda bahkan mungkin perlu menambahkan sufiks _i atau _n ke op, untuk memberi tahu jenis argumen apa yang Anda gunakan, meskipun assembler harus menguraikan ini untuk Anda, pada saat Anda membaca ini.

Pemrograman burung beo mirip dengan pemrograman bahasa assembly dan Anda mendapat kesempatan untuk bekerja di tingkat yang lebih rendah. Berikut adalah daftar contoh pemrograman untuk membuat Anda mengetahui berbagai aspek Pemrograman Parrot.

  • Klasik Halo dunia!
  • Menggunakan register
  • Menjumlahkan kotak
  • Angka Fibonacci
  • Menghitung faktorial
  • Mengompilasi ke PBC
  • PIR vs. PASM

Klasik Halo dunia!

Buat file bernama hello.pir yang berisi kode berikut:

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

Kemudian jalankan dengan mengetik:

parrot hello.pir

Seperti yang diharapkan, ini akan menampilkan teks 'Hello world!' di konsol, diikuti oleh baris baru (karena \ n).

Dalam contoh di atas, '.sub _main' menyatakan bahwa instruksi yang mengikuti membuat subrutin bernama '_main', hingga '.end' ditemukan. Baris kedua berisi instruksi cetak. Dalam hal ini, kami memanggil varian dari instruksi yang menerima string konstan. Assembler akan menentukan varian instruksi mana yang akan digunakan untuk kita. Baris ketiga berisi instruksi 'end', yang menyebabkan interpreter berhenti.

Menggunakan Register

Kita bisa memodifikasi hello.pir untuk menyimpan string Hello world! \ N pertama dalam register dan kemudian menggunakan register itu dengan instruksi cetak.

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

Di sini kami telah menyatakan dengan tepat register mana yang akan digunakan. Namun, dengan mengganti S1 dengan $ S1 kita dapat mendelegasikan pilihan register mana yang akan digunakan ke Parrot. Dimungkinkan juga untuk menggunakan notasi = daripada menulis instruksi set.

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

Untuk membuat PIR lebih mudah dibaca, register bernama dapat digunakan. Ini kemudian dipetakan ke register bernomor nyata.

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

Direktif '.local' menunjukkan bahwa register bernama hanya diperlukan di dalam unit kompilasi saat ini (yaitu, antara .sub dan .end). Mengikuti '.local' adalah sebuah tipe. Ini bisa berupa int (untuk register I), float (untuk register N), string (untuk register S), pmc (untuk register P) atau nama jenis PMC.

Menjumlahkan kotak

Contoh ini memperkenalkan beberapa instruksi dan sintaks PIR. Baris yang dimulai dengan # adalah komentar.

.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 memberikan sedikit gula sintaksis yang membuatnya terlihat lebih tinggi daripada perakitan. Sebagai contoh:

temp = i * i

Apakah hanya cara lain untuk menulis lebih banyak assembly-ish:

mul temp, i, i

Dan:

if i <= maxnum goto loop

Sama dengan:

le i, maxnum, loop

Dan:

total += temp

Sama dengan:

add total, temp

Sebagai aturan, setiap kali instruksi Parrot mengubah isi register, itu akan menjadi register pertama saat menulis instruksi dalam bentuk assembly.

Seperti biasa dalam bahasa assembly, loop dan pilihan diimplementasikan dalam istilah pernyataan dan label cabang bersyarat, seperti yang ditunjukkan di atas. Pemrograman perakitan adalah salah satu tempat di mana menggunakan goto bukanlah bentuk yang buruk!

Angka Fibonacci

Deret Fibonacci didefinisikan seperti ini: ambil dua angka, 1 dan 1. Kemudian berulang kali menjumlahkan dua angka terakhir dalam deret tersebut untuk membuat angka berikutnya: 1, 1, 2, 3, 5, 8, 13, dan seterusnya . Angka Fibonacci fib (n) adalah angka ke-n dalam rangkaian tersebut. Berikut adalah program assembler Parrot sederhana yang menemukan 20 angka Fibonacci pertama:

# 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

Ini adalah kode yang setara di 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:Sebagai titik perhatian yang bagus, salah satu cara terpendek dan pasti terindah untuk mencetak deret Fibonacci di Perl adalah perl -le '$ b = 1; cetak $ a + = $ b sedangkan cetak $ b + = $ a '.

Menghitung faktorial secara rekursif

Dalam contoh ini kita mendefinisikan fungsi faktorial dan secara rekursif memanggilnya untuk menghitung faktorial.

.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

Mari kita lihat sub _fact terlebih dahulu. Satu hal yang telah diabaikan sebelumnya adalah mengapa nama-nama subrutin, semuanya dimulai dengan garis bawah! Hal ini dilakukan hanya sebagai cara untuk menunjukkan bahwa label tersebut bersifat global dan bukan mencakup subrutin tertentu. Ini penting karena label kemudian dapat dilihat oleh subrutin lain.

Baris pertama, .param int n, menetapkan bahwa subrutin ini mengambil satu parameter integer dan kita ingin merujuk ke register yang diteruskan dengan nama n untuk subrutin lainnya.

Banyak hal berikut ini telah dilihat pada contoh sebelumnya, selain dari pembacaan baris:

result = _fact($I0)

Baris tunggal PIR ini sebenarnya mewakili beberapa baris PASM. Pertama, nilai dalam register $ I0 dipindahkan ke register yang sesuai agar dapat diterima sebagai parameter integer oleh fungsi _fact. Register terkait panggilan lainnya kemudian disiapkan, diikuti dengan _fact yang dipanggil. Kemudian, setelah _fact kembali, nilai yang dikembalikan oleh _fact ditempatkan ke dalam register berdasarkan hasil nama.

Tepat sebelum .end dari sub _fact, perintah .return digunakan untuk memastikan nilai yang disimpan dalam register; hasil bernama ditempatkan ke dalam register yang benar untuk itu untuk dilihat sebagai nilai kembali oleh kode yang memanggil sub.

Panggilan ke _fact in main bekerja dengan cara yang sama seperti panggilan rekursif ke _fact dalam sub _fact itu sendiri. Bit yang tersisa dari sintaks baru adalah: main, ditulis setelah .sub _main. Secara default, PIR mengasumsikan bahwa eksekusi dimulai dengan sub pertama dalam file. Perilaku ini dapat diubah dengan menandai sub diawali dengan: main.

Mengompilasi ke PBC

Untuk mengkompilasi PIR menjadi bytecode, gunakan tanda -o dan tentukan file keluaran dengan ekstensi .pbc.

parrot -o factorial.pbc factorial.pir

PIR vs. PASM

PIR dapat diubah menjadi PASM dengan menjalankan:

parrot -o hello.pasm hello.pir

PASM untuk contoh terakhir terlihat seperti ini:

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

PASM tidak menangani alokasi register atau memberikan dukungan untuk register bernama. Itu juga tidak memiliki direktif .sub dan .end, sebagai gantinya menggantinya dengan label di awal instruksi.