Cara membina jurubahasa di Jawa, Bahagian 1: ASAS

Ketika saya memberitahu seorang rakan bahawa saya telah menulis jurubahasa ASAS di Jawa, dia tertawa begitu keras sehingga hampir menumpahkan soda yang dipegangnya di seluruh pakaiannya. "Mengapa di dunia ini Anda akan membina jurubahasa ASAS di Jawa?" adalah soalan pertama yang dapat diramal keluar dari mulutnya. Jawapannya mudah dan kompleks. Sambutan ringkasnya adalah menyenangkan menulis juru bahasa di Jawa, dan jika saya mahu menulis jurubahasa, saya mungkin juga menulis satu yang saya mempunyai kenangan indah dari awal pengkomputeran peribadi. Di sisi yang rumit, saya perhatikan bahawa banyak orang yang menggunakan Java hari ini telah melewati titik untuk membuat applet Duke yang jatuh dan beralih ke aplikasi yang serius. Selalunya, dalam membina aplikasi, anda mahu ia dapat dikonfigurasi.Mekanisme pilihan untuk konfigurasi semula adalah semacam mesin pelaksanaan dinamik.

Dikenal sebagai bahasa makro, atau bahasa konfigurasi, pelaksanaan dinamis adalah fitur yang memungkinkan aplikasi "diprogramkan" oleh pengguna. Manfaat memiliki mesin pelaksanaan yang dinamik adalah bahawa alat dan aplikasi dapat disesuaikan untuk melakukan tugas yang kompleks tanpa mengganti alat tersebut. Platform Java menawarkan pelbagai pilihan mesin pelaksanaan dinamik.

HotJava dan pilihan panas lain

Mari kita terokai secara ringkas beberapa pilihan mesin pelaksanaan dinamik yang ada dan kemudian melihat pelaksanaan jurubahasa saya secara mendalam. Enjin pelaksanaan dinamik adalah jurubahasa tertanam. Jurubahasa memerlukan tiga kemudahan untuk beroperasi:

  1. Kaedah untuk memuatkan arahan
  2. Format modul, untuk menyimpan arahan yang akan dilaksanakan
  3. Model atau persekitaran untuk berinteraksi dengan program tuan rumah

HotJava

Jurubahasa tertanam yang paling terkenal ialah persekitaran "applet" HotJava yang telah membentuk semula cara orang melihat penyemak imbas Web.

Model "applet" HotJava didasarkan pada gagasan bahawa aplikasi Java dapat membuat kelas dasar generik dengan antara muka yang diketahui, dan kemudian memuat subkelas kelas itu secara dinamis dan melaksanakannya pada waktu berjalan. Applet ini memberikan kemampuan baru dan, dalam lingkungan kelas asas, menyediakan pelaksanaan yang dinamik. Keupayaan pelaksanaan dinamik ini adalah bahagian mendasar dari lingkungan Java dan salah satu perkara yang menjadikannya sangat istimewa. Kami akan melihat persekitaran khusus ini secara mendalam di ruangan seterusnya.

EMAS GNU

Sebelum HotJava tiba, mungkin aplikasi yang paling berjaya dengan pelaksanaan dinamik adalah GNU EMACS. Bahasa makro seperti LISP editor ini telah menjadi pokok bagi banyak pengaturcara. Secara ringkas, persekitaran EMACS LISP terdiri daripada jurubahasa LISP dan banyak fungsi jenis penyuntingan yang dapat digunakan untuk menyusun makro yang paling kompleks. Ia tidak boleh dianggap mengejutkan bahawa editor EMACS pada asalnya ditulis dalam makro yang direka untuk editor bernama TECO. Oleh itu, ketersediaan bahasa makro yang kaya (jika tidak dapat dibaca) di TECO membolehkan penyunting yang sama sekali baru. Hari ini, GNU EMACS adalah penyunting asas, dan keseluruhan permainan ditulis tidak lebih daripada kod EMACS LISP, yang dikenali sebagai kod el. Keupayaan konfigurasi ini telah menjadikan GNU EMACS sebagai penyunting utama,sementara terminal VT-100 yang dirancang untuk berjalan telah menjadi nota kaki hanya di ruangan penulis.

REXX

Salah satu bahasa kegemaran saya, yang tidak pernah berjaya, adalah REXX, yang direka oleh Mike Cowlishaw dari IBM. Syarikat memerlukan bahasa untuk mengawal aplikasi pada mainframe besar yang menjalankan sistem operasi VM. Saya menemui REXX di Amiga di mana ia digabungkan dengan pelbagai aplikasi melalui "port REXX." Pelabuhan ini membolehkan aplikasi digerakkan dari jarak jauh melalui jurubahasa REXX. Gandingan jurubahasa dan aplikasi ini mewujudkan sistem yang jauh lebih kuat daripada yang mungkin dilakukan dengan bahagian komponennya. Nasib baik, bahasa itu hidup di NETREXX, versi yang ditulis Mike yang disusun ke dalam kod Java.

Semasa saya melihat NETREXX dan bahasa yang jauh lebih awal (LISP di Java), saya merasa bahawa bahasa-bahasa ini membentuk bahagian penting dari kisah aplikasi Java. Apa cara yang lebih baik untuk menceritakan bahagian cerita ini daripada melakukan sesuatu yang menyeronokkan di sini - seperti menghidupkan semula BASIC-80? Lebih penting lagi, akan berguna untuk menunjukkan satu cara bahasa skrip dapat ditulis di Java dan, melalui penyatuan mereka dengan Java, menunjukkan bagaimana mereka dapat meningkatkan kemampuan aplikasi Java Anda.

Keperluan ASAS untuk meningkatkan aplikasi Java anda

ASAS adalah bahasa asas. Terdapat dua aliran pemikiran tentang bagaimana seseorang boleh menulis penafsir untuknya. Salah satu pendekatannya ialah menulis gelung pengaturcaraan di mana program jurubahasa membaca satu baris teks dari program yang ditafsirkan, menghuraikannya, dan kemudian memanggil subrutin untuk melaksanakannya. Urutan membaca, menghurai, dan melaksanakan diulang sehingga salah satu pernyataan program yang ditafsirkan menyuruh jurubahasa berhenti.

Cara kedua dan lebih menarik untuk menyelesaikan projek ini sebenarnya ialah menguraikan bahasa menjadi pokok parse dan kemudian melaksanakan pokok parse "di tempatnya." Ini adalah bagaimana jurubahasa token beroperasi dan cara saya memilih untuk meneruskannya. Penafsir token juga lebih cepat kerana mereka tidak perlu mengimbas kembali input setiap kali mereka melakukan pernyataan.

Seperti yang saya sebutkan di atas, tiga komponen yang diperlukan untuk mencapai pelaksanaan dinamis adalah cara pemuatan, format modul, dan lingkungan pelaksanaan.

Komponen pertama, alat dimuat, akan ditangani oleh Java InputStream. Oleh kerana aliran input adalah asas dalam seni bina I / O Java, sistem ini dirancang untuk membaca dalam program dari InputStreamdan mengubahnya menjadi bentuk yang dapat dilaksanakan. Ini menunjukkan cara yang sangat fleksibel untuk memasukkan kod ke dalam sistem. Sudah tentu, protokol untuk data yang melalui aliran input akan menjadi kod sumber ASAS. Penting untuk diperhatikan bahawa sebarang bahasa boleh digunakan; jangan membuat kesilapan memikirkan teknik ini tidak dapat diterapkan pada aplikasi anda.

Setelah kod sumber program yang ditafsirkan dimasukkan ke dalam sistem, sistem mengubah kod sumber menjadi representasi dalaman. Saya memilih untuk menggunakan pokok parse sebagai format perwakilan dalaman untuk projek ini. Setelah pokok parse dibuat, ia dapat dimanipulasi atau dilaksanakan.

Komponen ketiga adalah persekitaran pelaksanaan. Seperti yang akan kita lihat, syarat untuk komponen ini agak mudah, tetapi pelaksanaannya mempunyai beberapa kelainan yang menarik.

Lawatan ASAS yang sangat pantas

Bagi anda yang mungkin belum pernah mendengar tentang ASAS, saya akan memberikan gambaran ringkas mengenai bahasa ini, supaya anda dapat memahami cabaran penghuraian dan pelaksanaan yang akan datang. Untuk maklumat lebih lanjut mengenai ASAS, saya sangat mengesyorkan sumber di hujung lajur ini.

BASIC adalah singkatan dari Beginners All-purpose Symbolic Instructional Code, dan dikembangkan di Dartmouth University untuk mengajar konsep pengiraan kepada pelajar sarjana. Sejak berkembang, BASIC telah berkembang menjadi pelbagai dialek. Dialek yang paling mudah digunakan sebagai bahasa kawalan untuk pengawal proses industri; dialek yang paling kompleks adalah bahasa berstruktur yang merangkumi beberapa aspek pengaturcaraan berorientasikan objek. Untuk projek saya, saya memilih dialek yang dikenali sebagai BASIC-80 yang popular di sistem operasi CP / M pada akhir tahun tujuh puluhan. Dialek ini hanya lebih kompleks daripada dialek yang paling mudah.

Sintaks penyataan

Semua baris penyataan adalah dalam bentuk

[: [: ...]]

where "Line" is a statement line number, "Keyword" is a BASIC statement keyword, and "Parameters" are a set of parameters associated with that keyword.

The line number has two purposes: It serves as a label for statements that control execution flow, such as a goto statement, and it serves as a sorting tag for statements inserted into the program. As a sorting tag, the line number facilitates a line editing environment in which editing and command processing are mixed in a single interactive session. By the way, this was required when all you had was a teletype. :-)

While not very elegant, line numbers do give the interpreter environment the ability to update the program one statement at a time. This ability stems from the fact that a statement is a single parsed entity and can be linked in a data structure with line numbers. Without line numbers, often it is necessary to re-parse the entire program when a line changes.

The keyword identifies the BASIC statement. In the example, our interpreter will support a slightly extended set of BASIC keywords, including goto, gosub, return, print, if, end, data, restore, read, on, rem, for, next, let, input, stop, dim, randomize, tron, and troff. Obviously, we won't go over all of these in this article, but there will be some documentation online in my next month's "Java In Depth" for you to explore.

Each keyword has a set of legal keyword parameters that can follow it. For example, the goto keyword must be followed by a line number, the if statement must be followed by a conditional expression as well as the keyword then -- and so on. The parameters are specific to each keyword. I'll cover a couple of these parameter lists in detail a bit later.

Expressions and operators

Often, a parameter specified in a statement is an expression. The version of BASIC I'm using here supports all of the standard mathematical operations, logical operations, exponentiation, and a simple function library. The most important component of the expression grammar is the ability to call functions. The expressions themselves are fairly standard and similar to the ones parsed by the example in my previous StreamTokenizer column.

Variables and data types

Part of the reason BASIC is such a simple language is because it has only two data types: numbers and strings. Some scripting languages, such as REXX and PERL, don't even make this distinction between data types until they are used. But with BASIC, a simple syntax is used to identify data types.

Variable names in this version of BASIC are strings of letters and numbers that always start with a letter. Variables are not case-sensitive. Thus A, B, FOO, and FOO2 are all valid variable names. Furthermore, in BASIC, the variable FOOBAR is equivalent to FooBar. To identify strings, a dollar sign ($) is appended to the variable name; thus, the variable FOO$ is a variable containing a string.

Finally, this version of the language supports arrays using the dim keyword and a variable syntax of the form NAME(index1, index2, ...) for up to four indices.

Program structure

Programs in BASIC start by default at the lowest numbered line and continue until there are either no more lines to process or the stop or end keywords are executed. A very simple BASIC program is shown below:

100 REM This is probably the canonical BASIC example 110 REM Program. Note that REM statements are ignored. 120 PRINT "This is a test program." 130 PRINT "Summing the values between 1 and 100" 140 LET total = 0 150 FOR I = 1 TO 100 160 LET total = total + i 170 NEXT I 180 PRINT "The total of all digits between 1 and 100 is " total 190 END 

The line numbers above indicate the lexical order of the statements. When they are run, lines 120 and 130 print messages to the output, line 140 initializes a variable, and the loop in lines 150 through 170 update the value of that variable. Finally, the results are printed out. As you can see, BASIC is a very simple programming language and therefore an ideal candidate for teaching computation concepts.

Organizing the approach

Typical of scripting languages, BASIC involves a program composed of many statements that run in a particular environment. The design challenge, then, is to construct the objects to implement such a system in a useful way.

When I looked at the problem, a straightforward data structure fairly leaped out at me. That structure is as follows:

The public interface to the scripting language shall consist of

  • A factory method that takes source code as input and returns an object representing the program.
  • An environment that provides the framework in which the program executes, including "I/O" devices for text input and text output.
  • A standard way of modifying that object, perhaps in the form of an interface, that allows the program and the environment to be combined to achieve useful results.

Internally, the structure of the interpreter was a bit more complicated. The question was how to go about factoring the two facets of the scripting language, parsing and execution? Three groups of classes resulted -- one for parsing, one for the structural framework of representing parsed and executable programs, and one that formed the base environment class for execution.

In the parsing group, the following objects are required:

  • Analisis leksikal untuk memproses kod sebagai teks
  • Menghuraikan ekspresi, untuk membina pokok penghuraian ungkapan
  • Menghuraikan pernyataan, untuk membina pokok penghuraian penyataan itu sendiri
  • Kelas ralat untuk melaporkan kesilapan dalam menghuraikan

Kumpulan kerangka terdiri daripada objek yang memegang pokok parse dan pemboleh ubahnya. Ini termasuk:

  • Objek penyataan dengan banyak subkelas khusus untuk mewakili pernyataan yang dihuraikan
  • Objek ekspresi untuk mewakili ungkapan untuk penilaian
  • Objek pemboleh ubah dengan banyak subkelas khusus untuk mewakili contoh data atom