06. PERANCANGAN SCANNER
1. Bahasa Sumber
Bahasa adalah kumpulan kalimat. Kalimat adalah rangkaian kata. Kata adalah unit terkecil dari komponen bahasa yang tidak bisa dipisahkan lagi.
Kalimat-kalimat : ‘Seekor kucing memakan seekor tikus.’ dan ‘Budi
menendang sebuah bola.’ adalah dua contoh kalimat lengkap Bahasa
Indonesia.
‘A cat eats a mouse’ dan ‘Budi kick a ball.’ adalah dua contoh kalimat lengkap Bahasa Inggeris.
‘if a2 < 9.0 then b2 := a2+a3;’ dan ‘for i := start to finish do A[i] :=
B[i]*sin(i*pi/16.0).’ adalah dua contoh kalimat lengkap dalam Bahasa
Pemrograman Pascal.
Dalam bahasa pemrograman, kalimat lebih dikenal sebagai ekspresi
sedangkan kata sebagai token.
Perancangan sebuah bahasa harus memperhatikan tiga aspek berikut :
1. spesifikasi leksikal, misalnya setiap kata harus tersusun atas huruf mati dan huruf hidup yang disusun bergantian, atau setiap token harus dimulai dengan huruf dan selanjutnya boleh diikuti oleh huruf atau angka,
2. spesifikasi sintaks, misalnya setiap kalimat mengikuti pola subyek-predikat-obyek atau ekspresi for_do mengikuti pola for-identifier
-:=-identifier-to-identifier-do-ekspresi.
3. aturan-aturan semantik, misalnya kata yang mendahului kata kerja haruslah kata benda yang menggambarkan sesuatu yang hidup dan berkaki, atau operasi perkalian hanya bisa dilakukan antara dua operan dengan tipe yang sama.
Dalam spesifikasi leksikal biasanya digunakan grammar regular (GR) dalam bentuk ekspresi regular (ER). Sebagai contoh pola token identifier
ditentukan oleh grammar regular berikut : I → aAbA...zAab...z,
A →aAbA...zA0A1A...9Aab...z01...9
yang ekuivalen dengan ekspresi regular berikut :
I = (ab...z)( ab...z01...9)* = huruf(hurufangka)*
Dalam spesifikasi sintaks biasanya digunakan context free grammar
(CFG). Sebagai contoh ekspresi if-then E adalah :
E → if L then,
L → IOA, I = huruf(hurufangka)*, O → <=><=>=,
A→01...9.
Algoritma Scanning
Scanner diimplementasikan dengan Automata Hingga Deterministik
(AHD).
Sebagai contoh,
scanner (yaitu AHD) untuk mengenali identifier adalah :
• Algoritma scanning adalah algoritma yang digunakan untuk menentukan token dalam analisis leksikal.
• Contoh kasus penyusunan algoritma scanning yang disusun dari sebuah diagram transisi sebuah automata hingga adalah sebagai berikut:
• Diagram transisi ini menggambarkan pola sebuah bahasa yang terdiri dari token <, <=, =, >=, >, ( ), (, ), +, -, *, /, :=, identifier, ; , keyword, konstanta, literal (yang diapit oleh apostrof).
• Baris komentar, yang dimulai dengan /* dan diakhiri dengan */, dan blank, diabaikan, dan diartikan semata-mata sebagai pemisah Token.
bahwa semua karakter input selain dari karakter tadi, akan mengikuti transisi.
• Busur berlabel "ERROR" menunjukkan bahwa suatu karakter adalah invalid (tidak berlaku), ditemukan dalam program sumber. Karakter ini bukan merupakan suatu Token yang manapun, kecuali jika ia terdapat di dalam komentar, atau dalam lateral.
• Stata 5, 6, dan 7 digunakan untuk mengenali Token <, <=, dan <>. Jika bukan karakter = atau > mengikuti karakter <, maka Akseptor akan berakhir pada Stata Akhir 5, ini menunjukkan bahwa Token < telah terkenali. Dalam hal ini Token <= atau <> akan dikenali juga. Token <= bila Akseptor berakhir di Stata 6, atau Token <> bila berakhir di Stata 7.
• Dengan cara yang sama, masing-masing Stata 2, 3, dan 4 digunakan untuk mengidentifikasi komentar dan Token /. Semua karakter sesudah tanda /* sampai dengan karakter yang diikuti oleh */ merupakan isi dari komentar. Komentar tidak akan dikirim sebagai Token. Dan selanjutnya kembali ke Stata Awal.
• Stata 19 dan 22 digunakan berturut-turut untuk mengenali identifier dan konstanta integer. Identifier dimulai dengan sebuah huruf dan diikuti sejumlah karakter alfanumerik. Konstanta merupakan sebarisan digit, yang dianggap satu Token sampai suatu karakter non numerik terbaca.
dari literal. Dua tanda apostrof yang berturutan dapat digunakan untuk menyatakan sebuah karakter dalam untai. Bila hal ini terjadi maka transisi dari Stata 21 ke Stata 20 terjadi.
Dari diagram transisi suatu “automata hingga” tersebut di atas yang menggambarkan suatu bahasa pemrograman tertentu, dapat disusun sebuah algoritma scanning sebagai berikut ;
(Prosedur disusun dari beberapa sub-prosedur atau Function misalnya; POS, GET_CHAR, KEYWORD, LOOKAHEAD, dll.
• POS digunakan untuk memberikan nomor "entry" atau nomor pada tabel simbol yang memberikan label pada setiap token.
• GET_CHAR(PROGRAM) digunakan untuk mendapatkan karakter input berikutnya dari program sumber.
• LOOKAHEAD adalah variabel logika yang menunjukkan apakah simbol lookahead dalam CHAR sudah digunakan sebelumnya. Harga "false" menyatakan bahwa simbol lookahead itu belum dipergunakan.
• CHAR menyatakan karakter yang pada saat itu tengah disidik(diperiksa) dalam untaian karakter sumber.
• INSERT(STRING, type) digunakan untuk memasukkan Token yang diberikan, STRING(jika perlu), dan tipenya (sebagai contoh: konstanta, literal, atau nama variabel).
• KEYWORD(STRING) digunakan untuk menentukan apakah argumennya adalah keyword, dan jika benar akan dikirimkan bilangan penyajian internal dari Token ini, dan jika bukan maka 0 (nol).
• Variabel local STRING mengandung Token aktual berisi nama variabel, literal, atau konstanta. Terakhir, variabel DIVISION, LEQ, NEQ, GEQ, GTN, EQ, LEFT, RIGHT, ADDITION, SUBTRACTION, MULTIPLICATION, ASSIGNMENT, SEMICOLON, LITERAL, IDENTIFIER dan CONSTANT, berisi bilangan penyajian internal dari Token /, <=, <>, <, >=, >, =, (, ), +, -, *, :=, ;, literal, identifier, dan konstanta.
Algoritmanya dapat dituliskan sbb. 1. (Inisialisasi)
POS
←
02. (Mendapatkan karakter pertama)
then CHAR
←
GET_CHAR (PROGRAM) LOOKAHEAD←
false3. (Ulangi sampai sebuah Token diketemukan)
Repeat langkah 4 while true
4. (Statemen CASE untuk menentukan Token berikut)
select by (CHAR)
case ' ' :
(scan dan abaikan blank) CHAR
←
GET_CHAR(PROGRAM)case '/' :
CHAR
←
GET_CHAR(PROGRAM) if CHAR = '*'(scan dan abaikan komentar) then Repeat while true
CHAR
←
GET_CHAR(PROGRAM)if CHAR = '*' (scan dan abaikan komentar) then Repeat while CHAR = '*'
CHAR
←
GET_CHAR(PROGRAM) if CHAR = '/'then CHAR
←
GET_CHAR(PROGRAM) Exit loopelse LOOKAHEAD
←
trueTOKEN
←
DIVISION ( / ) Returncase '<' :
CHAR
←
GET_CHAR(PROGRAM) if CHAR = '='then TOKEN
←
LEQ ( <= ) else if CHAR = '>'then TOKEN
←
NEQ ( <> ) else LOOKAHEAD←
trueTOKEN
←
LTN ( < ) Returncase '>' :
then TOKEN
←
GEQ ( >= ) else LOOKAHEAD←
trueTOKEN
←
GTN ( > ) Returncase '=' : ( = ) TOKEN
←
EQReturn
case '(' : ( ) ) TOKEN
←
LEFTReturn
case ')' : ( ) ) TOKEN
←
RIGHTReturn
case '+' : ( + ) TOKEN
←
ADDITIONReturn
case '-' : ( = ) TOKEN
←
SUBTRACTIONReturn
case ':' :
TOKEN
←
GET_CHAR(PROGRAM) if CHAR = '='then TOKEN
←
ASSIGNMENT ( := ) Returnelse
CHAR
←
GET_CHAR(PROGRAM)write ('UNKNOWN TOKEN'':''', CHAR,'IN SOURCE STRING')
case ';' : ( ; ) TOKEN
←
SEMICOLONReturn
Repeat while true
CHAR
←
GET_CHAR(PROGRAM) if CHAR = ''''then CHAR
←
GET_CHAR(PROGRAM) if CHAR ''''then LOOKAHEAD
←
truePOS
←
INSERT(STRING, LITERAL) TOKEN←
LITERALReturn
STRING
←
STRING.CHARDefault:
if CHAR >= 'A' dan CHAR <= 'Z' then STRING
←
CHAR (identifier)CHAR
←
GET_CHARRepeat while (CHAR >= 'A' AND CHAR <= 'Z') or (CHAR >= '0' AND <= '9')
STRING
←
STRING.CHAR CHAR←
GET_CHAR LOOKAHEAD←
trueif KEYWORD(STRING) > 0
then TOKEN
←
KEYWORD(STRING) ReturnPOS
←
INSERT(STRING,IDENTIFIER) TOKEN←
IDENTIFIERReturn
if CHAR >= '0' and CHAR <= '9' (konstanta) then STRING
←
CHARCHAR
←
GET_CHAR(PROGRAM)Repeat while CHAR >= '0' and CHAR <= '9' STRING
←
STRING.CHARCHAR
←
GET_CHAR(PROGRAM) LOOKAHEAD←
truePOS
←
INSERT(STRING,CONSTANT) TOKEN←
CONSTANTReturn
write ('ERROR-UNKNOWN CHARACTER', CHAR.IN SOURCE STRING')
Keterangan:
1. Langkah 1 menginisialisasi harga function POS dengan harga nol. Function POS disiapkan untuk menyimpan harga tabel dari suatu identifier, konstanta, ataupun literal dalam langkah 4.
2. Langkah 2 memperoleh simbol input saat itu, jika LOOKAHEAD tidak digunakan pada pemanggilan terdahulu dari scan.
3. Langkah 3 menjalankan langkah 4 berulang-ulang. Karena komentar dan blank diabaikan dalam proses scanning, langkah 4 pasti berulang dalam kasus seperti itu.
4. Langkah 4 menggunakan Statemen Case bercabang tergantung dari harga karakter saat itu tengah disidik. Ia secara khusus menjalankan potongan algoritma, yang menyebabkan transisi Stata hingga Akseptor.
3. Membaca Program Sumber
type Text_Pos = record {posisi penunjuk karakter}
Row_Numb : word; {baris ke-, bisa ribuan baris/program_sumber} Char_Numb : byte; {karakter ke-, maksimum 255 karakter/baris} end;
var Now_Pos : Text_Pos; {posisi sekarang}
Line : string; {baris yang sedang diproses}
End_of_line : byte; {posisi akhir baris yang sedang diproses}
procedure Next_Character(var Ft : text); {baca karakter berikutnya pada program_sumber}
begin
with Now_Pos do {coba tebak, apa itu perintah with ... do ?}
begin
if Char_Numb = End_of_line then begin
Next_Line(Ft); {membaca baris berikutnya} Row_Numb := Row_Numb + 1;
Char_Numb := 1 end
else
Char_Numb := Char_Numb + 1; character := Line[Char_Numb] end
end;
procedure List_Line; begin
write(Now_Pos.Row_Numb : 3, ‘ ‘); writeln(Line);
List_Error; {menampilkan kesalahan-kesalahan yang terjadi pada suatu baris}
End
procedure Next_Line(Ft : text); begin
readln(Ft, Line);
End_of_line := length(Line) + 1:
Line := Line + #32; {karakter spasi} end;
Simulasi DFA
Simulasi DFA dimaksudkan untuk mengenali token.
type Token_Kind = record tipe : byte; nilai : byte end;
var Token : array[0..Max_State] of Token_Kind;
Found_Token : Token_Kind; {token yang ditemukan}
Tok_Pos : Text_Pos; {posisi token dalam program sumber}
procedure Next_Token(var Ft : text); {digunakan untuk mengenali sebuah token}
begin
state1 := 0;
Tok_Pos := Now_Pos; repeat
state2 := Next_State(state1, character);
if state2 <> -1 then {-1 bersesuaian dengan x pada tabel transisi} begin
state1 := state2;
Next_Character(Ft); {baca karakter berikut pada program sumber, di antaranya
menghasilkan nilai baru untuk Now_Pos}
end;
until state2 = -1;
Act_for_Token(state1); end;
procedure Act_for_Token(state : shortint); var Tok_Length : byte;
Err : integer; begin
Current_Token(Token[state].tipe, Token[state].nilai);
Tok_Length := Now_Pos.Char_Numb - Tok_Pos.Char_Numb; case Token[state].tipe of
0 : Error(‘Token tidak dikenal!’, Tok_Pos);
27 : Id := copy(Line, Tok_Pos.Char_Num, Tok_Length);
28 : val(copy(Line, Tok_Pos.Char_Num, Tok_Length), IN, Err); 29 : val(copy(Line, Tok_Pos.Char_Num, Tok_Length), RN, Err); end
end;
catatan :
copy(string, start, length) mengembalikan substring val(string_value, number_variable, error_flag) :
jika string value = ‘137’ maka number variable = 137 dan error flag = 0 jika string value = ‘string’ maka number variable = 137 dan error flag ≠ 0 Token.tipe {1, 2, 3, ..., 26} dimisalkan bernilai pasti, sehingga tidak
perlu penanganan lebih lanjut
Found_Token.tipe := tipe; Found_Token.nilai := nilai; end;
LATIHAN SOAL
1. Misalkan a dan b adalah simbol input. Buatlah sebuah AHD (Automata Hingga Deterministik) M yang hanya menerima untaian karakter yang mengandung sejumlah genap a. (Contohnya untai aababab atau aa dapat diterima, sedangkan untai ababa atau abbb tidak diterima).
2. Buatlah sebuah Automata Hingga Deterministik dengan simbol input a,b, yang hanya dapat menerima untai karakter yang mengandung sejumlah b yang habis dibagi 3. (Petunjuk: dibutuhkan 3 Stata)
3. Buatlah sebuah Automata Hingga Deterministik M dengan simbol input a dan b, yang hanya dapat menerima untai karakter yang mengandung sejumlah genap a dan sejumlah b yang habis dibagi 3.
4. Misalkan M1(K1, VT, Z1, q0, f1) dan M2(K2, VT, Z2, s0, f2) adalah dua buah Automata Hingga Deterministik dengan himpunan simbol input VT yang sama. Misalkan L(M1) dan L(M2) berturut-turut adalah himpunan untai yang dapat diterima oleh M1 dan M2. Buatlah sebuah Automata Hingga Deterministik N dengan himpunan simbol input VT yang dapat menerima L(M1) IRISAN L(M2), yakni untai yang dapat diterima baik oleh M1 dan M2.