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:
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 };
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
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;
{ 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
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=¥iGramer.yeniKural("exp");
temp->sagTarafEkle(yeniGramer.sembolBul("set")); temp->sagTarafEkle(yeniGramer.sembolBul("id")); temp->sagTarafEkle(yeniGramer.sembolBul("=")); temp->sagTarafEkle(yeniGramer.sembolBul("exp"));
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 }
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
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=="$") {
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;
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.
Ç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: ?