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; Durum *dfaDurumListe; durumların eklendiği liste int dfaDurumSayi; char *tumKarakterler[1000]; karakterlerin listesi public: Otomat(Durum *);
//NFA aĢamasındaki durum listesi //DFA'ya dönüĢürken yeni oluĢan //DFA durum sayısını tutan değiĢken //GeçiĢ için kullanılan tüm //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; char * tur; int *val;
//Sozcugun içeriği (x,y,val, ifzero vs.) //Sozcuğun turu (id,num vs) //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; tutar Sembol * sagTaraf; listesini tutar int sagTarafSayi;
//Gramer kuralının sol tarafındaki sembol ü //Sağ taraftaki sembol veya sembol //sağ taraftaki listenin eleman sayısını tutar
040010447 Mehmet Melih Karcı
Kural(); Kural(Sembol & yeniSolTaraf); void sagTarafEkle(Sembol & yeniSembol); yaratılırken sağ taraf listesine sembol ekler Sembol & solTarafAl(); döndürür
//Yeni bir kural //Sol tarafı
};
class Gramer{ public: Kural *kuralListe; listelendiği dizi int kuralSayi; Sembol * sembolListe; listelendiği dizi int sembolSayi; string girisKatar; Hucre * tablo; oluĢturan hücreler int tabloSayi;
//TÜm kuralların //toplam kural sayısı //kullanılan tüm sembollerin //Toplam sembol sayısı //GiriĢ sözcüğü //AYrıĢtırma tablosunu //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"));
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 var set ifzero then else + = { } ( ) ; id num program exp exp2 term block vars vars2 exps exps2 Izle kumeleri Sembol program exp exp2 term block vars vars2 exps exps2
Küme (boşlukla ayrılmış) var set ifzero then else + = { } ( ) ; id num id num ( { set ifzero id num ( { set ifzero +-# id num ( { var id num ( { set ifzero id # id id num ( { set ifzero #;
Küme (boşlukla ayrılmış) $ $ then else ) ; } $ then else ) ; } + - $ then else ) ; } } ; ; } }
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 Program Program program program program program exp id exp num exp ( exp { exp set exp ifzero exp2 + exp2 exp2 $ exp2 then exp2 else exp2 ) exp2 ; exp2 } term id term num term ( term { block var block id block num block ( block { block set block ifzero vars id vars2 ; vars2 id exps id exps num exps ( exps { exps set exps ifzero exps2 } exps2 ;
Sonuç id num ( { set ifzero
exp exp exp exp exp exp termexp2 termexp2 termexp2 termexp2 setid=exp ifzeroexpthenexpelseexp +term -term # # # # # # id num (exp) {block} varvars;exps exps exps exps exps exps exps idvars2 # vars expexps2 expexps2 expexps2 expexps2 expexps2 expexps2 # ;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ı: Yapılacak anlamsal işlem:
exp → set id = exp 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: ?