Bina jurubahasa di Java - Laksanakan mesin pelaksanaan

Sebelumnya 1 2 3 Halaman 2 Seterusnya Halaman 2 dari 3

Aspek lain: Rentetan dan tatasusunan

Dua bahagian lain dari bahasa ASAS dilaksanakan oleh jurubahasa COCOA: rentetan dan tatasusunan. Mari kita lihat pelaksanaan rentetan terlebih dahulu.

Untuk menerapkan rentetan sebagai pemboleh ubah, Expressionkelas diubah untuk memasukkan pengertian ungkapan "string". Pengubahsuaian ini berbentuk dua penambahan: isStringdan stringValue. Sumber untuk dua kaedah baru ini ditunjukkan di bawah.

String stringValue (Program pgm) melemparkan BASICRuntimeError {lemparkan BASICRuntimeError baru ("Perwakilan Tanpa String untuk ini."); } boolean isString () {return false; }

Jelas, tidak terlalu berguna untuk program BASIC untuk mendapatkan nilai rentetan ungkapan dasar (yang selalu berupa ungkapan berangka atau boolean). Anda mungkin menyimpulkan dari kekurangan utiliti bahawa kaedah ini kemudian tidak dimiliki Expressiondan dimiliki oleh subkelas sebagai Expressiongantinya. Namun, dengan meletakkan kedua kaedah ini di kelas dasar, semua Expressionobjek dapat diuji untuk melihat apakah, sebenarnya, itu adalah rentetan.

Pendekatan reka bentuk lain adalah mengembalikan nilai numerik sebagai rentetan menggunakan StringBufferobjek untuk menghasilkan nilai. Jadi, sebagai contoh, kod yang sama dapat ditulis semula sebagai:

String stringValue (Program pgm) melemparkan BASICRuntimeError {StringBuffer sb = StringBuffer baru (); sb.append (nilai ini. (pgm)); kembali sb.toString (); }

Dan jika kod di atas digunakan, anda boleh menghilangkan penggunaannya isStringkerana setiap ungkapan dapat mengembalikan nilai rentetan. Selanjutnya, anda dapat mengubah valuekaedah untuk mencuba mengembalikan nombor jika ungkapan tersebut dinilai menjadi rentetan dengan menjalankannya melalui valueOfmetode java.lang.Double. Dalam banyak bahasa seperti Perl, TCL, dan REXX, jenis menaip amorf digunakan untuk kelebihan. Kedua-dua pendekatan itu sah, dan anda harus membuat pilihan berdasarkan reka bentuk jurubahasa anda. Dalam ASAS, jurubahasa perlu mengembalikan kesalahan ketika rentetan diberikan ke pemboleh ubah angka, jadi saya memilih pendekatan pertama (mengembalikan kesalahan).

Mengenai tatasusunan, terdapat pelbagai cara di mana anda dapat merancang bahasa anda untuk menafsirkannya. C menggunakan tanda kurung persegi di sekitar elemen array untuk membezakan rujukan indeks array dari rujukan fungsi yang mempunyai tanda kurung di sekitar argumen mereka. Namun, perancang bahasa untuk BASIC memilih untuk menggunakan tanda kurung untuk kedua fungsi dan tatasusunan sehingga ketika teks NAME(V1, V2)dilihat oleh pengurai, itu boleh menjadi fungsi panggilan atau rujukan array.

Penganalisis leksikal membezakan antara token yang diikuti oleh tanda kurung dengan terlebih dahulu menganggap bahawa ia adalah fungsi dan ujian untuk itu. Kemudian terus melihat apakah kata kunci atau pemboleh ubah. Keputusan inilah yang menghalang program anda untuk menentukan pemboleh ubah bernama "SIN." Mana-mana pemboleh ubah yang namanya sesuai dengan nama fungsi akan dikembalikan oleh penganalisis leksikal sebagai token fungsi sebagai gantinya. Trik kedua yang digunakan penganalisis leksikal adalah untuk memeriksa untuk mengetahui apakah nama pemboleh ubah segera diikuti oleh ` MYARRAY ( 2 )'dari ditafsirkan sebagai larik yang sah (perhatikan ruang antara nama pemboleh ubah dan kurungan terbuka).

Trik terakhir untuk melaksanakan tatasusunan adalah di Variablekelas. Kelas ini digunakan untuk contoh pemboleh ubah, dan seperti yang saya bincangkan dalam lajur bulan lalu, itu adalah subkelas dari Token. Walau bagaimanapun, ia juga mempunyai beberapa mesin untuk menyokong tatasusunan dan itulah yang akan saya tunjukkan di bawah:

pemboleh ubah kelas meluaskan Token {// Sub jenis pemboleh ubah undang-undang akhir statik int NOMBOR = 0; int stating akhir STRING = 1; int statik akhir NUMBER_ARRAY = 2; int statik akhir STRING_ARRAY = 4; Nama rentetan; subjenis int; / * * Sekiranya pemboleh ubah berada dalam jadual simbol nilai-nilai ini * diinisialisasi. * / int ndx []; // indeks tatasusunan. int mult []; // pengganda array berganda nArrayValues ​​[]; Rentetan sArrayValues ​​[];

Kod di atas menunjukkan pemboleh ubah contoh yang berkaitan dengan pemboleh ubah, seperti di ConstantExpressionkelas. Kita mesti membuat pilihan mengenai bilangan kelas yang akan digunakan berbanding dengan kerumitan kelas. Satu pilihan reka bentuk mungkin untuk membina Variablekelas yang hanya mempunyai pemboleh ubah skalar dan kemudian menambahkan ArrayVariablesubkelas untuk menangani selok-belok tatasusunan. Saya memilih untuk menggabungkannya, mengubah pemboleh ubah skalar pada dasarnya menjadi tatasusunan panjang 1.

Sekiranya anda membaca kod di atas, anda akan melihat indeks array dan pengganda. Ini ada di sini kerana tatasusunan multidimensi dalam ASAS dilaksanakan dengan menggunakan array Java linear tunggal. Indeks linier ke dalam array Java dihitung secara manual menggunakan unsur-unsur array pengganda. Indeks yang digunakan dalam program BASIC diperiksa untuk kesahan dengan membandingkannya dengan indeks undang-undang maksimum dalam array ndx indeks .

Sebagai contoh, susunan BASIC dengan tiga dimensi 10, 10, dan 8, akan mempunyai nilai 10, 10, dan 8 yang disimpan dalam ndx. Ini membolehkan penilai ekspresi menguji keadaan "indeks di luar batasan" dengan membandingkan nombor yang digunakan dalam program BASIC dengan bilangan sah maksimum yang kini disimpan dalam ndx. Susunan pengganda dalam contoh kami akan mengandungi nilai 1, 10, dan 100. Pemalar ini mewakili nombor yang digunakan untuk memetakan dari spesifikasi indeks array multidimensi menjadi spesifikasi indeks array linear. Persamaan sebenarnya ialah:

Indeks Java = Indeks1 + Indeks2 * Saiz Maksimum Indeks1 + Indeks3 * (Ukuran Maksimum Indeks1 * MaxSizeIndex 2)

Susunan Java seterusnya dalam Variablekelas ditunjukkan di bawah.

 Ekspresi exp []; 

The expns mudah digunakan untuk berurusan dengan pameran yang ditulis sebagai " A(10*B, i)." Dalam kes itu, indeks sebenarnya adalah ungkapan dan bukan pemalar, jadi rujukan harus mengandungi petunjuk kepada ungkapan yang dinilai pada waktu berjalan. Akhirnya ada sekeping kod yang kelihatan agak jelek yang mengira indeks bergantung pada apa yang diteruskan dalam program. Kaedah peribadi ini ditunjukkan di bawah.

int int computeIndex (int ii []) melemparkan BASICRuntimeError {int offset = 0; jika ((ndx == null) || (ii.length! = ndx.length)) buang BASICRuntimeError baru ("Bilangan indeks yang salah."); untuk (int i = 0; i <ndx.length; i ++) {if ((ii [i] ndx [i])) membuang BASICRuntimeError baru ("Indeks di luar julat."); mengimbangi = mengimbangi + (ii [i] -1) * mult [i]; } mengimbangi kembali; }

Melihat kod di atas, anda akan perhatikan bahawa kod tersebut terlebih dahulu memeriksa untuk melihat bahawa bilangan indeks yang betul digunakan ketika merujuk pada array, dan kemudian setiap indeks berada dalam julat yang sah untuk indeks tersebut. Sekiranya ralat dikesan, pengecualian dilemparkan kepada jurubahasa. Kaedah numValuedan stringValuemengembalikan nilai dari pemboleh ubah sebagai nombor atau rentetan masing-masing. Kedua-dua kaedah ini ditunjukkan di bawah.

double numValue (int ii []) melemparkan BASICRuntimeError {return nArrayValues ​​[computeIndex (ii)]; } String stringValue (int ii []) melemparkan BASICRuntimeError {if (subType == NUMBER_ARRAY) return "" + nArrayValues ​​[computeIndex (ii)]; pulangkan sArrayValues ​​[computeIndex (ii)]; }

Terdapat kaedah tambahan untuk menetapkan nilai pemboleh ubah yang tidak ditunjukkan di sini.

Dengan menyembunyikan banyak kerumitan bagaimana setiap bagian dilaksanakan, ketika akhirnya tiba waktunya untuk melaksanakan program BASIC, kode Java cukup mudah.

Menjalankan kod

Kod untuk menafsirkan pernyataan ASAS dan melaksanakannya terkandung dalam

run

kaedah

Program

kelas. Kod untuk kaedah ini ditunjukkan di bawah, dan saya akan melaluinya untuk menunjukkan bahagian yang menarik.

1 larian kekosongan awam (InputStream in, OutputStream out) melemparkan BASICRuntimeError {2 PrintStream cemberut; 3 Penghitungan e = stmts.elements (); 4 stmtStack = Tumpukan baru (); // anggap tidak ada penyataan yang tersusun ... 5 dataStore = vektor baru (); // ... dan tiada data untuk dibaca. 6 dataPtr = 0; 7 Penyataan s; 8 9 vars = RedBlackTree baru (); 10 11 // jika program ini belum sah. 12 jika (! E.hasMoreElements ()) 13 kembali; 14 15 jika (keluar instance PrintStream) {16 pout = (PrintStream) keluar; 17} lain {18 pout = PrintStream baru (keluar); 19}

Kod di atas menunjukkan bahawa runkaedah ini memerlukan InputStreamdan OutputStreamdigunakan sebagai "konsol" untuk program yang dijalankan. Pada baris 3, objek penghitungan e diatur ke set pernyataan dari koleksi bernama stmts . Untuk koleksi ini, saya menggunakan variasi pada pokok carian binari yang disebut pokok "merah-hitam". (Untuk maklumat lebih lanjut mengenai pokok carian binari, lihat lajur saya sebelumnya mengenai membina koleksi generik.) Selepas itu, dua koleksi tambahan dibuat - satu menggunakan a Stackdan satu menggunakanVector. Tumpukan digunakan seperti tumpukan di mana-mana komputer, tetapi vektor digunakan dengan jelas untuk penyataan DATA dalam program BASIC. Koleksi terakhir adalah pokok merah-hitam lain yang menyimpan rujukan untuk pemboleh ubah yang ditentukan oleh program BASIC. Pokok ini adalah jadual simbol yang digunakan oleh program semasa ia dijalankan.

Setelah inisialisasi, aliran input dan output disiapkan, dan kemudian jika e tidak nol, kita mulai dengan mengumpulkan data yang telah dinyatakan. Itu dilakukan seperti yang ditunjukkan dalam kod berikut.

/ * Pertama kita memuatkan semua penyataan data * / while (e.hasMoreElements ()) {s = (Pernyataan) e.nextElement (); jika (s.keyword == Statement.DATA) {s.execute (ini, dalam, cemberut); }}

Gelung di atas hanya melihat semua pernyataan, dan setiap penyataan DATA yang dijumpainya kemudian dijalankan. Pelaksanaan setiap pernyataan DATA memasukkan nilai-nilai yang dinyatakan oleh pernyataan itu ke dalam vektor dataStore . Seterusnya kita melaksanakan program dengan betul, yang dilakukan dengan menggunakan kod berikut:

e = stmts.elements (); s = (Penyataan) e.nextElement (); buat {int yyy; / * Semasa berjalan kami melangkau penyataan Data. * / cuba {yyy = in.available (); } tangkapan (IOException ez) {yyy = 0; } if (yyy! = 0) {pout.println ("Berhenti pada:" + s); tolak; rehat; } if (s.keyword! = Statement.DATA) {if (traceState) {s.trace (ini, (traceFile! = null)? traceFile: pout); } s = s.execute (ini, dalam, cemberut); } lain s = nextStatement (s); } sementara (s! = null); }

Seperti yang anda lihat dalam kod di atas, langkah pertama adalah melakukan inisialisasi semula e . Langkah seterusnya adalah mengambil pernyataan pertama ke dalam pemboleh ubah s dan kemudian memasuki gelung pelaksanaan. Terdapat beberapa kod untuk memeriksa input yang tertunda pada aliran input untuk memungkinkan kemajuan program terganggu dengan mengetik pada program, dan kemudian loop memeriksa untuk melihat apakah pernyataan yang akan dilaksanakan adalah pernyataan DATA. Sekiranya ada, gelung melangkau penyataan kerana ia sudah dilaksanakan. Teknik yang agak rumit untuk melaksanakan semua penyataan data terlebih dahulu diperlukan kerana BASIC membenarkan penyataan DATA yang memuaskan pernyataan BACA muncul di mana sahaja dalam kod sumber. Akhirnya, jika penjejakan diaktifkan, rekod jejak dicetak dan pernyataan yang sangat tidak mengesankans = s.execute(this, in, pout);dipanggil. Keindahannya adalah bahawa semua usaha merangkumi konsep asas ke dalam kelas yang mudah difahami menjadikan kod terakhir itu remeh. Sekiranya tidak sepele maka mungkin anda mempunyai petunjuk bahawa mungkin ada cara lain untuk memisahkan reka bentuk anda.

Mengemas dan berfikir lebih jauh

The interpreter was designed so that it could run as a thread, thus there can be several COCOA interpreter threads running simultaneously in your program space at the same time. Further, with the use of function expansion we can provide a means whereby those threads can interact with each other. There was a program for the Apple II and later for the PC and Unix called C-robots that was a system of interacting "robotic" entities that were programmed using a simple BASIC derivative language. The game provided me and others with many hours of entertainment but was also an excellent way to introduce the basic principles of computation to younger students (who mistakenly believed they were just playing and not learning). Java based interpreter subsystems are much more powerful than their pre-Java counterparts because they are instantly available on any Java platform. COCOA ran on Unix systems and Macintoshes the same day I got working on a Windows 95 based PC. While Java gets beaten up by incompatibilities in the thread or window toolkit implementations, what is often overlooked is this: A lot of code "just works."