Download as pdf or txt
Download as pdf or txt
You are on page 1of 12

040010447

Mehmet Melih Karcı


Derleyici Tasarımı – Dönem Ödevi

D Programlama Diline İlişkin Derleyici Tasarımı

Proje’nin amacı D programlama dilinde verilen program kodunun derlenip çalıştırılmasını sağlayan bir
derleyici tasarlamaktır. Derleyici yapılırken programalama dili olarak C++, geliştirme ortamı olarak ise
Windows işletim sisteminde çalışan Visual Studio kullanılmıştır. Ortaya çıkan çalıştırılabilir dosyanın
gereksinimleri ise Windows işletim sistemi olan bir bilgisayardır.

Program 3 farklı yapıya sahiptir;

 Tarayıcı
 Ayrıştırıcı
 Yorumlayıcı

1) Tarayıcı

Tarayıcı işlev olarak gelen kaynak kodunu sözcüklere indirger. Karakter karakter taradığı kaynak
kodunu, dilin tanımlı sözcüklerine çevirir ve bunları bir sözcük dizisi olarak tutar. Dilde tanımlı
olmayan bir karakter geldiğinde hata verir. Bu işlemi gerçekleştirmek için öncelikli olarak dilin tanımlı
sözcükleri bir NFA oluşturacak şekilde kullanılır;

Örneğin “var” kelimesi;

V a r

Görüldüğü üzere girişten v-a-r karakterleriyle son duruma giden bu NFA gibi diğer sözcüklerin de
NFA’ları yaratılır ve hepsi tek bir başlangıç konumunda birleştirilir. Oluşturulan bu NFA daha sonra
DFA’ya çevrilir. Bütün bu işlemlerin gerçekleştirildiği kısmın kaynak kodu ve sınıfları aşağıda
gösterilmiştir;

class Otomat{
Durum *eskiDurumListe; //NFA aĢamasındaki durum listesi
Durum *dfaDurumListe; //DFA'ya dönüĢürken yeni oluĢan
durumların eklendiği liste
int dfaDurumSayi; //DFA durum sayısını tutan değiĢken
char *tumKarakterler[1000]; //GeçiĢ için kullanılan tüm
karakterlerin listesi
public:
Otomat(Durum *); //Constructor
040010447
Mehmet Melih Karcı
Durum & nfa2dfa(); //NFA DFA dönüĢümünü yapan metod
void dfaGecisYarat( Durum *); //Verilen sözcüklere göre DFA
geçiĢlerini yaratır
bool ayniDfaDurumVarmi(int *); //NFA - DFA dönüĢümü sırasında
oluĢan yeni durumların daha önce oluĢturudu mu diye kontrol eder
void dfaYarat(int *altDurumList);//DFA için durum yaratır
Durum & dfaDurumBul(int*); //DFA listesindeki durumlar içinde
arama yapar
bool kontrolEdilmemisVarMi(); //DFA oluĢtururken tüm karakterlere
geçiĢleri kontrol edilmemiĢ durum var mı diye bakar

};

class Durum{
Gecis *gecisListe; //Tüm geçiĢlerin listelendiği dizi
int id; //Durum id'sini tutar
int gecisSayi; //Sahip olduğu geçiĢlerin sayısını tutar
bool baslangic; //BaĢlangıç durumu olup olmadığını tutar
bool son; //Son durum olup olmadığını tutar
bool dfa; //DFA'ya ait olup olmadığını tutar,
false ise bir NFA dahilindedir
int *altDurumListe; //DFA dahilinde bir durumsa, dönüĢüm
sırasında temsil ettiği NFA durumlarının listesi vardır
bool dfaGecisKontrol; //DFA dönüĢümü sırasında tüm geçiĢlere
karĢılığı bulunmuĢ mu onu belirtir. FALSE ise iĢlem yapılmamıĢtır
char * sozcukTip; //Eğer son durum ise hangi sözcüğün son durumu
olduğu bilgisini tutar
public:
Durum(int,char*,char*); //Constructor
Durum(); //Default constructor
void yeniGecis(Durum&,char* ); //Bu durumdan yeni bir geçiĢ
yaratır (baĢka duruma)
void setId(int); //Id atar
int getId(); //Id döndürür
void baslangicYap(); //baĢlangıç durumu yapar
void sonYap(); //Son durum yapar
bool baslangicKontrol(); //BaĢlangıç durumu olup olmadığını
döndürür
bool sonKontrol(); //Son durum olup olmadığını
döndürür
void ayarlaDfa(); //DFA durumu yapar
void sifirlaDfaGecisKontrol(); //DFA gecis durumunu false yapar
void ayarlaDfaGecisKontrol(); //DFA gecis durumunu true yapar
bool alDfaGecisKontrol(); //DFA gecis durumunu döndürür
int * closureIdAl(); //Closure kümesini döndürür (id)
void altDurumListeEkle(int *); //Alt durumlarına liste ekler (DFA
dönüĢümü sırasında)
int * gecisAl(char*); //GeçiĢlerin listesini döndürür
int gecisIdAl(char*); //istenen geçiĢin id'sini döndürür
Durum & sonrakiDurumAl(char *); //Belirtilen geçiĢ için sonraki
durumu verir
int * altDurumListeAl(); //DFA dönüĢümü sırasında temsil
ettiği durumların listesini ver,r
char * alSozcukTip(); //Sozcuk tipini döndürür
void degistirSozcukTip(char *); //Sozcuk dipini değiĢtirir

};
040010447
Mehmet Melih Karcı

class Gecis{
Durum *sonraki; //Bu geçiĢin iĢaret ettiği sonraki
durumun iĢaretçisi
char* gecisKatar; //GeçiĢin hangi katarla yapıldığı
public:
Gecis(Durum &, char*);
Gecis();
bool operator==(Gecis const &) const; //Operatör overload, eĢit
mi?
char* gecisKatarAl() const; //GeçiĢ katarını
döndürür
Durum & sonrakiDurumAl(); //Sonraki durumu
döndürür

};

Yukardaki sınıf tanımları NFA – DFA dönüşümü için kullanılmıştır. Yarattığım Otomat objesinin içinde
bir durum listesi bulunmaktadır. Bu durumlar da bir birleriyle geçiş objeleri sayesinde bağlıdır. Her
durumda bir geçiş listesi bulunmaktadır. Bu geçişler bir sonraki duruma ait bir işaretçi taşır. Her
geçişin hangi karakter için olduğu da yine bu geçiş objelerinde saklanmaktadır. Bu sayede bir NFA
bilgisayarda simüle edilebilir. Öncelikle tüm sözcük tipleri NFA olarak otomata yerleştirirlir. Daha
sonra otomat bu NFA’ları tek bir başlangıç durumuyla birleştirir. Oluşan bu yeni NFA üzerinde DFA
dönüşümü yapılır. Boş katar olarak “#” kullanılmıştır. Otomat daha sonra sahip olduğu bu DFA ile
programı sözcüklere ayırabilmektedir.

DFA oluşturulduktan sonra oluşturulan Tarayici objesine bu DFA’nın ilk durumu eklenir (işaretçisi)
Tarayıcı sınıfının yapısı aşağıdaki gibidir;

class Tarayici{
public:
Sozcuk *sozcukListe; //Son durumda oluĢacak sözcük listesi

int sozcukSayi; //Sözcük listesindeki sözcük


sayısı
char *girilenKod; //DOsyadan okunan ve taranacak kod
Durum *dfaIlkDurum; //DFA'nın ilk durumu
char * tumKarakterler[1000];//Dildeki olası tüm karakterlerin listesi
DegiskenTablosu degiskenTablo; //DeğiĢken tablosu
bool islemIzin; //DeğiĢken tablosunda iĢlem izni
Tarayici(Durum &);
void SozcukListeYarat(); //Sözcük listesi oluĢturur
bool tekSozcukCek(); //Koddan tek bir sözcük tarar
void dosyadanOku(); //Dosyadan okur

};
040010447
Mehmet Melih Karcı

class Sozcuk{
public:
char *icerik; //Sozcugun içeriği (x,y,val, ifzero vs.)
char * tur; //Sozcuğun turu (id,num vs)
int *val; //Sözcük num ise, sahip olduğu int değeri

Sozcuk();
Sozcuk(char * ,char * );
void print(); //yazdırır

};

DFA’ya sahip olan tarayıcı objesi, dosyadan (kod.txt) ilgili kodu okur. Daha sonra bu kodu,
otomata sokarak, çıkan son durumlara göre sözcük listesine sözcükleri ekler. Böylece girilen
kod sözcüklere ayrılmış bir şekilde eklenir. Üzerinde çalıştığımız D programlama dilinde
girilen tek karakterler değişken olarak algılandığından Tarayıcının kodda hata bulması
mümkün değildir. Çünkü hatalı olan tüm a-z karakterleri değişken olarak kaydedilmektedir.
Fakat beklenmeyen bir karakter geldiğinde ( mesela ! ) tarayıcı işlemi iptal edip, bu karakteri
tanımadığını belirtir. Yine D programlama dilinde değişkenler

Var a;

Şeklinde tanımlanmaktadır. Bu durumda tarayıcı değişkeni DegiskenTablosu tipindeki


tablosuna ekler. Bu sınıfın yapısı aşağıda incelenmiştir.

class DegiskenTablosu{
public:
Degisken *liste; //DeğiĢkenlerin tutulduğu liste
int degiskenSayi; //değiĢken listesindeki obje sayısı
int sonDerinlik; //Son kullanılan derinlik
DegiskenTablosu();
void yeniDegiskenEkle(string yeniIsim); //Yeni değiĢken ekler
Degisken & degiskenBul(string yeniIsim); //EklenmiĢ değiĢkenlerden
isme göre tarama yapıp iĢaretçisini döndürür
};

class Degisken{
public:
int deger; //DeğiĢkenin tuttuğu int tipinden değer
string isim; //DeğiĢkenin tanımlanmıĢ ismi
int derinlik; //Sahip olduğu derinlik
Degisken();
Degisken(int yeniDeger,string yeniIsim,int yeniDerinlik);

};

Değişken tablosu bir değişken dizisi ihtiva eder. Ayrıca tabloda sonDerinlik isimli bir int değeri
vardır. Bu değer {block} durumlarında değişkenlerin ayırt edilmesini sağlar;
040010447
Mehmet Melih Karcı
{ var x y; // x derinliği=0, y derinliği=0
set x = 1 + (3 - 2) ; // x derinliği = 0

{ var x ; // yeni bir x derinliği 1


set y = x + ( 2 + y) ; //derinliği 1 olan x ile derinliği
0 olan y iĢlemde

}
}

Yani her { geldiğinde derinlik 1 arttırılır, her } geldiğinde derinlik 1 azaltılır. Bu çözüm sanırım
derste kullandığımızla aynı değildi ama ben 3. Ödeve gelene kadar kurduğum yapıya daha
uygun bir çözüm bulamadım. Yorumlama sırasında bu yöntem bana büyük kolaylık sağladı.
Değişken tablosunda liste halinde bulunan Değişken objeleri ise değişken hakkında gerekli
bilgileri tutar.

Özet olarak, tarayıcı gelen katar dizisini bir sözcük dizisine çevirir ve değişkenleri değişken
tablosuna kaydeder. Bundan sonraki aşama ayrıştırıcının işidir.

2) Ayrıştırıcı

Ayrıştırıcının gerçeklenmesi için verilen gramer kurallarının uygun bir şekilde kodlanması
gerekmektedir. Bu yapı için aşağıdaki sınıfları yarattım;

class Sembol{
public:
string isim; //Sembolün ismi, burada program, exp, term vs
birer semboldür
bool terminal; //terminal/nonterminal belirtir
string *ilk; //Ġlk kümesi oluĢturulduktan sonra bu liste
kullanılacak
int ilkSayi; //ilk kümesi için liste eleman sayısı
string *izle; //Ġzle kümesi oluĢturulduktan sonra burada
tutulacak
int izleSayi; //izle kümesi için liste elemen sayısı
Sembol();
Sembol(string yeniIsim,bool terminalMi);
string isimAl(); //isim döndürür
bool esitKontrol(string kontrolIsim); //Verilen sözcükle aynı olup
olmadığını döndürür

};

class Kural{
public:
Sembol solTaraf; //Gramer kuralının sol tarafındaki sembol ü
tutar
Sembol * sagTaraf; //Sağ taraftaki sembol veya sembol
listesini tutar
int sagTarafSayi; //sağ taraftaki listenin eleman sayısını tutar
040010447
Mehmet Melih Karcı

Kural();
Kural(Sembol & yeniSolTaraf);
void sagTarafEkle(Sembol & yeniSembol); //Yeni bir kural
yaratılırken sağ taraf listesine sembol ekler
Sembol & solTarafAl(); //Sol tarafı
döndürür

};

class Gramer{
public:
Kural *kuralListe; //TÜm kuralların
listelendiği dizi
int kuralSayi; //toplam kural sayısı
Sembol * sembolListe; //kullanılan tüm sembollerin
listelendiği dizi
int sembolSayi; //Toplam sembol sayısı
string girisKatar; //GiriĢ sözcüğü
Hucre * tablo; //AYrıĢtırma tablosunu
oluĢturan hücreler
int tabloSayi; //Hücre sayısı

Gramer(Sembol * yeniSembolListe, int yeniSembolSayi,string


yeniGirisKatar );
Kural & yeniKural(string yeniIsim); //Yeni kural ekler
Sembol & sembolBul(string araIsim); //Verilen isimdeki sembolü
döndürür
void ilkOlustur(string yisim); //ĠStenen sembolün ilk
kümesini oluĢturur
string * ilkAl(string yisim); //Ġstenen sembolün ilk kümesinin
iĢaretçisini döndürür
void izleOlustur(Kural & gKural); //Ġstenen sembolün izle kümesini
oluĢturur
void tabloYarat(); //AyrıĢtırma tablosu
yaratır
void ilkKumeleriniOlustur(); //Tüm ilk kümelerini oluĢturur
void izleKumeleriniOlustur(); //Tüm izle kümelerini oluĢturur
Hucre * tablodanCek(Sembol satir, string sutun); //Tablodu ilgili
satırın sütundaki karĢılığı döndürürlür

};

Yukarıda görüldüğü üzere Gramer tipinden bir sınıfım var ve bu sınıfta kurallardan ve sembollerden
oluşan bir liste var. Temel olarak bir Gramer objesi bir Kurallar listesi barındırıyor. Kuralların her biri
de “sol” ve “sağ” olmak üzere 2 Sembol objesi tutuyor. Tabi ki sağ taraftaki sembol objesi aslında bir
sembol dizisi.

Exp → set ID = exp

Şeklindeki bir kural, gramer’e aşağıdaki kodla eklenebiliyor;

temp=&yeniGramer.yeniKural("exp");
temp->sagTarafEkle(yeniGramer.sembolBul("set"));
temp->sagTarafEkle(yeniGramer.sembolBul("id"));
temp->sagTarafEkle(yeniGramer.sembolBul("="));
temp->sagTarafEkle(yeniGramer.sembolBul("exp"));
040010447
Mehmet Melih Karcı
Burada dikkat etmemiz gereken nokta, yeni kural eklerken kullandığımız isimlerin bir önceki aşamada
( tarayıcı) kullandığımız sözcüklerle aynı olması. Bu sayede tarayıcının hazırladığı sözcük dizisinin bu
gramere uygun olup olmadığını kontrol edeceğiz.

Gramer kurallarını yukarıda bahsettiğim şekilde programa yerleştirdikten sonra yapmamız gereken İlk
ve İzle kümlerini bulmak ve daha sonra Ayrıştırma Tablosunu oluşturmak.

Verilen gramerin sol rekürsif olmasından dolayı öncelikle bu sorunu ortadan kaldırmamız gerekiyor.
Rekürsif bir ayrıştırıcı da sol rekürsif özelliklerin bulunması sonsuz döngüye sebebiyet vermektedir.
Sol rekürsif ve factoring özellikleri giderildikten sonra oluşan gramerin ilk ve izle tabloları program
kendisi yaratmaktadır. Ödevin 2. Tesliminde ekrana yazılan fakat son aşamada ekrana yazdırma
gereği duymadığım ilk ve izle kümeleri aşağıdaki gibi verilmektedir.

Ilk kumeleri
Sembol Küme (boşlukla ayrılmış)
var var
set set
ifzero ifzero
then then
else else
+ +
- -
= =
{ {
} }
( (
) )
; ;
id id
num num
program id num ( { set ifzero
exp id num ( { set ifzero
exp2 +-#
term id num ( {
block var id num ( { set ifzero
vars id
vars2 # id
exps id num ( { set ifzero
exps2 #;

Izle kumeleri
Sembol Küme (boşlukla ayrılmış)
program $
exp $ then else ) ; }
exp2 $ then else ) ; }
term + - $ then else ) ; }
block }
vars ;
vars2 ;
exps }
exps2 }
040010447
Mehmet Melih Karcı
Bu kümeler şu şekilde listelenmiştir, sol tarafta kimin kümesi olduğu, sağ tarafta küme elemanları.
Yani program sembolünün izle kümesinde sadece $ karakteri vardır. Oluşturulan bu kümelere göre
ayrıştırma tablosu aşağıdaki gibidir;

Satır Sütun Sonuç


Program id exp
Program num exp
program ( exp
program { exp
program set exp
program ifzero exp
exp id termexp2
exp num termexp2
exp ( termexp2
exp { termexp2
exp set setid=exp
exp ifzero ifzeroexpthenexpelseexp
exp2 + +term
exp2 - -term
exp2 $ #
exp2 then #
exp2 else #
exp2 ) #
exp2 ; #
exp2 } #
term id id
term num num
term ( (exp)
term { {block}
block var varvars;exps
block id exps
block num exps
block ( exps
block { exps
block set exps
block ifzero exps
vars id idvars2
vars2 ; #
vars2 id vars
exps id expexps2
exps num expexps2
exps ( expexps2
exps { expexps2
exps set expexps2
exps ifzero expexps2
exps2 } #
exps2 ; ;exps

Yukarıdaki gösterim aslında tablonun değişik bir halidir. Derste gördüğümüz tabloyu kullanırken nasıl
bir mantık izliyorsak program da aynı mantığı izlemektedir. Yığından gelen sembolü satır üzerinden
seçiyor, sözcük dizisinden aldığını sütun kısmından ve sonucu alıyor. Kodda belirttiğim üzere aslında
040010447
Mehmet Melih Karcı
Tablo Hücre objelerinden oluşmuş bir liste barındırıyor ve bu dizi içinde satır&sütun bilgileri girilerek
yapılan bir arama sonucunu veriyor.

Yazmış olduğum kod esnek bir yapı barındırmaktadır. Yani dilenirse kolayca başka bir gramer girilip
ayrıştırma tablosu otomatik olarak çıkartılabilir. Tek yapılması gereken gramer kurallarının girildiği
kod parçasını yeni gramer e göre oluşturmaktır. Ayrıştırma tablosu tamamen kod çalışırken
oluşturulduğundan ayrıştırma fonksiyonlarını da elle yazmama gerek kalmamıştır. Tablodaki verilere
göre ayrıştırma işlemini yapan Genel Fonksiyonum Yorumlayıcı kısmında ayrıntılı olarak işlenecektir.

Ayrıştırma işleminin yapısına geri dönmek gerekirse, öncelikle bir yığın yaratır ve giriş sözcüğüyle $
karakterini bu yığına ekler. Sözcük listesine de $ karakterini ekledikten sonra yığından ve sözcük
listesinden çektiği elemanları kullanarak tablodan yeni bir sembol alır. Bu sembolü yığına ekleyerek
devam eder. Yığındaki $ karakterine karşılık sözcük listesinde $ karakterini görünce girilen kodun
belirtilen gramere uygun olduğuna karar verir. Yığın sınıfı aşağıdaki gibidir;

class Yigin
{
public:
Sembol * yiginListe; //Yığındaki sembollerin tutulduğu dizi
int yiginListeSayi; //Sembol dizisinin eleman sayısı
Yigin();
Sembol & cek(); //Yığının en üstündeki sembolü
geri döndürüp yığından siler
void ekle(Sembol * yeni,int yeniSayi); //Yığına yeni sembol ekler

};

3) Yorumlayıcı
Yorumlayıcının görevi, dilin sözel olarak belirtilmiş anlamsal özelliklerini işlemek ve bir sonuç
döndürmektir. Derleyicinin bu kısmı için daha önce belirttiklerim dışında bir sınıf yaratmama gerek
kalmamıştır. Rekürsif ayrıştırıcının gerçeklendiği metod dahilinde, her ayrıştırma işlemi sırasında
değişkenlerin değerlerini belirtilen kurallara göre değiştirmem yeterli olmuştur. Ayrıştırıcının bu
kısmının genel yapısı özet olarak aşağıdaki gibidir;

int rekursifAyristir(int &girisSayi, Yigin & yeniYigin, Tarayici &


yeniTarayici, Gramer & yeniGramer)
{
int sonuc=-1;
Sembol yiginSonCek=yeniYigin.cek();
Sozcuk giris=yeniTarayici.sozcukListe[girisSayi];

string karsGiris;

string karsYigin=yiginSonCek.isim;
if( (strcmp( giris.tur , "id" )==0) || (strcmp( giris.tur ,
"num" )==0) )
karsGiris=string(giris.tur);
else
karsGiris=string(giris.icerik);

if(karsYigin=="$" && karsGiris=="$")


{
cout << "\nIslem BASARILI!\n\n\n\n" << endl;
040010447
Mehmet Melih Karcı
system("pause");
exit(0);
}

if(karsYigin=="$" && girisSayi!=yeniTarayici.sozcukSayi)


{
cout << "\nHATA: Islem basarisiz!\n\n\n\n" << endl;
system("pause");
exit(-1);
}

if(yiginSonCek.terminal==true && karsYigin==karsGiris)


{
girisSayi++;

//rekursifAyristir(girisSayi,yeniYigin,yeniTarayici,yeniGramer);
}
else
{
Hucre *temp=new Hucre();
try{
temp=yeniGramer.tablodanCek(yiginSonCek,karsGiris);
}catch(string hata)
{
cout << "\nHATA: Islem basarisiz!\nBeklenmeyen
karakter : \"" << karsGiris<< "\"\n\n\n" << endl;
system("pause");
exit(-1);
}

yeniYigin.ekle(temp->sonuc,temp->sonucSayi);

//ANLAMSAL ĠġLEMLERĠN OLDUĞU KISIM


//Çok yer kapladığından
//buraya aktarılmamıĢtır, derleyici.cpp dosyasından
//tamamı incelenebilir

}
}

Kodu açıklamam gerekirse şu aşamalardan oluşmaktadır;


 Yığından bir sembol çek
 Sözcük listesinden bir sözcük al
 Sözcük tipini belirle
 Yığından çekilen sembol = $ ve sözcük =$ ise işlemi sonlandır (başarılı)
 Yığından çekilen sembol = $ ve sözcükler bitmediyse işlemi sonlandır (başarısız)
 Yığından çekilen sembol bir terminalse ve sözcükle aynıysa bir sonraki sözcüğe geç
 Yığından son çekilen sembol nonterminalse tablodan karşılığını al ve bunu tabloya ekle
 Tablodan alınan bu gramer kuralının gerektirdiği anlamsal işlemi yerine getir

Yukarıda görüldüğü gibi anlamsal işlemler yığından çekilen sembolün tablodan alınan bilgiye göre
başka bir sembol/sembollere dönüşmesi esnasından gerçekleşmektedir. Sözel olarak verilen bu
anlamsal işlemlerden birini ilgili kod üzerinden anlatacağım;
040010447
Mehmet Melih Karcı
Gramer kuralı: exp → set id = exp
Yapılacak anlamsal işlem: değişkenin değerini exp sonucuna değiştir

Kod:

if(yiginSonCek.isim=="exp" )
{

if(temp->sonuc->isim=="set")
{
rekursifAyristir(girisSayi,yeniYigin,yeniTarayici,yeniGramer);
int *temp;
temp=&(yeniTarayici.degiskenTablo.degiskenBul(yeniTarayici.sozc
ukListe[girisSayi].icerik).deger);
rekursifAyristir(girisSayi,yeniYigin,yeniTarayici,yeniGramer);
rekursifAyristir(girisSayi,yeniYigin,yeniTarayici,yeniGramer);
if(yeniTarayici.islemIzin==true)
{
temp=rekursifAyristir(girisSayi,yeniYigin,yeniTarayici,yeniGram
er);
return *temp;
}else
{

rekursifAyristir(girisSayi,yeniYigin,yeniTarayici,yeniGramer);
return -1;
}

Burada exp sembolünden set sembolüne geçiş yapılmışsa yapılması gereken işlemi görmektesiniz.
Öncelikli olarak set kısmının işlenmesi için fonksiyon rekürsif olarak tekrar çağırılır. Daha sonra hangi
değişkenin değeri değiştirilecekse o değişkenin değerini tutan int tipindeki değişkenin işaretçisi alınır.
İd = kısımları da rekürsif fonksiyon sayesinde işlendikten sonra, exp kısmı işlemeye gönderilir ve
değeri temp işaretçisine atanır.

Burada dikkat edilmesi gereken işlem izni olup olmadığıdır. Diyelimki yukarıda ki kod aşağıdaki şekilde
yazılmış olsun

İfzero (2-2) then set x = 12 else set y = 13

Bu kodda y değişkeninin üzerinde işlem yapan son kısmın ayrıştırıcı tarafından ayrıştırılması
gerekmektedir. Fakat koşul sağlanmadığından y değişkeninin değerinin değiştirilmemesi lazım. Bunu
engellemek için “ifzero exp then exp else exp” kalıbı ayrıştırılırken koşula göre ilk ya da ikinci kısmı
ayrıştırmadan önce işlem izni kapatılır daha sonra tekrar açılır.
040010447
Mehmet Melih Karcı

Çalışan Örnekler:

{ var a b c;
set b = a + 23;
set a = b + 1
}
Sonuç : 24

{ var a b;
set a = 4+((4+2)-3) ;
ifzero a-12 then
set a = b + 7
else
set b = 4 - a ;
set b = a + 1 ;
(set b = b + 10) +
{ var b c ;
set b = a + ( 2 + b) ;
set c = ifzero b then 8 else b;
b + c
}
}
Sonuç : 36

Çalışmayan Örnekler:

{ var a b c;
set b = a + 23;
set a = b + 1
set
Sonuç: HATA: Islem basarisiz!
Beklenmeyen karakter : "set"

{ var a ;
set b = a + 23;
set a = b + 1
}
Sonuç: HATA: "b" isimli degisken bulunamadi, daha once tanimlamamis olabilirsiniz

{ var a ?;
set b = a + 23;
set a = b + 1
}
Sonuç: Tarayici bir hata buldu!
Bilinmeyen karakter: ?

You might also like