Total Tayangan Halaman

Senin, 26 Oktober 2009

Pembuatan Compiler dengan Metode Recursive Descent Parser

Pendahuluan
Manusia dapat melakukan interaksi secara efektif dengan menggunakan media bahasa. Bahasa memungkinkan penyampaian gagasan dan pemikiran. Bahasa pemrograman adalah bahasa yang dibuat agar manusia dapat melakukan komunikasi dengan mesin. Bahasa pemrograman menjembatani antara pemikiran manusia yang sering tidak terstruktur dengan mesin yang memerlukan kepastian untuk melakukan eksekusi. Bahasa pemrograman harus memiliki konstruksi yang dapat merefleksikan gagasan dan independen dari komputer yang dipergunakan. Bahasa pemrograman seperti ini sering disebut dengan bahasa tingkat tinggi. Bahasa tingkat tinggi lebih mudah untuk dipelajari karena bahasa yang digunakan lebih dekat dengan bahasa manusia dan tidak membutuhkan latar belakang mengenai perangkat keras.

Gagasan untuk perancangan bahasa pemrograman bisa berasal dari bahasa alami (natural language), matematika, dan bahasa pemrograman yang sudah ada. Konstruksi yang diturunkan dari bahasa alami berguna untuk kejelasan dan kemudahan pembacaan. Sebuah instruksi akan mengerjakan mirip dengan arti sesungguhnya dari instruksi tersebut dalam bahasa alami. Matematika telah banyak digunakan untuk aturan-aturan yang terdapat pada bahasa pemrograman, khususnya untuk ekspresi aritmatika dan notasi matematika. Notasi digunakan untuk mempersingkat kode program dan mempermudah pemrogram dalam pembuatan dan pembacaan ulang program. Bahasa pemrograman yang sudah ada dapat digunakan sebagai patokan dalam perancangan bahasa pemrograman. Namun, diperlukan ketelitian pada waktu menggunakannya karena bahasa yang sudah ada tersebut bisa saja memiliki suatu kesalahan yang cukup fatal. Bahasa yang digunakan sebagai referensi dapat juga lebih dari satu bahasa pemrograman karena setiap bahasa pemrograman memiliki kelebihan dan kekurangannya masing-masing.

Pengembangan sebuah kompilator merupakan pekerjaan yang tidak sederhana. Sebuah bahasa yang terlalu kompleks akan menyulitkan pembuatan kompilator untuk bahasa tersebut. Kebanyakan pemrogram menginginkan bahasa yang sederhana, tetapi kesederhanaan dapat pula berarti kekurangan di sisi lainnya. Kesederhanaan tidak dapat dicapai dengan keterbatasan struktur dan tidak dapat dicapai dengan generalitas yang tidak terbatas. Kesederhanaan dapat dicapai dengan melakukan pembatasan tujuan, perhatian pada keterbacaan, pendefinisian yang baik, dan konsep yang sederhana.

Sintaks adalah susunan kalimat dan grammar[3]. Sintaks dianalisis oleh suatu mesin yang disebut dengan parser. Parser bertugas menganalisis token yang dihasilkan pada proses scan sesuai dengan grammar. Recursive Descent Parser (RDP) adalah salah satu cara untuk mengaplikasikan bahasa bebas konteks untuk melakukan analisis sintaksis suatu source code. Ciri dari RDP yang menonjol adalah secara rekursif menurunkan semua variabel dari awal sampai bertemu terminal dan tidak pernah mengambil token secara mundur (no back track). Ciri lain dari RDP adalah sangat bergantung pada algoritma scan dalam mengambil token.

RDP juga dapat dikombinasikan dengan Predictive Parser. Predictive Parser (PP) adalah parser yang menggunakan prediksi untuk mengoptimalkan kerja dari parser. Parser model ini juga akan mengecilkan terjadinya rekursi kiri atau salah interpretasi. Prinsip dari predictive parser adalah mengelompokkan produksi sesuai dengan pola yang ada sehingga aturan produksi tertentu sudah diprediksikan penurunannya.

Tinjauan Pustaka
I Made Wiryana[4] menyebutkan bahwa teknik kompilasi telah lama diberikan di lingkungan pendidikan tinggi bidang komputer di Indonesia. Pembahasan dalam mata kuliah ini biasanya berkisar pada teori otomata, teori kompilasi, teori grammar. Praktek teknik kompilasi pun telah diberikan di lingkungan laboratorium, walau biasanya masih terbatas pada demonstrasi hal teori, ataupun sekedar pengenalan kompiler yang ada atau banyak digunakan. Beberapa universitas telah mulai memperkenalkan penggunaan perangkat pembangun kompiler.

Firrar Udirartatmo[3] mengemukakan bahwa dalam lingkungan pemrograman komputer, bahasa pemrograman bertindak sebagai sarana komunikasi antara manusia dan permasalahannya dengan komputer yang dipakai untuk membantu memperoleh penyelesaian. Suatu solusi untuk suatu masalah akan menjadi lebih mudah bila bahasa pemrograman yang digunakan lebih dekat dengan permasalahan tersebut.

Teori
Sebuah kompiler umumnya memiliki dua tugas pokok, yaitu fungsi analisis dan fungsi sintesis. Tugas dari fungsi analisis adalah melakukan dekomposisi program sumber menjadi bagian-bagian dasarnya, sedangkan fungsi sintesis memiliki tugas melakukan pembangkitan dan optimasi program objek. Model sebuah kompiler dapat dilihat pada gambar 1
Gambar 1. Model kompiler

Program sumber merupakan deretan simbol-simbol yang memuat konstruksi bahasa yang mendasar seperti nama variabel, label, konstanta, keyword, dan operator. Scanner membaca setiap karakter kode sumber dan memisahkan teks ke dalam token-token. Token digunakan oleh parser untuk menganalisis sintaks sesuai dengan grammar yang telah dirancang. Selanjutnya, semantic analyzer menentukan maksud dari program sumber dan menentukan aksi yang harus dilakukan. Analisis semantik bisa menghasilkan intermediate form dari program sumber yang berguna untuk memudahkan dalam pembuatan code generator dan code optimizer. Keluaran dari semantic analyzer diberikan ke code generator untuk ditranslasikan ke dalam bahasa assembly atau ke dalam bahasa mesin. Keluaran dari code generator diberikan ke code optimizer. Proses ini bertujuan untuk menghasilkan program objek yang lebih efisien. Di dalam perancangan sebuah parser dikenal dua buah notasi, yaitu BNF dan diagram sintaks. BNF adalah notasi yang dapat digunakan untuk memberikan definisi formal dari bahasa pemrograman. Diagram sintaks memberikan gambaran yang jelas mengenai BNF yang telah dirancang dalam bentuk grafis. Diagram sintaks menggunakan simbol pada gambar 2.
Gambar 2. Simbol yang digunakan dalam diagram sintaks

Di dalam proses kompilasi, analisis sintaksis menggunakan parser merupakan bagian front end dari model kompilasi. Karakteristik dari parser top-down hanya membutuhkan beberapa baris untuk mengakomodasi bahasa yang telah dirancang dan sangat cocok dengan BNF, simbol variabel direpresentasikan dengan sebuah prosedur, misalnya:
<exp> ::= <bool_term> { T_OR <bool_term> }
<bool_term> ::= <bool_not_fact> { T_AND <bool_not_fact> }
dapat diubah ke dalam algoritma:
procedure exp()
bool_term()
while CToken = T_OR do
scan()
bool_term()
endwhile
endprocedure


procedure bool_term()
bool_not_fact()
while CToken = T_AND do
scan()
bool_not_fact()
endwhile
endprocedure
Penurunan secara rekursif dapat terlihat dari kedua algoritma. Prosedur bool_term dipanggil oleh prosedur exp. Di dalam prosedur bool_term juga terjadi pemanggilan prosedur, yaitu prosedur bool_not_fact. Pemanggilan prosedur tersebut terjadi berulang-ulang (rekursif) dan terjadi penurunan (descent). Rekursi (penurunan) terjadi sampai menemui simbol terminal (token). Masalah utama dalam penggunaan rekursi adalah rekursi yang tidak berhenti. Oleh karena itu, diperlukan kehati-hatian dalam pemakaian rekursi. Predictive parser diperlukan untuk menekan atau menghilangkan kemungkinan terjadinya hal tersebut.
Pembahasan
Tahap-tahap penelitian yang dilakukan dalam penelitian ini adalah:

1. Pedefinisian kebutuhan
Pendefinisian kebutuhan meliputi penentuan perangkat lunak, penentuan perangkat keras yang sesuai dengan perangkat lunak dan aplikasi yang hendak dibuat serta data-data yang diperlukan untuk membuat aplikasi. Aplikasi ini hanya membutuhkan 3 elemen dari elemen-elemen kompiler yang ada, yaitu parser, scanner dan code generator. Hal ini bisa dilakukan karena bahasa yang dirancang tidak memerlukan deklarasi variabel (memerlukan tabel informasi) dan optimasi kode (memerlukan kode antara), sedangkan analisis semantik sudah ada secara implisit di dalam parser.

2. Perancangan
Perancangan merupakan tahap awal dari suatu aplikasi program untuk menghasilkan sistem yang baik. Perancangan dibuat sebagai landasan implementasi untuk mempermudah pembuatan aplikasi. Perancangan meliputi perancangan parser (BNF dan diagram sintaks), perancangan scanner (FA) dan desain aplikasi.

a. Perancangan Parser menggunakan BNF dan Diagram Sintaks
BNF dan Diagram Sintaks Kode sumber pada kompiler memerlukan BNF dan diagram sintaks agar pembuat program mudah dalam membuat program. Adapun beberapa rancangan BNF dari kompiler yang dibuat adalah sebagai berikut:
<simple_exp> ::= <term> { <add_operator> <term> }
<add_operator> ::= T_ADD | T_SUB
<term> ::= <signed_fact> { <mul_operator> <signed_fact> }
<signed fact> ::= <add_operator> <fact> | <fact>
<mul_operator> ::= T_MUL | T_DIV
<fact> ::= T_LPARENT <exp> T_RPARENT | T_NUMERIC
dimana:
T_ADD = ’+’
T_SUB = ’-‘
T_MUL = ’*’
T_DIV = ’/‘
T_LPARENT = ’(‘
T_RPARENT = ’)’
T_ANGKA = ’0’..’9’
sedangkan beberapa rancangan berupa diagram sintaks dari kompiler yang dibuat dapat dilihat pada gambar 3, 4 dan 5.
Gambar 3. Diagram sintaks dari <simple_exp>
Gambar 4. Diagram sintaks dari <add_operator>
Gambar 5. Diagram sintaks dari <term>

b. Perancangan Scanner menggunakan FA
Dalam memeriksa sintaks dari kode sumber, parser membutuhkan bantuan scanner untuk memberikan token dari sintaks. Berdasarkan BNF dan diagram sintaks yang telah dibuat, maka dapat dirancang sebuah scanner dengan rancangan seperti pada gambar 6.
Gambar 6. Rancangan scanner berupa Finite Automata

3. Implementasi
a. Scanner
Berikut ini adalah implementasi dari FA Scanner:
Option Explicit
'inisialisasi token
Enum Token
T_NUMERIC = 0
T_ADD
T_SUB
T_MUL
T_DIV
T_RPARENT
T_LPARENT
T_EOF
T_UNKNOWN
End Enum
'inisialisasi
Const TAB_ = 9
Const LINEFEED_ = 10
Const CARRIAGE_RETURN_ = 13
Const SPACE_ = 32
Const EOF_SIGN = "#"
Private keySymbol() As Variant
Dim lst As ListItem
Dim CTokenType As String


Sub Reset()
'reset posisi pita ke posisi awal
Index = 0
LineNumber = 1
CC = ""
BOF = True
EOF = True
ReDim errorTrap(0)
End Sub


Sub getCh()
'pita maju satu karakter
If CC <> EOF_SIGN Then
Index = Index + 1
CC = Mid(theRtf.Text, Index, 1)
If CC <> EOF_SIGN Then EOF = False Else EOF = True
Else
EOF = True
End If
End Sub


Sub Initialize(ByRef Rtb As Object)
‘inisialisasi awal
Set theRtf = Rtb
isAnyError = False
Reset
If Len(Rtb) <> 0 Then EOF = False
End Sub


Sub skipComment()
‘mengabaikan komentar
If CC = "{" Then
Do
getCh
Loop Until CC = "}" Or EOF
If Not EOF Then
getCh
Scan
End If
End If
End Sub


Sub Scan()
'kode scanner
Dim indexKey As Integer
Dim isAllow As Boolean
Identifier = ""
If BOF Then
getCh
BOF = False
End If
While Not EOF
Select Case CC
Case Chr(TAB_), Chr(LINEFEED_), Chr(SPACE_): getCh
Case Chr(CARRIAGE_RETURN_)
getCh
LineNumber = LineNumber + 1
Case "#"
CToken = T_EOF
Case "0" To "9"
Do
Select Case CC
Case "0" To "9"
isAllow = True
Identifier = Identifier & CC
getCh
Case Else: isAllow = False
End Select
Loop Until Not isAllow Or EOF
CToken = T_NUMERIC
CTokenType = "numerik"
GoTo addToken
Case "+"
applyToken T_ADD, "operator penjumlahan"
getCh
GoTo addToken
Case "-"
applyToken T_SUB, "operator penjumlahan"
getCh
GoTo addToken
Case "*"
applyToken T_MUL, "operator perkalian"
getCh
GoTo addToken
Case "/"
applyToken T_DIV, "operator perkalian"
getCh
GoTo addToken
Case "("
applyToken T_LPARENT, "delimiter"
getCh
GoTo addToken
Case ")"
applyToken T_RPARENT, "delimiter"
getCh
GoTo addToken
Case "{"
skipComment
Exit Sub
Case "}"
setError 2
getCh
Scan
Exit Sub
Case Else
setError 1
applyToken T_UNKNOWN, "tidak diketahui"
getCh
GoTo addToken
End Select
Wend
CToken = T_EOF
Exit Sub
addToken:
With frmUtama.ListView3
No = No + 1
Set lst = .ListItems.Add(, , No)
lst.SubItems(1) = CTokenType
lst.SubItems(2) = Identifier
End With
End Sub


Sub applyToken(ByVal theToken As Token, kindOfToken As String)
‘memasukkan informasi tentang token ke dalam tabel simbol
CToken = theToken
CTokenType = kindOfToken
Identifier = CC
End Sub
b. Parser dan object code
Pembuatan object code dalam bentuk bahasa assembly disisipkan ke dalam implementasi notasi BNF untuk mempermudah pembuatan program. Object code yang disisipkan di tandai dengan pemanggilan sub “writeAssemblyCode”. Berikut ini adalah implementasi dari BNF yang telah dibuat dan pembuatan object code:
Option Explicit


Sub simple_exp()
'<simple_exp> ::= <term> { <add_operator> <term> }
term
While add_operator
writeAssemblyCode "PUSH AX"
Select Case CToken
Case T_ADD
Scan
term
writeAssemblyCode "POP tempAX" & gantiBaris & "ADD AX, tempAX"
Case T_SUB
Scan
term
writeAssemblyCode "POP tempAX" & gantiBaris & "balikAX2tempAX" & gantiBaris & "SUB AX, tempAX"
End Select
Wend
End Sub


Function add_operator() As Boolean
'<add_operator> ::= T_ADD | T_SUB
If CToken = T_ADD Or CToken = T_SUB Then add_operator = True Else add_operator = False
End Function


Sub term()
'<term> ::= <signed_fact> { <mul_operator> <signed_fact> }
signed_fact
While mul_operator
writeAssemblyCode "PUSH AX"
Select Case CToken
Case T_MUL
Scan
signed_fact
writeAssemblyCode "POP tempAX" & gantiBaris & "MUL tempAX"
Case T_DIV
Scan
signed_fact
writeAssemblyCode "POP tempAX" & gantiBaris & "balikAX2tempAX" & gantiBaris & "DIV tempAX"
End Select
Wend
End Sub


Sub signed_fact()
'<signed fact> ::= <add_operator> <fact> | <fact>
If add_operator Then
If CToken = T_SUB Then Scan
fact
writeAssemblyCode "negatifAX"
Else
fact
End If
End Sub


Function mul_operator() As Boolean
'<mul_operator> ::= T_MUL | T_DIV
If CToken = T_MUL Or CToken = T_DIV Then mul_operator = True Else mul_operator = False
End Function


Sub fact()
'<fact> ::= T_LPARENT <exp> T_RPARENT | T_NUMERIC
If CToken = T_LPARENT Then
Scan
simple_exp
Match T_RPARENT, 101
Else
If Not (CToken = T_NUMERIC) Then
setError 102
Else
writeAssemblyCode "MOV AX, " & Identifier
Scan
End If
End If
End Sub


Sub startParsing(ByRef Rtb As Object)
'prosedur awal untuk memulai proses parsing
Initialize Rtb
writeAssemblyCode "negatifAX MACRO ;mengalikan AX dengan -1" & gantiBaris(True) & "MOV BX, -1" & gantiBaris & "MUL BX" & gantiBaris & "ENDM" & gantiBaris(False)
writeAssemblyCode " ;start definisi program exe" & gantiBaris & ".MODEL SMALL" & gantiBaris & ".STACK 200h" & gantiBaris & ".DATA" & gantiBaris & ".CODE" & gantiBaris & "program:" & gantiBaris & "MOV AX, CS ;segmen data" & gantiBaris & "MOV DS, AX ;pada exe DS tak otomatis terinisialisasi" & gantiBaris & " ;end definisi program exe" & gantiBaris & "JMP programUtama" & gantiBaris & " ;start deklarasi variabel serbaguna" & gantiBaris & "tempAX DW ?" & gantiBaris & "temp DW ?" & gantiBaris & "temporary DW ?" & gantiBaris & "lastFor DW ?" & gantiBaris & "T_ENTER EQU 0Dh ; konstanta karakter <Enter>" & gantiBaris & "Buffer DB 30,?,30 DUP(?)" & gantiBaris & "true_str DB 'true'" & gantiBaris & "false_str DB 'false'" & gantiBaris & " ;end deklarasi variabel serbaguna" & gantiBaris
writeAssemblyCode "programUtama:"
Scan
simple_exp
writeAssemblyCode "end_program:" & gantiBaris & "MOV AX, 4C00h ;Servis kembali ke OS" & gantiBaris & "INT 21h" & gantiBaris & "END program"
End Sub


Sub writeAssemblyCode(theCode As String)
'menulis string ke dalam richTextBox sehingga kode assembly langsung dapat dilihat
frmUtama.RichTextBox2.Text = frmUtama.RichTextBox2.Text & theCode & gantiBaris
End Sub


Sub Match(theToken As Token, errNumIfNotMatch As Byte)
'memeriksa token saat ini
If CToken = theToken Then Scan Else setError errNumIfNotMatch
End Sub


Function gantiBaris(Optional isPL As Variant) As String
'memberi nomor baris pada kode assembly sekaligus ganti baris
barisAssembly = barisAssembly + 1
If Not IsMissing(isPL) Then
If isPL Then
isPrcLn = True
procedureLine = 0
Else
isPrcLn = False
End If
End If
gantiBaris = vbTab & " ;; " & barisAssembly
If isPrcLn Then
procedureLine = procedureLine + 1
gantiBaris = gantiBaris & " : " & procedureLine
End If
gantiBaris = gantiBaris & vbCrLf
End Function
c. Tampilan Program
Pada program ini terdapat 3 bagian yang berisi teks, yaitu source code (tempat untuk mengetikkan baris program), tabel informasi dan assembly code. Program ini akan menghasilkan sebuah file aplikasi (*.exe) jika baris program sukses dikompilasi seperti pada gambar 7 dan program ini akan menghasilkan pesan kesalahan jika baris program tidak sukses dikompilasi seperti pada gambar 8.
Gambar 7. Tampilan program sewaktu kode sumber sukses dikompilasi

Untuk menghasilkan sebuah file aplikasi diperlukan TASM (Turbo Assembly). Adapun kode untuk memanggil TASM adalah sebagai berikut:
Private Sub Command2_Click()
Dim dTaskID As Double, path As String, file As String
RichTextBox2.SaveFile App.path & "\tempCode.asm", rtfText
path = Chr(34) & App.path & "\runtasm.bat" & Chr(34)
dTaskID = Shell(path, vbNormalFocus)
enableExecute False
End Sub
dimana file “runtasm.bat” dibuat melalui sub “buatFile” berikut ini:
Sub buatFile()
Dim nofile As Integer
nofile = FreeFile()
If Exists(App.path & "\runtasm.bat") Then Kill App.path & "\runtasm.bat"
Open App.path & "\runtasm.bat" For Output As #nofile
Print #nofile, Chr(34) & App.path & "\tasm" & Chr(34) & " tempCode.asm"
Print #nofile, "tlink /t tempCode"
Print #nofile, "tlink tempCode"
Print #nofile, "del *.map"
Print #nofile, "del *.obj"
Close #nofile
nofile = FreeFile()
If Exists(App.path & "\show.bat") Then Kill App.path & "\show.bat"
Open App.path & "\show.bat" For Output As #nofile
Print #nofile, Chr(34) & "C:\WINDOWS\TCG_HELP\%1" & Chr(34)
Close #nofile
End Sub
Gambar 8. Tampilan program sewaktu kode sumber tidak sukses dikompilasi

Kesimpulan
1. Pembuatan kompiler merupakan masalah yang kompleks karena di dalam kompiler sendiri terdapat beberapa proses yang memiliki karakteristik dan masalah masing-masing.
2. Recursive descent parser mudah untuk diimplementasikan tetapi memerlukan kehati-hatian dalam perancangannya

Saran
1. Grammar dari bahasa pemrograman dapat diperluas dan ditambah dengan struktur yang lebih kompleks, seperti: penambahan prosedur dan fungsi dengan parameter, floating point, string, array, I/O file, kontrol pemilihan case, tipe data user defined, struct, class dan sebagainya
2. Menambahkan error recovery dan error repair.
3. Menambahkan optimasi sebelum diubah ke dalam object code atau optimasi object code yang dihasilkan.

Daftar Pustaka
[1] Crenshaw, Jack W., Ph.D., 1988, LET'S BUILD A COMPILER!, http://www.etekchalmers.se/~e8johan/compiler/
[2] Prabowo, Basit Adhi, 2006, Pembuatan Kompiler dengan Metode Recursive Descent Parser, Skripsi S1, Universitas Ahmad Dahlan Yogyakarta
[3] Utdirartatmo, Firrar, 2005, Teknik Kompilasi, Graha Ilmu, Yogyakarta
[4] Wiryana, I Made, Compiler Construction, http://www.wiryana.pandu.org/

Tidak ada komentar: