Nota Lengkap Compiler
Nota Lengkap Compiler
PENGENALAN
Bahasa
Bahasa Pengaturcaraan
Bahasa paras rendah atau bahasa mesin merupakan bahasa pengaturcaraan yang mula
digunakan oleh manusia untuk menyelesaikan masalah khususnya masalah dalam
bidang matematik. Ia adalah satu bahasa yang sukar difahami oleh manusia kerana
arahan dan pernyataan bahasa menggunakan sistem pernomboran binari iaitu
gabungan nombor 0 dan 1. Pada masa itu, bahasa ini sentiasa diubah suai supaya
manusia mudah menggunakannya. Maka, dalam perkembangannya, wujudlah bahasa
perhimpunan yang menggunakan istilah bahasa Inggeris yang mudah untuk arahan-
arahan seperti add, mul dan div.
Namun penggunaan arahan yang mudah masih lagi tidak memenuhi kemampuan
manusia untuk menguasai bahasa pengaturcaraan dengan mudah. Oleh kerana itu,
terciptalah bahasa pengaturcaraan yang menggunakan arahan bahasa tabii iaitu
bahasa Inggeris. Bahasa ini dinamakan sebagai bahasa paras tinggi kerana ia berada
di atas iaitu pada pandangan pengguna. Penggunaan bahasa ini mudah difahami oleh
manusia tetapi bahasa ini memerlukan mekanisma untuk menukarkan arahannya
kepada bahasa mesin semula kerana komputer hanya memahami bahasa mesin untuk
melaksanakan arahan tersebut. Mekanisma yang digunakan sebagai orang tengah
diantara bahasa mesin dengan bahasa pengaturcaraan paras tinggi dinamakan sebagai
pengkompil.
1. Bahasa Imperatif seperti Algol 60, Algol 68, Fotran, COBOL, Pascal dan C.
Bahasa ini mempunyai perlaksanaan mengikut struktur komputer von Neumann
yang mempunyai CPU, ingatan dan bas.
2. Bahasa fungsian seperti LISP dan HOPE. Bahasa ini mempunyai kategori seperti
berikut :
Tiada perbezaan antara statements dan operasis.
Nama digunakan sebagai hanya untuk mengenalpasti operasi dan
functions; tidak digunakan untuk mengenalpasti lokasi ingatan.
3. Bahasa Logik seperti Prolog. Bahasa ini adalah berasaskan kepada pandangan
operasi dan jangkaan kalkulus. Mekanisma resolution digunakan iaitu satu
tatacara untuk membaiki implikasi dalam first-order jangkaan kalkulus.
Penterjemah
Satu bahasa, L.
Input kepada penterjemah, IL adalah aturcara L iaitu PL dan input kepada PL pula
adalah e. Dalam masa yang sama IL perlu menghasilkan output, katakan e.
Pengkompil
Dua kaedah yang diguna-pakai sejak dahulu adalah penterjemah (interpreter) dan
pengkompil (compiler). Kedua-dua kaedah ini merupakan perisian yang menerima
Perterjemah terima input, proses dan laksankan input tersebut pada masa yang sama.
Pengkompil pula mempunyai beberapa peringkat pemprosesan sebelum melaksana
input tersebut. Input dianalisa tanpa melibatkan input data kepada aturcara pengguna
dan kemudian ditukarkan kepada bentuk objek untuk menjadikan perlaksanaan
aturcara yang lebih berkesan. Objek ini akan digunakan untuk perlaksanaan aturcara
sebenar yang akan melibatkan data input kepada aturcara tersebut.
Proses pengkompilasi melibatkan aktiviti :
Penganalisa Leksikal
Penganlisa Sintaksis
Penganalisa Semantik
Pengendali
Pengurus Ralat
Jadual Penjana Kod Pertengahan
Simbol
Pengoptimum
Penjana Kod
Analisis Leksikal
Dalam pengkompil, analisis linear ini dipanggil sebagai analisis leksikal atau
scanning (pengimbas). Sebagai contoh, dalam analisis leksikal karakter dalam
pernyataan umpukkan :
A = B + Kadar * 50;
1. Pencam A
2. Simbol umpukkan =
3. Pencam B
4. Tanda tambah
5. Pencam Kadar
6. Tanda darab
7. Nombor 50
Ruang kosong antara karakter biasanya diabaikan pada masa analisis ini.
Dalam operasi B + Kadar * 50, frasa Kadar * 50 adalah unit logikal kerana dalam
peraturan arithmetik, operasi pendaraban dilakukan dahulu sebelum penambahan.
Oleh kerana operasi B + Kadar diikuti dengan tanda *, maka oleh kerana itu, ia tidak
dikumpulkan dalam satu frasa.
Dalam struktur hirarki suatu aturcara, aturcara selalunya dinyatakan oleh peraturan
rekursif. Sebagai contoh,
Peraturan (1) dan (2) adalah peraturan nonrekursif. Manakala (3) mendefinisikan
operasi dalam keadaan tanda operator digunakan pada operasi lain. Dengan peraturan
(2), 50 adalah operasi dan peraturan (3), Kadar * 50 adalah operasi maka akhirnya B
+ Kadar * 50 adalah satu operasi.
Analisis Semantik
Analisis ini menyemak aturcara sumber untuk ralat semantik (logik). Ia akan
mengumpul jenis maklumat dalam aturcara untuk analisis yang seterusnya iaitu
penjana kod pertengahan. Ia mempunyai 2 rutin iaitu :
1. Menyemak semantik statik bagi satu binaan (struktur daripada analisis semantik)
iaitu ia menguji sama ada sesuatu binaan itu sah dan bererti.
2. Melakukan terjemahan sebenar bagi binaan tersebut.
Contoh,
Ungkapan X = Y + Z
Ia adalah rutin untuk mencipta aliran kod pertengahan iaitu kod yang menghampiri
bahasa mesin. Biasanya ia menggunakan arahan dengan satu operator dan beberapa
operan.
Contoh :
Setara dengan { X X + Y }
MUAT X {AKU X }
TAMBAH Y {AKU AKU + Y }
STOR X {X AKU}
Perbezaan utama antara kod pertengahan dengan kod perhimpunan adalah kod
pertengahan tidak perlu menyebut daftar-daftar yang digunakan untuk setiap operasi.
Daftar adalah lokasi ingatan dalam CPU. Daftar akumulator digunakan untuk operasi
arithmetik dalam CPU.
PENGOPTIMUMAN KOD
Ia adalah fasa untuk membaiki kod pertengahan supaya untuk mendapatkan kod yang
lebih berkesan supaya masa larian yang lebih cepat.
PENJANA KOD
Ia memberikan lokasi bagi data, memilih kod untuk mencapai setiap data dari lokasi
dan memilih daftar tertentu untuk setiap operasi yang dilaksanakan.
Analisis leksikal
Penganalisis sintaks
=
+
id1 id2 *
id3 60
Analisis semantik
=
+
id1 id2 *
inttofloat
id3 60
Penjana kod
MULF id3, R2
MULF #60.0, R2
MOVF id2, R1
ADDF R2, R1
MOVF R1, id1
Penggunaan operan dua kod ini iaitu kod yang pertama dan yang kedua, setiap arahan
bermaksud sumber dan destinasi.
1. Pra-pemproses
2. Penghimpun
3. Penghimpun dua pas
4. Pemuat dan pemaut
Pengenalan
Dalam bahagian ini kita melihat bagaimana satu pengkompil melaksanakan tugasnya
memproses bahasa pengaturcaraan. Kita juga menganalisis secara ringkas bagaimana
sintaks bahasa pengaturcaraan dibangunkan mengikut Rumus Nahu Bebas-Konteks
(Context Free Grammar (CFG)) atau BNF (Backus-Naur Form).
Oleh kerana tugas utama dalam memproses bahasa adalah untuk menterjemah bahasa,
maka dalam bahagian ini kita akan menggunakan kaedah menterjemah bahasa
berpandukan kepada sintaks iaitu, CFG.
Definisi Sintaks
Penbolehubah seperti ungkp dan pern mewakili token-token yang dipanggil non-
terminal.
Contoh :
(nahu 1)
+-0123456789
Pengeluaran untuk nonterminal wujud jika nonterminal muncul pada sebelah kiri
pengeluaran. Satu string mengandungi sifar token, yang ditulis sebagai , dipanggil
sebagai string nol.
Satu nahu mengeluarkan string dengan dimulai oleh simbol mula dan berterusan
dengan menggantikan nonterminal pada sebelah kanan pengeluaran untuk
nonterminal tersebut. Token string yang boleh dikeluarkan daripada simbol mula
membentuk bahasa yang didefinisikan oleh nahu.
Contoh:
Bahasa yang dinyatakan dalam rumus nahu 1 adalah bahasa yang mengandungi
senarai digit yang diasingkan dengan tanda tambah dan tolak. Kita uji 9-5+2 dengan
nahu tersebut untuk menentukan ia adalah bahasa yang sah atau tidak mengikut
rumus nahu ini.
Huraian ini boleh digambarkan seperti dalam gambarajah pokok dalam rajah 2.0 di
bawah. Setiap nod pada pokok dilabelkan oleh simbol nahu.
senarai
senarai digit
senarai digit
digit
9 - 5 + 2
Rajah 2.0
Pokok di atas merupakan sejenis pokok penghurai. Daripada rajah didapati bahawa
senarai sebagai akar mempunyai anak kiri iaitu senarai dan anak kanan digit. Anak
kiri pula mempunyai anak kiri dan anak kanan iaitu senarai dan digit. Pokok ini
dibincangkan dengan lebih lanjut dengan contoh berikut :
blok_atur { pern_pilihan }
pern_pilih senarai_pern |
senarai_pern senarai_pern ; pern | pern
(nahu 2)
POKOK SINTAKS
X Y X
Secara formal, nahu bebas konteks mempunyai pokok sintaks dengan ciri-ciri yang
berikut :
Dalam rajah pokok di atas, akar pokok dilabelkan sebagai senarai, iaitu simbol mula
nahu. Anak dilabel daripada kiri ke kanan, senarai, + dan digit. Pokok sintaks akan
membantu pengkompil mengenal pasti SEMANTIK bahasa pengaturcaraan.
Ambiguiti
Satu nahu boleh mempunyai lebih daripada satu pokok huraian. Nahu ini dikatakan
sebagai nahu berambiguiti. Untuk menunjukkan sesuatu nahu itu berambiguiti, kita
perlu uji nahu tersebut dengan pokok huraian.
Contoh :
9 5 5 2
Contoh Pengkompil
Diberi pengeluaran :
Jika input pengkompil adalah pernyataan : b*b – 4*a*c, maka hasil daripada operasi
leksikal adalah :
[pencam,b] * [pencam,b]-[pencam,4]*[pencam,a]*[pencam,a]
unkp
unkp - term
pencam b pencam a
b 4
Oleh kerana dengan lebih daripada satu pokok huraian akan menghasilkan lebih
daripada satu maksud, maka, pengkompil perlu merekabentuk satu nahu yang tidak
berambiguiti.
Ini dilakukan dengan peraturan seperti kesatuan operator dan keutamaan operator.
Kesatuan operator bermaksud perlaksanaan adalah mengikut daripada kiri ke kanan.
Perbezaan antara operator kesatuan kanan dengan operator kesatuan kiri ditunjukkan
dalam rajah pokok penghurai berikut :
sen kanan
digit 5 b c
Keutamaan Operator
Dengan ini, kita perlu tahu keutamaan operator. Operator ‘*’ dikatakan mempunyai
keutamaan yang lebih tinggi daripada operator ‘+’.
Nahu untuk satu ungkapan arithmetik boleh dibina daripada jadual keutamaan dan
kesatuan operator-operator. Berikut adalah adalah susunan operator yang mempunyai
sama keutamaan dan kesatuan :
Kesatuan kiri : + -
Kesatuan kiri : * /
Kita bina non-terminal unkp dan term untuk dua paras keutamaan dan tambahan non-
terminal faktor untuk menjana unit asas dalam ungkapan.
Kita anggap operator ‘*’ dan ‘/’ mempunyai keutamaan paling tinggi,
Dan,
ANALISIS SEMANTIK
Pokok Sintaks
Pokok sintaks merupakan satu struktur data yang menunjukkan dengan jelas
bagaimana satu segmen aturcara boleh dilihat dari sudut NAHU. Menggunakan
pengeluaran bahasa di atas, dengan input pengkompil : b*b – 4*a*c, maka hasil
daripada operasi semantik kita perolehi satu pokok sintaks seperti berikut :
* *
b b * b
4 a
Jenis : float
Lok : daftar 1
-
b b c jenis: float
Jenis : float Jenis : float * lok : sp + 24
Lok : sp + 16 Lok : sp + 16 jenis : float
lok : daftar 2
4 a
jenis : float jenis : float
lok : pemalar lok : pemalar
Dalam bahagian ini, kita akan cuba untuk menterjemah bahasa mengikut
petunjuk/petua Nahu Bebas Sintaks.
Untuk menterjemah satu binaan bahasa pengaturcaraan, satu pengkompil perlu untuk
menjejaki beberapa perkara selain daripada penjanaan kod. Sebagai contoh,
pengkompil perlu tahu 3 elemen iaitu :
Notasi Postfix
1. Jika E adalah satu pembolehubah atau pemalar, maka notasi postfix baginya
adalah E sendiri.
2. Jika E adalah ungkapan dalam bentuk E1 op E2, dimana op adalah operator
binari, maka notasi postfix untuk E adalah E1’E2’op, dimana E1 dan E2 adalah
notasi postfix untuk E.
3. Jika E adalah ungkapan dalam bentuk (E1), maka notasi postfix untuk E1 adalah
juga postfix untuk E.
Definisi
Satu penterjemahan adalah suatau pemetaan input-output. Output untuk setiap input x
dihasilkan dengan beberapa langkah berikut :
Satu atribut dikatakan akan disynthesized jika nilainya pada nod pokok huraian
dikenalpasti daripada nilai atribut pada anak nod tersebut. Proses ini mempunyai
keupayaan untuk dinilai pada masa penjelajahan bawah-atas pokok huraian. Sebagai
contoh :
Satu definisi sintaks untuk menterjemah ungkapan yang mengandungi digit yang
dipisahkan oleh tanda tambah(campur) atau tanda kurang(tolak) kepada notasi
postfix.
Ini ditunjukkan dalam rajah di bawah :
Daripada rajah didapati bahawa atribut t digunakan untuk mewakilkan notasi postfix
untuk ungkapan yang dijana oleh nonterminal dalam pokok huraian.
Postfix yang terbentuk daripada digit adalah digit itu sendiri. Sebagai contoh,
peraturan semantik yang bersekutu dengan pengeluaran term 9 mendefinisikan
term.t menjadi 9 dimana sahaja pengeluaran tersebut berada dalam pokok.
Bila pengeluaran unkp term digunakan, nilai untuk term.t menjadi nilai untuk
unkp.t.
Peraturan ini berkaitan dengan pengeluaran yang menyatakan nilai atribut unkp.t
yang bercantum dengan bentuk postfix unkp1.t dan term.t pada sebelah kiri dan
kanan operan-operan. Operator || dalam peraturan semantik mewakili string
concatenation (cantum).
unkp.t = 95-2+
unkp.t = 9 term.t = 5
term.t = 9
9 - 5 + 2
Satu definisi sintaks-berpandu tidak semestinya mempunyai satu cara yang tetap
untuk menilai atribut pada satu pokok huraian.
Secara am, kita perlu menilai atribut pada masa nod pertama ditemui atau setelah
semua anak dijelajah atau pada satu titik antara anak dan nod, iaitu pada masa
penjelajahan pokok.
Perterjemahan dalam kursus ini boleh dilakukan dengan menilai peraturan semantik
untuk semua atribut dalam pokok huraian menggunakan langkah pra-jangkaan.
Satu jelajah pokok bermula di akar dan ia akan melawat semua nod dalam pokok
dalam langkah yang tertentu.
Skema Penterjemahan
Skema ini adalah satu spesifikasi untuk mendefinisikan penterjemahan. Ia adalah satu
nahu bebas konteks dimana satu fragment aturcara yang dipanggil sebagai tindakan
semantik berada di sebelah kanan pengeluaran.
Skema penterjemahan menjana output untuk setiap ayat x yang dijana oleh nahu yang
dinyatakan dengan melaksanakan tindakan mengikut jujukan yang diperolehi pada
masa penjelajahan kedalaman dahulu untuk satu pokok huraian x.
Sebagai contoh, andaikan satu pokok huraian yang dilabel rest mewakili satu
pengeluaran. Tindakan {print(‘+’)} akan dilaksanakan setelah anak pokok term
dijelajah tetapi sebelum anak untuk rest1 dilawati.
Hasil ini adalah untuk menulis ouput penterjemahan ke dalam satu fail, iaitu string
atau karakter. Sebagai contoh, untuk menterjemah 9-5+2 kepada 95-2+ dengan
mencetak setiap karakter dalam 9-5+2 tanpa menggunakan ruang ingatan untuk
melakukannya. Apabila bilangan output yang dicetak bertambah, kedudukan atau
susunan karakter yang dicetak amat penting untuk memastikan maknanya.
Seperti mana yang telah dinyatakan dalam definsi sintaks-berpandu, satu string
mewakili penterjemahan satu nonterminal pada sebelah kiri setiap pengeluaran
dicantum oleh penterjemahan nonterminal pada sebelah kanan.
Lihat contoh pengeluaran dan peraturan semantik yang telah dinyatakan sebelum ini :
Daripada contoh di atas, dapat dilihat bahawa penterjemahan unkp.t dicantum dengan
penterjemahan unkp1 dan term, diikuti dengan simbol +. Didapati bahawa unkp1
muncul sebelum term pada sebelah kanan pengeluaran.
Namun, nonterminal term tetap muncul sebelum rest1 pada sebelah kanan.
Berikut adalah skema penterjemahan yang dihasilkan daripada definisi yang telah
dinyatakan dalam rajah di atas. Ia adalah berasaskan kepada pengeluaran :
unkp {print(‘+’)}
unkp term
term
#include “parser.h”
#include “backend.h”
#include “error.h”
int main(void) {
AST_node *icode;
#include “lex.h”
Token_type Token;
void get_next_token(void) {
int ch;
do {
ch = getchar();
if (ch < 0)
Token.class = EOF;
Token.repr = ‘#’;
Return;
}
} while (Layout_char(ch));
if (Token.class = = ‘(‘ ) {
expr type = ‘P’;
get_next_token();
if (!Parse_expression(&exprleft)) {
Error(“Missing Expression”);
}
get_next_token()
return 1;
}
//percubaan gagal
free_expression (expr);
return 0;
}
#include “lex.h”
#include “error.h”
#include “parser.h”
get_next_token();
if (Parse_expression(&expr))
{
if (Token.class != EOF ) {
Error(“Garbage after end of program”);
}
*icode_p = expr;
return 1;
}
return 0;
}
Analisis Leksikal
Corak ini diterangkan menggunakan oleh simbol yang dinamakan ungkapan regular.
Satu cara yang paling mudah untuk membangunkan penganalisa Leksikal adalah
dengan membangunkan rajah yang menggambarkan struktur token. Bahasa AWK
menggunakan ungkapan regular untuk memilih baris input dan sistem shell UNIX
membenarkan pengguna untuk merujuk kepada nama fail dalam direktorinya
menggunakan ungkapan regular. Arahan rm *.o sebagai contoh digunakan untuk
menghapuskan semua fail yang mempunyai sambungan .o
Penganalisa leksikal merupakan fasa yang pertama dalam proses kompilasi. Tugasnya
yang paling utama ialah membaca aksara input dan menghasilkan jujukan token yang
digunakan oleh penghurai untuk melakukan analisis sintaks.
token
Token
seterusnya
Jadual
Simbol
Penganalisa boleh boleh dibahagikan kepada dua fasa iaitu pengimbas dan analisis
leksikal.
Dengan analisis leksikal kita menggunakan istilah “token”, “corak” dan “lexeme”
dengan maksud yang khusus. Ini ditunjukkan dalam jadual berikut :
Corak merupakan peraturan string yang boleh ada dalam aturcara. Lexeme adalah
jujukan aksara dalam aturcara sumber yang padan dengan corak suatu token. Contoh :
#define pi 3.143
Token diterima dalam pengkompil sebagai simbol terminal dalam nahu. Lexeme
yang pada dengan corak untuk perwakilan token mewakili aksara-aksara dalam
aturcara sumber.
DO 5 I = 1.25
Dalam pernyataan di atas, kita tidak boleh menyatakan bahawa DO bukan pencam
kata khas sehingga kita mengetahui bahawa wujud pemalar titik apung dihujungnya.
Sebaliknya kita boleh apabila wujud string DO5I
DO 5 I = 1, 25
Dalam pernyataan di atas, token-token yang terbina adalah pencam khas DO, pemalar
5, pencam I, operator = 1, tanda khas , dan pemalar 25. Dalam kes ini DO adalah
pencam khas hanya apabila koma dijumpai.
Oleh kerana terdapat pencam khas, penganalisa leksikal perlu membezakan antara
pencam khas dengan pencam pembolehubah.
Atribut Token
Apabila terdapat lebih daripada satu corak yang sepadan dengan lexeme, penganalisa
leksikal perlu maklumat tambahan untuk lexeme tertentu yang sepadan.
Secara parktikal, satu token akan mempunyai satu atribut sahaja iaitu penunjuk
kepada jadual simbol dimana maklumat mengenai token disimpan. Untuk tujuan
penilaian, pengkompil memerlukan lexeme untuk pencam dan baris dimana ia
dijumpai. Kedua-dua maklumat ini boleh disimpan dalam jadual simbol.
Ralat Leksikal
fi ( a = = 2)
Penganalisa leksikal tidak boleh memberikan ralat fi adalah pencam khas salah ejaan
atau satu pembolehubah yang tidak diistiharkan. Sebaliknya penganlisa leksikal akan
mengembalikan token pencam dan membiarkan fasa seterusnya untuk menunjukkan
ia adalah ralat.
Ralat yang ditemui di fasa analisis ini adalah ralat yang wujud apabila string yang
dijumpai tidak memenuhi corak token. Apabila satu aksara yang dibaca tidak
memenuhi syarat aksara yang sebelumnya, penganalisa leksikal boleh melakukan :
Aliran Input
Pasangan Penimbal
: : E: :=:M:*: C :* :* :2:eof : :
Permulaan bergerak
Simbol eof digunakan untuk menandakan penghujung penimbal, jika terdapat lagi
ruangan kosong di hadapan buffer kedua. Penggunaan dua penunjuk perlu untuk
proses pengenalpastian token. Contoh, pernyataan :
SPESIFIKASI TOKEN
Ungkapan Regular adalah notasi penting untuk menyatakan corak token. Istilah
aksara mewakilkan set simbol terhingga biasanya huruf dan karakter. Set {0, 1}
adalah aksara perduaan. ASCII dan EBCDIC merupakan aksara komputer.
String adalah simbol yang terhasil daripada jujukkan aksara. Dalam teori bahasa, ayat
atau perkataan adalah bersamaan dengan istilah string yang kita gunakan. Panjang
untuk satu string ditulis dengan cara | s |. String nol diwakilkan dengan .
Jika x dan y adalah string maka percantuman x dan y ditulis sebagai xy. Sebagai
contoh jika x = kucing dan y adalah harimau, maka xy menjadi kucingharimau.
String nul sentiasa berada sebagai percantuman satu string seperti berikut :
s = s = s
Operasi Bahasa
Operasi Definisi
Kesatuan L dan M ditulis sebagai L M L M = { s | s dalam L atau s dalam M }
Percantuman L dan M ditulis sebagai LM LM = {st | s dalam L dan t dalam m}
Tutupan Kleene ditulis L * L* =
L* mewakili kosong atau lebih cantuman L
Tutupan tambah ditulis L+ L+
L+ mewakili satu atau lebih cantuman L
Contoh :
Biarkan L adalah set {A, B, …., Z, a, b,…., z} dan D adalah set {0, 1,.., 9)
L adalah alphabet mengandungi aksara huruf besar dan huruf kecil. D pula
merupakan alphabet mengandungi set sepuluh digit decimal.
Ungkapan Regular
Ungkapan ini telah diperkenalkan oleh Warren McCulloch dan Walter Pitts, kedua-
duanya adalah ahli neuro-physiologists. Ia menunjukkan satu cara untuk
menunjukkan rangkaian neural menggunakan matematik. Pada tahun 1956, seorang
ahli matematik Amerika yang bernama Stephen Kleene, telah membina aturcara
menggunakan ungkapan ini. Beliau telah menerbitkan satu kertas kerja yang bertajuk
Representation of Events in Nerve Nets.
Ungkapan regular merupakan cara untuk menerangkan sesuatu yang dipanggil "the
algebra of regular sets". (Daripada istilah tersebut wujud nama"regular expression." ).
Penggunaan ungkapan ini menjadi pencetus kepada usaha-usaha awal dalam
pembangunan alkhawarizmi pencarian secara berkomputer yang digunakan oleh Ken
Thomson, pengasas Unix. Aplikasi yang pertama menggunakan ungkapan regular
yang dipratikkan dinamakan sebagai qed, digunakan dalam editor Unix.
Dalam C, pencam adalah satu huruf yang diikuti dengan kosong atau lebih sama ada
huruf atau digit atau aksara underscore. Kita menggunakan notasi yang dinamakan
sebagai ungkapan regular untuk menunjukkan perwakilan token. Dalam definisi
pencam C, ungkapan nalar yang kita perolehi adalah :
Bar menegak ‘|’ bermaksud atau, dan kurungan pula digunakan untuk mengumpulkan
sub-ungkapan. Ungkapan regular dibina menggunakan definisi set peraturan yang
dicipta. Setiap ungkapan regular r mewakilkan satu bahasa L( r ). Ia menunjukkan
peraturan L( r ) dari segi pembentukkan bahasanya dalam pelbagai bentuk mengikut
definisi.
1. adalah satu ungkapan regular yang mewakilkan {}. Ia menunjukkan set yang
mengandungi string nol.
2. Jika a adalah simbol dalam , maka a adalah ungkapan regular yang boleh
diwakilkan dengan{a}, iaitu set mengandungi a.
3. r dan s adalah ungkapan regular yang mewakilkan bahasa L(r) dan L(s), maka,
Contoh :
Biarkan = {a,b}
p/s : (a | b) = (b | a)
Definisi Regular
Jika adalah alphabet dengan simbol-simbol asas, definsi regular adalah jujukan
definisi pembentukkan :
d1 r1
d2 r2
:
dn rn
di adalah nama dan setiap ri adalah ungkapan regular untuk simbol { d1, d2, … di-
1}
Kita boleh menggunakan ungkapan regular yang telah diberikan nama untuk
pembentukkan ungkapan regular yang lain.
Contoh,
Kita telah mengetahui peraturan untuk string pencam dalam C. Kita gunakan
ungkapan regular untuk mewakilkan peraturan tersebut :
huruf A | B | …| Z| a | b | ….|z
digit 0 | 1 | …|9
unscore _
id huruf ( huruf | digit | unscore ) *
Contoh ungkapan regular pencam pemalar nombor seperti 1250, 39.37, 6.45E4 atau
2.75E-4 adalah seperti berikut :
Notasi Ringkas
Oleh kerana beberapa pengeluaran berlaku berulang kali dalam ungkapam regular,
maka, aktiviti ini amat mudah dibangunkan menggunakan notasi yang diringkaskan
seperti berikut :
1. Satu atau lebih. Operator +bermaksud satu atau lebih. Jika r adalah ungkapan
regular yang mewakili bahasa L(r ), maka r + merupakan ungkapan regular
yang mewakili (L(r)) +. Oleh itu, ungkapan regular a+ mewakili set untuk
semua string yang mengandungi satu atau lebih a. Operator + ini mempunyai
persamaan dengan operator , iaitu r* = r+ | dan r+ = rr*.
digit 0 | 1 | .. | 9
digit digit+
digit_apung (.digit)?
digit_exponen (E ( + | -)? digit)?
num digit digit_apung digit exponen
3. Kelas aksara. Notasi [abc] dimana a, b dan c adalah simbol alphabet yang
mewakili ungkapan regular a | b | c. Ringkasan [a-z] mewakili ungkapan
regular a | b | …|z. Menggunakan ungkapan yang dinyatakan, kita boleh
menghuraikan pencam string menggunakan ungkapan regular [A-Za-z][A-Za-
z0-9]*.
Set non-Regular
Ada bahasa yang tidak boleh dihuraikan menggunakan ungkapan regular. Untuk
menggambarkan keadaan ini, berikut adalah contoh yang dinyatakan. Mencari tutup
kurungan untuk pembuka kurungan. Masalah ini boleh diselesaikan menggunakan
Nahu Bebas Konteks atau Context Free Grammar (CFG).
Selain dari itu, pengulangan string tidak dapat diwakilkan menggunakan ungkapan
regular. { wcw | w adalah string a dan b }.
Terminal if, then, else, ophub, id dan num menjana set string yang diberikan oleh
definisi regular berikut :
if if
((
))
else else
ophub < | <= | = = | != | > | >=
huruf A | B | …| Z| a | b | ….|z
digit 0 | 1 | …|9
unscore _
id huruf (huruf | digit | unscore) *
num digit+ (. digit+) | (E ( + | - ) | digit +
P/S: Token tanda khas diberikan simbol khas iaitu BK, TK, LK, SD, TS, LB, BS
Gambarajah Peralihan
Gambarajah ini digunakan untuk menjejaki maklumat aksara yang dijumpai. Ini
dilakukan dengan menggerakkan posisi gambarajah.
Pada bahagian pertama ini kita mengandaikan bahawa rajah peralihan yang
digunakan adalah deterministic; iaitu, tiada simbol yang boleh sepadan dengan label
pada dua peralihan daripada satu keadaan.
Terdapat satu keadaan yang dilabelkan sebagai keadaan mula. Keadaan mula adalah
permulaan untuk suatu token. Untuk menjejaki token, kita membaca input dan
kemudian input tersebut dipadankan dengan keadaan dalam rajah. Oleh kerana itu,
input dibaca pada setiap kali tiba di keadaan. Jika wujud peralihan yang sepadan
dengan simbol input, maka pergerakan adalah mengikut peralihan ke arah keadaan
yang ditunjuk. Sebaliknya, jika tidak wujud aksara input yang sepadan dengan aksara
dalam rajah, maka ralat dijumpai.
Rajah berikut menunjukkan gambarajah perlihan untuk mengecam corak >= dan >
mula > =
(0) (6) ((7))
Lain-lain
((8))*
Kita menjejaki rajah peralihan bermula daripada keadaan 0, iaitu keadaan mula.
Daripada keadaan tersebut baca input. Jika aksara input adalah >, maka ikut
Apabila berada di keadaan 6, input seterusnya dibaca. Jika input adalah = maka ikut
peralihan ke keadaan 7. Jika tidak ikut peralihan ke keadaan 8. Tanda (( ))
menunjukkan keadaan terima, iaitu suatu token telah dicam.
Oleh kerana aksara > dan satu aksara tambahan telah dibaca apabila pergerakkan
sampai di keadaan 8, maka penganalisis perlu memulangkan kembali aksara
tambahan tersebut. Tanda * menunjukkan terdapat tindakan yang perlu dilakukan
terhadap aksara yang dibaca.
Secara umum, terdapat banyak jenis rajah peralihan untuk mengecam kumpulan-
kumpulan token. Semasa analisis mencari token, jika ralat berlaku, maka analisis
dilakukan menggunakan rajah yang lain pula dilakukan untuk mencari jenis suatu
token.
mula < =
return(ophub,KS)
(0) (1) ((2))
Lain-lain
((3))* return(ophub,LK)
=
=
(4) ((5)) return(ophub,SD)
Lain-lain
return(opkhas,UP)
((6))*
>
=
return(ophub,BS)
(7) ((8))
Lain-lain
return(ophub,LB)
((9))*
Satu teknik mudah untuk membezakan pencam pembolehubah dengan pencam khas
adalah dengan memberikan nilai awalan yang tepat kepada jadual simbol. Sebagai
contoh pencam if, else dimasukkan dalam jadual simbol sebelum input dibaca. Ini
membolehkan jadual simbol memulangkan maklumat mengenai token ini jika ia
dijumpai. Pernyataan ambiltoken( ) dan masukid( ) menunjukkan nilai token serta
atributnya diambil untuk disemak dan kemudian disimpan dalam jadual simbol.
Fungsi gettoken( ) digunakan untuk menyemak token yang dijumpai dengan token
dalam jadual simbol.
mula Lain-lain
digit . E digit + atau - digit
(13) (14) (15) (16) (17) (18) (19) ((20)) *
digit digit
digit digit
digit
Terdapat beberapa isu yang timbul apabila membina pengecam untuk nombor tak
bertanda yang diberikan oleh ungkapan regular berikut :
lexeme untuk token seperti ini berkemungkinan panjang. Sebagai contoh, penganalisa
tidak boleh berhenti sehingga 22 atau 22.4 apabila menjumpai input 22.4E3.
Jujukan rajah peralihan boleh ditukarkan kepada aturcara untuk mengecam token
yang telah diterangkan menggunakan rajah peralihan.
Setiap keadaan akan menjadi satu kod segmen aturcara. Jika terdapat peralihan
daripada satu keadaan, maka kod membaca aksara dan pilih peralihan untuk diikuti.
Fungsi aksberikut( ) digunakan untuk membaca aksara seterusnya daripada buffer
input.
int fail(void)
{
depan = token_mula;
switch(mula)
{
case 0 : mula = 10; break;
case 10 : mula = 13; break;
case 13 : mula = 21; break;
case 21 : mula = 26; break;
case 26 : recover( ); break;
}
return mula;
}
Kita memperkenalkan satu alat yang dinamakan sebagai Lex, yang telah digunakan
dalam penganalisa leksikal untuk banyak bahasa. Pengkompil ini dinamakan sebagai
pengkompil Lex dan inputnya dipanggil sebagai Bahasa Lex.
case 10 : c = aksberikut ( );
if ( isletter( c ) ) keadaan = 11;
else
keadaan = fail( );
break;
case 11 : c = aksberikut ( );
if (isletter( c ) ) keadaan = 11;
else if (isdigit( c ) ) keadaan = 11;
else
keadaan = 12;
break;
case 12 : retract(1); masukid( );
return( ambiltoken( ) );
case 13 :
case 14 :
case 15 :
case 16 :
case 17 :
case 18 :
case 19 :
case 26 : c = askberikut ( );
if (isdigit( c ) ) keadaan = 27;
else
keadaan = fail( );
break;
case 27 : c = aksberikut ( );
if (isdigit( c )) keadaan = 26;
else
keadaan = 28;
break;
case 28 : retract(1); masuknum( );
return (NUM);
}
}
}
void masukid(void)
{
/*fungsi ini adalah untuk memasukkan nilai pencam dalam lexeme. */
}
void masuknum(void)
{
/* fungsi ini adalah untuk memasukkan nilai nombor dalam lexeme */
}
1. Untuk setiap nonterminal A, bina satu fungsi yang mempunyai parameter formal
untuk setiap atribut warisan A yang memulangkan nilai-nilai sinthesized atribut
A. Atribut ini adalah berkemungkinan satu record[struct], penunjuk kepada rekod
dengan medan untuk setiap atribut atau menggunakan mekanisma panggilan
fungsi secara rujukan untuk menghantar parameter. Untuk memudahkan
kefahaman, kita andaikan bahawa setiap nonterminal hanya mempunyai satu
atribut sinthesized. Fungsi untuk A mempunyai satu pembolehubah setempat
untuk setiap atribut nahu simbol yang wujud dalam pengeluaran A.
2. Kod untuk nonterminal A menentukan pengeluaran untuk digunakan bergantung
kepada input simbol semasa.
3. Kod yang berkaitan dengan setiap pengeluaran melakukan perkara yang
dinyatakan di bawah. Andaikan token-token, nonterminal dan perlaksanaan pada
sebelah kanan dilakukan dari kiri ke kanan.
OPERATOR pandang
DO 5 I = 1.25
DO 5 I = 1,25
Dalam Fotran, ruang kosong adalah tidak dikira. Oleh kerana itu, pernyataan di atas
boleh dianggapkan sebagai :
DO5I=1.25
DO5I=1,25
AUTOMATA TERHINGGA
Satu pengecam untuk bahasa merupakan satu aturcara yang menerima input, string x
dan mengeluarkan maklumat “betul” apabila x adalah satu ayat yang betul untuk
bahasa tersebut.
1. deterministic
2. nondeterministic
Satu NFA boleh diwakilkan oleh label graf terarah yang dipanggil graf peralihan.
Nod-nod dalam graf ini adalah keadaan dan anak panah merupakan fungsi peralihan.
Graf ini seakan-akan sama dengan rajah peralihan. Perbezaaannya adalah, aksara
yang sama boleh menjadi peralihan daripada dua atau lebih kedaaan. Anak panah
juga boleh dilabelkan dengan simbol .
Graf peralihan untuk menerima bahasa (a | b)* abb ditunjukkan seperti berikut :
a b b
mula
0 1 2 3
BAB III
NAHU BAHASA
Pengenalan
Struktur sesuatu bahasa dikenali sebagai sintaks bahasa dan makna sesuatu bahasa
dikenali sebagai semantik bahasa.
Semantik hanya boleh dikenalpasti setelah sintaks sesuatu bahasa telah memenuhi
syarat atau rumus. Sebagai contoh :
A S+P
S FN
P {FN,FK,FA,FS}
FN (Bil) + (Pbil) + (Gel) + KN + (KN..)+ (Penerang) + (Penentu)
FK (Kbtu) + {Kktr+FN+(Ket)} {Kkttr+(Pel)}
FA (Kbtu) + (Kpeng) + KA + {(KA,KN)}+(Ket)
FS (Kbtu)+KSN+(Karah)+FN+(Ket)
Penerang {FA,FS}
Pel {FN,FA,FS}
Kbtu (Knafi)+Kbtu+(Kbtu)
Ket FS
Kpeng Kpeng + (Kpeng)
Penentu Kbtu
Rajah 1
Oleh kerana itu, tiga ayat di atas dapat disemak kesahihannya dengan merujuk kepada
jadual ini.
Kn Kkjr Kn.
Ayat kedua tidak memenuhi syarat yang ditetapkan.
Ayat yang ketiga betul daripada segi sintaks (struktur), tetapi ia mempunyai kesilapan
semantik (makna).
Analisis Nahu :
Dalam pemerhatian kita, sintaksis (nahu) boleh dinyatakan dalam bentuk matematik
sebagai 4-tuple; yang mewakilkan empat komponen iaitu : alphabet, nonterminal,
production dan simbol gol. Perwakilan ini diwakilkan dengan simbol , N, P dan S.
Ia dilitupi dengan ( dan ) dan dipisahkan dengan koma :
(, N, P, S)
Set yang pertama, , adalah alphabet atau satu set terminals. Ia adalah terhingga dan
mengandungi karakter atau simbol yang boleh disusun membentuk ayat dalam
Set kedua dalam definisi nahu matematik adalah set nonterminals yang ditunjukkan
dalam 4-tuple sebagai “N”. Ia adalah set simbol terhingga yang tidak berada dalam
alphabet. Nonterminals bukan satu set string, tetapi simbol boleh dinyatakan sebagai
perwakilan string iaitu subset *. Dalam nonterminal tertentu, simbol gol,
mewakilkan semua string dalam bahasa. Nonterminal dipanggil kategori sintatik atau
pembolehubah nahu.
Peraturan untuk nahu ( tuple ketiga) adalah set untuk menulis semula
peraturan, ditulis sebagai 2 string yang diasingkan dengan anak panah.
Contoh-contoh Nahu
Contoh yang pertama, G1 adalah nahu yang paling mudah. Ia adalah nahu untuk
semua string mengandungi dua huruf a, b, c ( kemungkinan huruf yang sama) dalam
mana-mana turutan.
Dalam grammar ini, adalah set huruf kecil { a, b, c}; nonterminal pula adalah set
huruf besar { A, B} yang mana A adalah simbol gol. Set pengeluaran disenaraikan di
bawah :
A aB B a
A bB B b
Contoh :
Sehingga ini, hanya terminals yang tinggal dan tiada nonterminals yang ditemui.
Proses terhenti.
Langkah-langkah ini dinamakan derivation, dan boleh ditulis dalam satu baris
menjadi :
A => bB => bc
Selain daripada derivation di atas, pemilihan production yang lain juga boleh
menghasilkan : A => aB => aa
D.1. E E + T
D.2. E T
D.3. T T * F
D.4. T F
D.5. F (E)
D.6. F n
Pengeluaran pertama mempunyai satu nonterminal tunggal, E pada sebelah kiri dan
dua nonterminal E dan T yang dipisahkan oleh terminal “+’ pada sebelah kanan.
Pengeluaran ini bermaksud apabila nahu ini digunakan, nonterminal E muncul dalam
string iaitu nonterminal boleh ditulis sebagai string “E+T”. Nahu ini akan derive
n+n*n.
E
E+T
E+T*F
T+T*F
Proses ini boleh digambarkan dengan diagram yang dipanggil abstract syntact tree
atau parse tree atau derivation tree.
E =>* n+n*n
bermaksud nonterminal E mewakilkan n+n*n dalam sifar atau lebih jujukan. Ayat
boleh dibentuk daripada * yang bermula dengan simbol gol dengan satu atau lebih
jujukan. Satu sentential form, ialah string dalam ( N)* yang mengandungi
sejumlah terminals dan nonterminals dalam hubungan,
Jika dua parse tree dapat dibina daripada nahu, maka ia dikatakan sebagai nahu
berambiguiti.
Kedua-dua nahu di atas mempunyai satu simbol nonterminal pada sebelah kiri
pengeluaran. Karakteristik grammar seperti ini amat seseuai digunakan dalam
pembangunan pengkompil.
Terdapat juga satu grammar yang tidak rigid kepada peraturan ini.
A aABC CB BC
A aBC
bC bc ab ab
cC cc bB bb
Hirarki Chomsky
Ahli linguistik, Noam Chomsky (1950) mendefinisikan grammar kepada empat paras
yang boleh untuk menganalisis bahasa.
Paras ini berkait secara langsung dengan kelas automata untuk mengenalpasti bahasa.
Penghurai adalah proses untuk mengenalpasti token bagi string yang dijana oleh
nahu. Masalah ini lebih mudah difahami dengan mengkaji bagaimana satu pokok
boleh dibina. Walaupun pengkompil tidak membina pokok huraian, namun, satu
penghurai perlu menghasilkan pokok supaya hasil yang diperolehi adalah tepat.
Bahagian ini akan memperkenalkan satu method untuk menjana penterjemah sintak.
Satu penghurai boleh dibina untuk sebarang nahu.
Untuk nahu bebas konteks, satu penghurai selalunya mengambil masa paling lama O
(n3) untuk menghurai string yang mempunyai n token. Namun n kuasa tiga adalah
terlalu lama untuk bahasa pengaturcaraan komputer. Penghurai bahasa
pengaturcaraan biasanya membuat imbasan daripada kiri-ke-kanan terhadap inputnya.
Method penghuraian terbahagi kepada dua kelas iaitu atas-bawah dan bawah-atas.
Istilah ini merujuk kepada nod-nod dalam pokok penghurai yang dibina. Pembinaan
atas-bawah bermula di akar dan turun ke daun-daun sementara bawah atas adalah
bermula di daun dan naik ke atas. Penghurai atas-bawah lebih mudah dilakukan
berbanding dengan penghurai bawah-atas. Walaubagaimanapun, penghurai bawah-
atas mampu untuk melakukan huraian nahu untuk kelas-kelas yang besar dan
kompleks.
Penghurai Atas-bawah
Penjanaan penhurai atas-bawah bermula pada pokok huraian bermula di akar yang
dilabel dengan nonterminal mula. Ia seterusnya akan melakukan aktiviti berikut
secara berulang-ulang :
Untuk sesetengah nahu, langkah-langkah di atas, boleh dilakukan pada masa imbasan
kiri ke kanan input string. Gambarajah di bawah menunjukkan proses menghurai
string.
jenis
jenis
jenis
jenis
jenis
integer
Daripada rajah didapati bahawa, apabila satu nod dimasukkan dalam pokok, satu
simbol yang dinamakan simbol pandang akan membandingkan dengan input semasa.
Secara am, pemilihan pengeluaran untuk nonterminal melibatkan proses “trial and
error”, iaitu, satu pengeluaran dicuba dan kemudian cuba pula dengan yang lain jika
didapati bahawa pengeluaran tersebut tidak memenuhi padanan.
Penghurai Ramal
Penghurai ramal adalah satu method atas-bawah untuk analisis sintaks yang
menggunakan fungsi secara rekursif untuk memproses input. Satu fungsi adalah
berkait dengan setiap nonterminal nahu. Berikut adalah kod-pseudo untuk penghurai
ramal.
void jenis
{
jika ( pandang adalah integer atau char atau num )
mudah;
else jika pandang = = ‘^’
{
padan(‘^’);
padan(id);
}
else jika pandang = = array
{
padan( array ) ;
padan( ‘[’ );
mudah;
padan( ‘]’);
padan ( of );
jenis;
}
else
ralat; }
Huraian bermula dengan panggilan terhadap fungsi untuk nonterminal jenis (kerana
jenis adalah simbol mula). Fungsi jenis melakukan aktiviti :
Perhatikan bahawa setiap terminal pada sebelah kanan dipadankan dengan simbol
pandang dan setiap nonterminal pada sebelah kanan akan memanggil fungsinya.
Biarkan berada pada sebelah kanan pengeluaran untuk nonterminal A. Kita berikan
PERTAMA() sebagai set token-token yang akan muncul sebagai simbol-simbol
pertama untuk satu atau lebih string yang terjana daripada .
Set PERTAMA perlu mengambil kira jika terdapat dua pengeluaran A dan A
. Simbol pandang boleh digunakan untuk memilih pengeluaran untuk digunakan.
Jika simbol pandang dalam PERTAMA() maka digunakan. Sebaliknya jika
simbol dalam PERTAMA() maka digunakan.
Sebagai contoh,
pern { pern_pilihan }
pern_pilihan senarai_pern |
Satu penghurai ramal adalah satu aturcara yang mengandungi fungsi-fungsi untuk
setiap nonterminal. Setiap fungsi melakukan dua perkara :
Rekursif kiri
Penghurai rekursif boleh menyebabkan penghurai bergelung tanpa henti. Masalah ini
timbul dengan pengeluaran seperti berikut :
Pengeluaran seperti ini mempunyai simbol nonterminal yang sama pada sebelah
kanan dan pada sebelah kiri. Ia akan menyebabkan fungsi akan memanggil fungsi
yang sama berulang kali. Masalah ini boleh diselesaikan dengan mengubah semula
pengeluaran. Sebagai contoh :
A A |
dan adalah perwakilan untuk jujukan terminal dan nonterminal yang tidak
bermula dengan A. Sebagai contoh,
A R
R R |
Dalam pengeluaran yang baru ini, R adalah nonterminal baru yang diwujudkan dan ia
telah menggantikan rekursif kiri kepada rekursif kanan.
Untuk menguji apa yang telah dibincangkan, kita cuba dengan ungkapan yang
mengandungi ungkapan yang mengandungi digit yang dipisahkan oleh tanda campur
dan tanda tolak seperti berikut :
Titik permulaan untuk menterjemah satu input string adalah menggunakan pokok
sintaks atau abstract syntax tree.Dalam pokok ini, setiap nod diwakilkan dengan
operator dan anak nod diwakilkan dengan operan.
Pokok penghurai pula dipanggil sebagai concrete syntax tree yang mewakilkan nahu
sebagai sintaks concrete sesuatu bahasa.
Pokok sintaks berbeza daripada pokok penghurai kerana hanya terminal yang penting
untuk sintaks sahaja yang muncul.
- 2
9 5
‘+’ dan ‘-’ mempunyai keutamaan yang sama, oleh sebab itu, pokok dinilai dari kiri
ke kanan. Kita dapati bahawa pokok sintaks bersekutu dengan operator di nod
dalaman.
Membina skema penterjemahan adalah bergantung kepada nahu yang mana pokok
penghurai adalah hampir sama dengan pokok sintaks.
maka ia tidak sesuai untuk dilakukan penghurai ramalan. Cara yang nyata untuk
mengatasi masalah ini ialah dengan menghapuskan rekursif kiri.
Nahu berikut pula tidak sesuai untuk diterjemah kepada bentuk postfix.
Nahu ini mempunyai masalah dimana operan operator yang dijana oleh rest +
ungkp dan rest - unkp adalah bukan nyata daripada pengeluaran. Tidak satupun
daripada pilihan pembentukan penterjemahan rest.t daripada unkp.t berikut diterima
pakai :
Penukaran A A | A | kepada
A R
R R | R |
unkp
term rest
2 {print(‘2’)}
void unkp(void)
{
term( );
rest( );
}
void rest(void)
{
if (pandang = = ‘+’)
{
match(‘+’);
term( );
putchar(‘+’);
rest( );
}
void term(void)
{
if (isdigit(pandang))
{
putchar(pandang);
match(pandang);
}
else error( );
}
#include <ctype.h>
int pandang;
void main(void)
{
pandang = getchar( );
unkp( );
putchar(‘\n’);
}
void unkp(void)
{
term( );
while (1)
if (pandang = = ‘+’)
{
match(‘+’);
term( );
void term(void)
{
if (isdigit(pandang))
{
putchar(pandang);
padan(pandang);
}
else error( );
}
void padan(int t)
{
if (pandang = = t)
pandang = getchar( );
else error( );
}
void ralat(void)
{
printf(“Ralat Sintaks”);
exit(1);
}
Analisis Leksikal
Tambahan kepada bahagian yang dibincangkan sebelum ini adalah analisis leksikal.
Penganalisa leksikal membaca dan kemudian menukar input kepada jujukan token
Ruangan kosong dan komen dalam aturcara perlu diabaikan untuk memberikan
maksud kepada pengkompil. Jujukan karakter yang terdiri daripada token dipanggil
lexeme.
Pemalar
Tugas untuk mengenalpasti digit sebagai integer dilakukan oleh penganalisa leksikal.
Biarkan num mewakili token integer. Apabila jujukan digit muncul dalam jujukan
input, penganalisa leksikal akan menghantar num kepada penghurai. Nilai integer
tersebut dihantar sebagai atribut token num.
31 + 28 + 59
< num, 31> < +, > <num, 28> < +, > <num, 59>
token + tidak mempunyai atribut. Komponen kedua dalam perwakilan iaitu atribut
tidak memainkan peranan semasa penghuraian tetapi ia diperlukan semasa
penterjemahan.
Satu masalah yang mungkin dihadapi oleh penganalisa adalah untuk token-token
yang mempunyai dua karakter seperti = = , < = dan > =. Teknik untuk mengecam
karakter seperti ini akan dibincangkan dalam bab khusus mengenai penganalisa
leksikal.
Pulang
karakter
Sebagai contoh, jika penganalisa menjumpai token operator >, jika karakter
seterusnya adalah =, maka token yang dijumpai adalah token lebih besar dan sama.
Tetapi jika token seterusnya adalah selain daripada =, maka ia akan memulang token
kembali kepada jujukan input dan menghantar token lebih besar kepada penghurai.
Token yang dipulang akan dibaca semula sebagai kumpulan token(lexemes) yang
baru.
Tujuan penganlisa leksikal adalah untuk membenarkan ruang kosong dan nombor
muncul di antara ungkapan. Berikut adalah model penganalisa leksikal.
(Penganalisa
ungetc(c,stdin) leksikal)
nilaitoken
Dalam model yang ditunjukkan di atas, karakter dibaca oleh fungsi lexan( ). Fungsi
akan menghantar nilai token menggunakan perwakilan nombor. Sebagai contoh jika
token adalah berjenis NUM, kita akan wakilkan token NUM sebagai integer 256.
Aturcara C untuk mengenalpasti nahu di atas boleh dilakukan secara langsung seperti
berikut :
void faktor(void)
{
if ( pandang = = ‘(’ )
{
padan( ‘(’ );
unkp( );
padan( ‘)’ );
}
else if ( pandang = = NUM )
{
printf(“ %d ”, nilaitoken);
padan(NUM);
}
else error( );
}
#include <stdio.h>
int baris = 1;
int nilaitoken = NONE;
int lexan(void)
{
int t;
while(1)
{
t = getchar( );
if ( t = = ‘ ’ | | t = = ‘\t’ )
;
else if ( t = = ‘\n’ )
baris++;
else if ( isdigit(t) )
{
nilaitoken = t – ‘0’;
t = getchar( );
while ( isdigit(t) )
{
nilaitoken = nilaitoken * 10 + t – ‘0’;
t = getchar( );
}
ungetc(t, stdin);
return NUM;
}
else {
nilaitoken = NONE;
return t;
}
}
}
}
Dalam fungsi lexan( ), apabila karakter ruang kosong atau tab dijumpai, ia tidak
memulangkan sebarang token kepada penghurai, dan ia akan terus mengulang.
Jika karakter ‘\n’ dijumpai, pembolehubah baris akan menokok satu baris. Ia adalah
untuk memantau aktiviti yang berlaku pada setiap baris. Ini membolehkan
pengkompil mengenalpasti baris ralat.
Jika input adalah nombor, nilai interger dalam ASCII dan EBCDIC diberikan
mengguna ungkapan t – ‘0’.
Sebagai contoh, pada masa analisis leksikal, karakter yang membentuk pencam
disimpan dalam kemasukkan jadual simbol. Pada fasa pengkompil seterusnya,
pengkompil akan memasukkan kemasukkan ini dengan maklumat seperti jenis
pencam dan kedudukkannya dalam ingatan. Dalam fasa penjanaan kod, maklumat ini
akan digunakan untuk menjana kod yang sempurna untuk menyimpan dan mencapai
pembolehubah ini.
Rutin jadual simbol berminat untuk menyimpan dan mencapai lexeme. Berikut
adalah operasi asas jadual simbol.
Katakunci atau perkataan Rezab juga disimpan dalam jadual simbol. Sebagai contoh
token div dan mod (Pascal).
Kita boleh memberikan nilai awalan kepada jadual simbol dengan cara :
SelitTok(“div”, div);
SelitTok(“mod”, mod);
Jika fungsi GelintarTok(“div”) dipanggil, maka, nilai true akan dikembalikan. Ini
menunjukkan bahawa “div” tidak boleh digunakan sebagai pencam pembolehubah.
{
mula loop
{
baca c;
if c adalah ruang kosong atau tab
;
else if c adalah baris baru
baris++;
else if c adalah digit
{
setkan nilaitoken dengan nilai ini dan digit-digit selepasnya;
return NUM;
}
Mesin abstrak mempunyai bahagian suruhan dan bahagian data dalam ingatannya.
Semua operasi arithmetik dilaksanakan pada nilai dalam timbunan. Suruhan ini
terbahagi kepada tiga kelas iaitu; arithmetik integer, manipulasi timbunan dan aliran
kawalan. Terdapat satu “program counter” (pc) untuk mengenalpasti suruhan yang
perlu dilaksanakan. Berikut Rajah berikut menggambarkan contoh mesin abstrak.
1 push 5 16 0 1
2 nkanan 2
3 + 7 11 2
atas
4 nkanan
5 * pc 7 3
6 ….
… 4
Suruhan Arithmetic
Walau bagaimanapun, dalam operasi yang lebih kompleks, ia perlu dilakukan sebagai
jujukan suruhan mesin abstrak.
Kita memudahkan penghuraian mesin ini dengan menggangap terdapat satu suruhan
untuk setiap operator arithmetik.
1. MasukTimbunan 1
2. MasukTimbunan 3
3. tambah dua operan teratas, KeluarTimbunan (pop) dan masukkan hasiltambah
dalam timbunan
4. MasukTimbunan 5
5. darabkan dua operan teratas, KeluarTimbunan dan masukkan hasildarab
dalam timbunan
Nilai atas timbunan pada akhirnya adalah 20. Ia adalah nilai untuk penyelesaian
ungkapan yang diberikan.
Terdapat perbezaan antara pencam pada sebelah kiri dan pencam pada sebelah kanan
dalam satu pernyataan umpukkan. Contoh :
i = 5;
i = i + 1;
Nilai pada sebelah kanan menyatakan nilai, sementara nilai di sebelah kiri
,menyatakan dimana nilai disimpan.
Begitu juga dengan penggunaan penunjuk. Jika p dan q adalah penunjuk kepada
karakter seperti berikut :
Manipulasi Timbunan
Terjermahan Ungkapan
Kod untuk penilaian satu ungkapan dalam timbunan berkait rapat dengan notasi
postfix ungkapan tersebut.
Nkanan a
Nkanan b
+
Diterjemahkan menjadi :
Aliran Kawalan
Dengan dua pilihan pertama di atas, terdapat lagi kemungkinan tambahan untuk
mengambil operan daripada atas timbunan.
Menterjemah Pernyataan
Berikut adalah kod mesin abstrak untuk pernyataan bersyarat pilihan dan ulangan.
Pilihan Ulangan
Goto test
Label out
Dalam mesin di atas, terdapat hanya satu suruhan untuk ke label out dari aturcara
sumber; jika tidak, akan berlaku kekeliruan.
Fungsi emit digunakan untuk mencetak hasil terjemahan. Berikut adalah nahu yang
digunakan :
nahu asal :
Dalam pengeluaran di atas, tertib tindakan adalah: tindakan semasa menghurai unkp
dilakukan, out disetkan kepada label, dikembalikan oleh labelbaru dan arahan gofalse
dikeluarkan. Tindakan semasa menghurai pern dilakukan, dan arahan label
dikeluarkan.
Kod untuk menterjemah pernyataan umpukkan dan pilihan dilakukan seperti berikut :
void pern(void)
{
int test,out;
if (pandang = = id)
{
emit(“nkiri”, nilaitok);
padan(id);
padan(‘=’);
unkp( );
}
Contoh :
if (t = = ‘’ || t = = ‘\t’)
Dalam ungkapan di atas, jika t adalah ruang kosong, ujian terhadap t sama dengan tab
tidak perlu dilakukan kerana menggunakan matik ‘atau’ syarat menjadi benar jika
syarat pertama telah benar.
Kod unkp1
Salin // salin nilai unkp1
Gotrue out
Pop // pop nilai unkp1
Kod unkp2
Label out
Arahan gotrue dan gofalse mengeluarkan nilai dari atas timbunan.
Menggabungkan teknik-teknik
Dalam perbincangan yang lepas kita telah mewakilkan mewakilkan nombor daripada
teknik sintaks-berpandu untuk membina pengkompil.
Dalam bahagian ini kita menggunakan aturcara untuk penterjemahan infix kepada
postfix untuk bahasa yang mengandungi ungkapan yang diakhiri dengan semicolon.
Output daripada terjemahan ini adalah perwakilan postfix untuk setiap ungkapan.
faktor ( unkp )
| id { printf (id.lexeme) }
| num { printf(num.nilai) }
Aturcara akan bermula dengan memanggil fungsi init( ) untuk pengistiharan nilai-
nilai awalan. Ini diikuti dengan fungsi parse( ) untuk aktiviti penterjemahan.
ID mewakili pencam, NUM mewakili nombor dan DONE mewakili aksara end-of-
file.
Fungsi parse( ) digunakan untuk melaksanakan tugas ini. Fungsi ini memanggil
fungsi lexan untuk mengenalpasti token. Kemudian ia menggunakan fungsi error
untuk melaporkan ralat sintaks.
Modul cetak menggunakan fungsi emit(t, nilait) yang akan menjana output daripada
token t dengan atribut nilait. Jadual simbol mempunyai tatasusunan jadualSim yang
mempunyai dua nilai iaitu penunjuk kepada tatasusunan lexemes dan integer yang
mengandungi perwakilan token yang disimpan. Operasi Selit(s, t) mengembalikan
indeks untuk lexeme s yang terdiri daripada token-token t. Fungsi pandang(s)
mengembalikan indeks untuk kemasukkan JadualSim iaitu lexemes s atau 0 jika s
tidak wujud.
Aturcara :
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
void unkp(void);
void term(void);
void padan(int);
void init(void);
int insert( char s[], int);
void error( char *m);
int lookahead;
int lineno = 1;
int tokenval = -1;
char lexemes[999];
int lastchar = -1;
int lastentry;
struct entry {
char *lexptr;
int token;
};
FILE *fin;
main()
{
fin = fopen("kompil.in","r");
int i;
init();
parse();
void term(void)
{
int t;
factor();
while(1)
switch(lookahead) {
case '*' :
case '/' :
case '%' :
case 257 :
case 258:
t = lookahead;
padan(lookahead);
factor();
emit(t, -1);
void factor(void)
{
switch(lookahead) {
case '(' :
padan('('); expr(); padan(')'); break;
case 256 :
emit(256, tokenval); padan(256); break;
case 259 :
emit(259, tokenval); padan(259); break;
default :
error("ralat sintaks 2");
}
}
void padan(int t)
{
if (lookahead == t)
{
// fflush(stdin);
//lookahead = getchar();
lookahead = lexan();
}
else error("ralat");
}
void init(void)
{
strcpy(temp1,"div");
keywords[0].lexptr = temp1;
keywords[0].token = DIV;
strcpy(temp2,"mod");
keywords[1].lexptr = temp2;
keywords[1].token = MOD;
strcpy(temp3,"0");
void parse(void)
{
lookahead = lexan();
while (lookahead != 260)
{
unkp();
padan(';');
}
}
while (1)
{
t = fgetc(fin);
if (( t == ' ') ||( t == '\t'))
;
else if ( t == '\n')
lineno++;
else if (isdigit(t)) {
ungetc(t,fin);
fscanf(fin,"%d", &tokenval);
return NUM;
}
else if (isalpha(t))
{
int p, b = 0;
while (isalnum(t))
{
lexbuf[b] = t;
t = fgetc(fin);
b++;
if ( b >= 128)
error ("ralat kompilasi");
}
lexbuf[b] = '\0';
if ( t != EOF)
ungetc(t, fin);
p = lookup(lexbuf);
if ( p == 0)
p = insert(lexbuf, 259);
tokenval = p;
return symtable[p].token;
}
Fail kompil.in
n4om * 4 ;
(9 - no) + 3 * 4 ;
n + 1;
5 + 10 ;
7 + bil ;
10 5+2-
Sintaks Bahasa
Definisi
Kita akan menghuraikan bahasa yang mempunyai peraturan yang mengikut notasi
yang dipanggil sebagai nahu bebas konteks. Nahu dihuraikan melalui struktur hiarki
bahasa pengaturcaraan yang dibina. Sebagai contoh pernyataan if-else dalam C
mempunyai bentuk
Pernyataan if-else ini terbentuk dengan katakunci if, buka kurungan, ungkapan, tutup
kurungan, pernyataan, katakunci else dan pernyataan. Pembentukan ini boleh
diwakilkan dalam bentuk peraturan seperti berikut :
(nahu 1)
Dengan mewakilkan pernyataan sebagai pern dan ungkapan sebagai unkp, peraturan
ini disebut sebagai satu pengeluaran (production). Anak panah dibaca sebagai
“boleh mempunyai pengeluaran”
1. Nahu yang bersesuaian dengan huraian tidak mengubah struktur hiarki bahasa.
Sebagai contoh, subrutin Fotran, boleh dilihat dengan melihat senarai
POKOK Sintaks
if-then-else
B S1 S2
Dalam pokok sintaks, operator dan katakunci tidak muncul sebagai daun, tetapi ia
muncul sama ada sebagai nod dalaman atau sebagai bapa. Satu lain kebaikkan dalam
pokok sintaks adalah jaringan pengeluaran boleh hilang seperti yang ditunjukkan
dalam rajah di bawah :
* 4
3 5
Setiap nod dalam pokok sintaks boleh disediakan sebagai rekod dengan beberapa
medan. Dalam nod untuk operator, satu medan mengisi operator dan yang selainnya
mengandungi penunjuk kepada nod untuk operan. Berikut adalah fungsi-fungsi yang
digunakan untuk mencipta nod pokok sintaks untuk ungkapan dengan operator binari.
Setiap fungsi akan mengembalikan penunjuk kepada nod yang baru dicipta.
Contoh :
Berikut adalah jujukan fungsi membina pokok sintaks untuk ungkapan a-4+c. p1,p2,
…,p5 adalah penunjuk nod pencam dan entrya dan entryc adalah penunjuk kepada
kemasukkan jadual-simbol untuk pencam a dan c.
(1) p1 = mkleaf(id,entrya);
(2) p2 = mkleaf(num,4);
(3) p3 = mknode(‘-’,p1,p2);
(4) p4 = mkleaf(id,entryc);
(5) p5 = mknode(‘+’,p3,p4);
Pokok ini dibina dari bawah ke atas. Fungsi memanggil mkleaf(id,entrya) dan
mkleaf(num,4). Panggilan ini mencipta nod daun untuk a dan 4 dan penunjuk untuk
nod-nod ini disimpan menggunakan p1 dan p2. Panggilan mknode(‘-’,p1,p2)
kemudian membina nod dalaman dengan anak nod adalah a dan 4. Selepas dua
langkah, p5 menunjuk kepada akar.
Rajah di bawah mengandungi S-atribut untuk membina satu pokok sintaks untuk satu
ungkapan yang mengandungi operator + dan -. Penggunaan atribut nptr untuk E dan
T adalah untuk menjejaki penunjuk yang memulangkan panggilan fungsi.
E.nptr
E.nptr + T.nptr
E - T.nptr id
T.nptr num +
-
id
num 4 id
id
a c
P
pengaturcaraan, 1, 2, 10, 14, 16, 18, 19, 24, 36, 42