Derleyicinin Kısımları:

Bir programın derlenme süreci, şu aşamalardan oluşur:


  Parser:
 

sözdizimi analizi

                         
  AG00085_.gif (503 bytes)     AG00086_1.gif (345 bytes) AG00089_.gif (335 bytes)       AG00086_1.gif (345 bytes)  
  Tarayıcı:           Kod üretici:    
  AG00084_.gif (503 bytes) kelime analizi   anlam analizi   kod üretimi  
program
 
                AG00086_1.gif (345 bytes)  
    kelime tablosu       sembol tablosu     makine kodu veya arakod  
                    AG00086_1.gif (345 bytes)  
              AG00091_.gif (505 bytes) optimizasyon  
optimize makine kodu
 

Kelime analizinin amacı, dili oluşturan kelimeleri (token) tanımak ve bunları sözdizimi analizi için parser'a iletmektir. Parser, tarayıcıdan ardarda aldığı kelimelere, dilin özel (ayrılmış) kelime ve işaretleri yardımıyla sözdizimsel anlamlar yükler. Bu, bir cümledeki kelimelere özne, nesne, zarf, yüklem gibi görevleri yüklemeye benzer. Bu kelimeler tanındıkça sembol tablosu denen veri yapısına birtakım yan bilgilerle birlikte (değişken tipleri, adresleri, büyüklükleri, vb.) birlikte kaydedilir. Aynı kelimeler tekrar kullanıldıklarında, kullanıldıkları yere göre sembol tablosunda kayıtlı bulunan bilgiler alınarak hata kontrolü yapılır. Son aşama ise, bu kelimelerin cümledeki rolü ve anlamını kullanarak,buna uygun makine kodları üretmektir. Gerekirse üretilen kod optimize edilerek hızlandırılabilir, veya bellekte tuttuğu alan küçültülebilir. Optimizasyonun daha etkin yapılabilmesi için ve değişik makineler için kod üretilebilmesi için, bu işler için daha uygun ve daha genel birarakod üretilip, daha sonra bu arakod optimize edilip istenen makine için kod üretilebilir. Bazı derleyiciler ise sanal bir makine için hızlı yorumlanabilen bir kod üretir ve bu kodu yorumlayarak programı değişik makinelerde çalıştırır.

Gramer, Üretim, Alfabe, Dil:

Bir dilin grameri,

cümle    -> özne nesne yüklem
özne      -> Ben | isim
nesne    -> kitabı | eve
yüklem  -> gittim | aldı

gibi kurallarla belirlenir. Burada | sembolü veya anlamındadır. Okun sol tarafındaki sembollerden sağ taraftaki sembollerin türetileceğini anlatan bu kuralların herbirine üretim denir.Üretim kurallarının kümesi P ile gösterilir. Bir gramerin üretimlerinin hiçbirinde sol tarafta yer almamış olan sembollere uç (terminal) semboller denir. Yukarıdaki örnekte bunlar Ben, isim, kitabı ,eve, gittim ve aldı sembolleridir. Uç sembollerin kümesi T ile gösterilir. Örneğin, "Mehmet kitabı aldı" cümlesi tarayıcı tarafından parser'a isim(Mehmet), kitabı,aldı şeklinde iletilir. Parantez içindeki Mehmet, isim uç sembolünün taşıdığı sıfattır. Uç sembollerle birlikte parser'a iletilen bu sıfatlar, parser tarafından üretimler uygulanırken işlenir. Örnekte parser, ileride kullanmak için Mehmet'in isim olduğu bilgisini sembol tablosuna kaydeder. Daha sonra üretimleri sembollere uygulayarak cümle sembolünü elde eder. Uç semboller dışındaki sembollere ara (non-terminal) semboller denir. Örnekte bunlar cümle, özne, nesne ve yüklem'dir. Ara sembollerin kümesi N ile gösterilir. Bütün sembolleri kendisinden türeten özel ara sembole cümlesel sembol denir ve S ile gösterilir. Örnekte bu sembol cümle'dir. İşte S,P,N,T dörtlüsüne gramer denir ve G={S,P,N,T} ile gösterilir. V=NUT kümesine alfabe denir.S sembolüne ardarda uygulanan P'ye ait üretimler sonucu elde edilen ve uç sembollerden oluşan sembol grubuna cümle denir. Bir gramerden türetilebilecek tüm cümlelerin kümesine ise dil denir ve L(G) ile gösterilir. EPSİLON ile gösterilen özel uç sembol, boşluğa, daha doğrusu boşluk karakterine değil de, hiçbirşeye karşılık gelir.

Gramerlerin Sınıflandırılması:


Chomsky sınıflandırmasına göre gramerler genelden özele doğru birbirlerini içeren 4 sınıfa ayrılır:

1. Serbest gramerler:
Üretimler u -> v şeklindedir. Burada u,v
eV ve u<>EPSİLON dur.

2. Çevre-bağımlı (context-sensitive) gramerler:
Üretimler uxv -> uvw şeklindedir. Burada u,v,wcV, v<>EPSİLON ve x
eN dir. Yani, x ara sembolü, yalnızca u ve w sembol grupları ile çevrelendiği takdirde v sembol grubu ile türetilebilir.

3. Çevre-bağımsız (context-free) gramerler:
Üretimler x->v şeklindedir. Burada v
eV ve xeN dir. Programlama dilleri genellikle bu tür gramerlerden üretilir. Bu tür diller her zaman tanınabilir.

4. Sonlu (veya düzgün) gramerler:
Üretimler x->a veya x->ay şeklindedir. Burada x,y
eN ve aeT dir.Bu tür gramerler,dilin kelimelerini tanımlamak için kullanılır, yani kelime analizi sırasında tanınan bir gramerdir. Sonlu gramerler, sonlu durum makineleriyle çok etkin şekilde tanınır.

İşte kelime ve sözdizimi analizinin ayrı ayrı yapılmasının nedeni, tanınan dillerin farklı gramer sınıflarından olmasıdır. Kelime analizi çok daha etkin yapılır.

Gramerlerin Denkliği, Belirsizliği:

L(G) ve L(H) kümeleri birbirine eşitse, G ve H gramerlerine denk gramerler denir.Üretimler farklı sıralarda uygulanıp aynı bir cümle farklı şekilde türetilebiliyorsa bu gramere belirsiz (ambiguous) gramer denir. Parser'ın bu tür belirsiz bir grameri tek şekilde tanıması sağlanmalıdır,aksi halde alınacak sonuçlar her seferinde değişik olacaktır. Örneğin şu gramerde:

s -> aa
a -> x | xx

"xxx" cümlesi iki farklı sırada tanınabilir:

 

    s           s  
  /   \   veya   /   \  
  a   a       a   a  
/   /   \ /   \   \
x   x   x x   x   x


Bunlardan birisi parser tarafından tercih edilmelidir.

Sıfatlar (attributes) ve Genişletilmiş Gramer:

Her sembolün taşıdığı kendisine ait özelliğe o sembolün sıfatı denir.Üretimlerin yanında bu sıfatlarla ilgili işlemler konarak elde edilen gramere genişletilmiş gramer denir.Sol taraftaki sembolün sıfatı, sağda yeralan sembollerin sıfatları işlenerek oluşuyorsa, yani bunların bir fonksiyonuysa:

x -> y1 y2 y3 ... yn ve x.a = f(y1.a, y2.a, y3.a,...,yn.a) ise

(burada x.a ile x'in taşıdığı sıfat gösteriliyor),bu sıfatlara üretilmiş sıfatlar (synthesized attributes) denir.

Lex ve YACC kullanımı:

UNIX altında mevcut olan Lex ve YACC programları, derleyici oluşturmada çok büyük kolaylıklar sağlar.Lex programı, verilen bir sonlu grameri kabul eden, yani tanıyan tarayıcı üretir. YACC de benzer şekilde, verilen genişletilmiş bir LALR grameri kabul eden ve üretimler sırasında, bunların yanında verilmiş olan sıfatlarla ilgili işlemleri yapan bir parser üretir. İki programın çıktısı da, yani oluşturdukları tarayıcı ve parser programlar C dili ile yazılmıştır.

Lex'in kabul ettiği (.l uzantılı) dosya yapısı:

%{
C ifadeleri (seçimlik)
%}
lex tanımları (seçimlik)
%%
lex düzgün-ifadeleri ve işlemler
%% (seçimlik)
C ifadeleri (kullanıcı fonksiyonları)

Lex düzgün-ifadeleri ve operatörler:

Lex'de kullanılan düzgün-ifadeler, lex operatörleri ve tanınması istenen karakterlerden oluşur.

. \n harici tek bir karakter .a satır başında olmayan a ve a'dan önceki karakter
* sıfır veya daha fazla adet a[a-z]* a ile başlayan küçük harfli kelimeler (a dahil)
+ bir veya daha fazla adet a[a-z]+ a ile başlayan küçük harfli kelimeler (a hariç)
? seçimlik (var veya yok) XY?Z XZ veya XYZ
| veya (ya sol ya sağ taraf) a|b a veya b
xy bitiştirme (x ve y yanyana) abc ardarda a, b ve c (abc)
( ) gruplandırma (UNIX)|(Unix) UNIX veya Unix
" " operatörlerin anlamını iptal c"++" c++ kelimesi
\ 1. tek op.ün anlamını iptal c\+\+ c++ kelimesi
2. C escape karakterleri \n satır sonu karakteri
3. sekizlik düzende karşılığı \176 ~ karakteri (ASCII=176)
[ ] karakter sınıfı [A-Z] bir büyük harf
[^ ] karakter sınıfını hariç tut [^XYZ] X,Y ve Z dışında bir karakter
^ satır başında ifade ^(abc) satır başında abc
$ satır sonunda ifade $a satır sonunda a
{m,n} en az m, en çok n adet ifade a{1,5} 1 ila 5 adet a

 

Lex tanımları:

kelime düzgün-ifade çiftlerinden oluşur. Soldaki kelime, karşılığı olan sağdaki ifade yerine diğer ifadelerde kullanılabilir.Örnek:

tamsayı [0-9]+
%%
tamsayı          {sscanf(yytext, "%d", &yylval.ival);return TAMS;}
tamsayı\.tamsayı {sscanf(yytext, "%f", &yylval.fval);return REELS;}

Örnekte görüldüğü gibi işlemler { } içinde yeralan C ifadeleridir. yytext değişkeni karakter dizisi olup,tanınan kelimenin (örnekte 15, 6349 gibi bir tamsayı veya 0.359, 1368.4 gibi bir reel sayı) saklandığı alandır.Lex tarafından oluşturulan ve parser tarafından çağırılan yylex() tarayıcı fonksiyonu tanıdığı tokeni return ettiği değer ile parser'a iletir. Bunun dışında tokenin sıfatını da yylval global değişkeni ile iletir. { } içindeki ifadeler, C bloku içinde yeraldığından, burada lokal C değişkeni tanımlamak da mümkündür. yylval değişkeninin tipi, YACC'nin oluşturduğu parser içinde tanımlanmıştır.

YACC'nin kabul ettiği (.y uzantılı) dosya yapısı:


%{ -I
C ifadeleri I-> seçimlik
%} -I
YACC tanımları
%%
YACC üretim kuralları ve işlemler
%%                
C ifadeleri (kullanıcı fonksiyonları ve main() fonksiyonu)

YACC tanımları:

%union{ } yylval değişkeninin tipini C'deki union tipi yapar. Bu istenmiyorsa, %{ %} içine #define YYSTYPE tip şeklinde bir ifade konmalıdır. %union{ } içinde union'a ait sahalar yeralmalıdır.

%token uç semboller, %token sembol şeklinde tanımlanmalıdır. Eğer %union{ } kullanıldıysa, tokenin tipi, union içinde o tipteki sahanın adı t olmak üzere %token <t> sembol şeklinde belirtilmelidir.

%left ,%right işlemin sola veya sağa birleşim özelliği varsa %left işlem veya %right işlem şeklinde belirtilir. Bunlar,gramerdeki belirsizliği gidermek için kullanılır.

%nonassoc işlemin sağa veya sola birleşme özelliği olmadığını belirtir.

%type uç semboller için %union{ } kullanıldıysa yapılan tip tanımının ara semboller için benzeridir.

İşlemlerin öncelikleri %left, %right, veya %nonassoc ile tanımlandıkları satırın konumuyla orantılıdır.Tanımı aşağıda yeralan işlemin önceliği, daha yukarıda tanımlanmış olan işlemin önceliğinden üstündür.Tanımlarda alt satırlara inildikçe işlemlerin önceliği artar. Örnek:

%{
#define  YYSTYPE int /* yylval tipi ve YACC stack tipi tamsayı */
%}
%token   TAMSAYI
%left    '+' '-'
%left    '*' '/'
%right   '^'
%left EKSİ
%%
ifade    : ifade '+' ifade      {$$=$1+$3;}
         | ifade '-' ifade       {$$=$1-$3;}
         | ifade '*' ifade       {$$=$1*$3;}
         | ifade '/' ifade       {$$=$1/$3;}
         | ifade '^' ifade       {$$=power($1,$3);}
         | '-' ifade %prec EKSİ {$$=-$2;}
         | TAMSAYI               {$$=$1;}
         ;
%%
...

gramerinde 1+2*3*4^5 cümlesi aşağıdan yukarı doğru şu sırayla türetilir:


Türetme, aynen bu ağaç yapısının postorder taranma sırasını izler. Yani her dal için önce o dalın soldan başlayarak tüm alt dalları taranır, sonra da dalın kendisi taranır. Üretim kurallarının yanında yeralan işlemler, { } içerisinde C ifadeleridir (C bloku). Burada $n şeklindeki hayali değişkenler, sembollerin sıfatlarına karşılık gelirler. Soldaki ara sembolün sıfatı $0 veya $$ ile,sağdaki k.ıncı sembolün sıfatı da $k ile gösterilir. { } içerisinde, diğer işlemlerin yanısıra $1,$2,...,$n işlenerek elde edilen sonuç $$'a atanır. Bu değerler, aslında parser'ın kullandığı stack üzerinde sembollerle birlikte tutularak yukarı doğru işlenerek yolalır, yoksa gerçekte ağaç veri yapısı kullanılmaz. En üst kuralın solundaki sembol, ya da %start ile tanımlanmış başka bir sembol, cümlesel semboldür.Tüm semboller, uç sembollerden başlamak üzere,sağ tarafında yeraldıkları kuralın solundaki ara sembole, aşağıdan yukarı doğru türetilirler. Bu aşamalarda taşınan sıfatlar kullanılarak ve işlenerek yukarı aşamalara geçirilerek,sonunda cümlesel sembole ulaşıldığında derlenme süreci biter.Özel bir ara sembol olan error sembolü, derlenen programda karşılaşılan sözdizimi hatasının, sanki olmamış gibi error sembolüne türetilmesi ve hatalı kısmın atlanarak derlenmeye devam edilmesi için kullanılır.

Değişkenlerin depolanışı ve Fonksiyonların bağlantısı:


Pascal dili blok yapılı bir dil olduğundan, C,BASIC ve FORTRAN'dan farklı olarak içiçe bloklar, yani içiçe fonksiyonlar tanımlanabilir. Tanımlanan bir sembol, tanımlandığı blok ve bunun içindeki tüm alt bloklar içinden görülebilir, üst bloklardan görülemez. Depolanma yani yaşam süresi de, o bloğun ömrü kadardır.Fonksiyon ve prosedürler, rekürsif biçimde kendi kendilerini çağırabilirler.Geçici (lokal) değişkenler de tanımlanabilir. Bütün bunlar, en uygun şekilde stack üzerinde sağlanabilir.Programın çalışma süresince stack'in aldığı genel yapı şöyledir:

değişken N AG00091_1.gif (505 bytes) T
...    
değişken 1    
dönüş adresi    
dinamik link    
statik link AG00091_1.gif (505 bytes) B
parametre M    
...    
parametre 1    
dönüş değeri    


Burada B ve T, P-makinesi adı verilen ve derlenmiş kodun çalışacağı sanal makinenin iki yazmacıdır. T, stack'in tepesini gösteren, B ise stack'i adresleyen pointerdır. Bütün adresler, B'nin tuttuğu base adrese pozitif veya negatif bir ofset eklenerek belirtilir. Parametre 1,...,M o an işleyen fonksiyonun parametreleri için, değişken1,...,N fonksiyonun içinde tanımlanmış olan lokal değişkenler için, dönüş değeri ise fonksiyonun dönüş değeri için ayrılmış alanı gösterir. Statik link, fonksiyonu (bloku) içeren üst blok fonksiyonlarındaki değişkenlere, bu fonksiyon tarafınfan erişilebilmesi için konmuş olan, bir üst bloğun base adresidir. Dinamik link ise, bu fonksiyonun işleyişi sona erdiğinde çağrıldığı noktaya geri döndüğünde, o fonksiyonun değişkenleri ve diğer stack yapısına tekrar dönmesini sağlayan, çağıran fonksiyonun base adresidir. Dönüş adresi ise, fonksiyonun geri döndüğü zaman çalıştırılacak komutun adresidir. P-makinesi, bütün komutları stack uzerinde işlem yapan bir sanal makinedir. Komutlarına P-kodu denir.

Sembol tablosu:

Bir blokta tanımlanmış semboller yalnızca bu blok ve bu bloğun içindeki bloklar içerisinden görülebilir. Yukarıdaki stack yapısı sayesinde program işlerken bu sağlanır. Derlenme aşamasında bunun sağlanması ve aynı isimli,ayrı bloklarda yeralan sembollerin birbiriyle karıştırılmaması için derleyicide kullanılan sembol tablosu da stack yapısındadır. Örneğin, A bloğu içinde B ve C blokları yeralıyorsa sembol stack'inin durumu şöyledir:

A bloku   B bloku   C bloku  
derlenirken   derlenirken   derlenirken  
           
    B'nin sembolleri   C'nin sembolleri
A'nın sembolleri   A'nın sembolleri   A'nın sembolleri

 

Genel olarak P-kodlar:

 

p-kod opkod anlamı
LIT 0,N 00 N değerini stack'e yükle
OPR 0,N 01 stack üstünde aritmetik N işlemi yap
LOD L,N 02 değişkeni stack'e yükle
LODX L,N 12 dizi değişkeni stack'e yükle
STO L,N 03 değişkeni stack'ten yükle
STOX L,N 13 dizi değişkeni stack'ten yükle
CRL L,N 04 prosedür veya fonksiyon çağır
INT 0,N 05 T'ye poz. veya neg. sabit ekle
JMP 0,N 06 adrese dallan
JPC C,N 07 adrese koşullu dallan
CSP 0,N 08 standart prosedür çağır

 

Not: opkod onaltılık sayı düzenindedir.

P-kod detayları:

 

p-kod yaptığı iş  
LIT 0,N N sabitini stack'e yükle PUSH N
OPR 0,0 prosedür veya fonksiyondan geri dön  
OPR 0,1 negatifini al POP A, PUSH (-A)
OPR 0,2 toplamını al POP A, POP B, PUSH (B+A)
OPR 0,3 farkını al POP A, POP B, PUSH (B-A)
OPR 0,4 çarpımını al POP A, POP B, PUSH (B*A)
OPR 0,5 bölümünü al POP A, POP B, PUSH (B/A)
OPR 0,6 alt bitini al POP A, PUSH (A AND 1)
OPR 0,7 kalanını al POP A, POP B, PUSH (B MOD A)
OPR 0,8 eşit mi POP A, POP B, PUSH (B =? A)
OPR 0,9 farklı mı POP A, POP B, PUSH (B <>? A)
OPR 0,10 küçük mü POP A, POP B, PUSH (B <? A)
OPR 0,11 büyük eşit mi POP A, POP B, PUSH (B >=? A)
OPR 0,12 büyük mü POP A, POP B, PUSH (B >? A)
OPR 0,13 küçük eşit mi POP A, POP B, PUSH (B <=? A)
OPR 0,14 veya POP A, POP B, PUSH (B OR A)
OPR 0,15 ve POP A, POP B, PUSH (B AND A)
OPR 0,16 değil POP A, PUSH (NOT A)
OPR 0,17 sola shift POP A, POP B, PUSH (B sola shift (A kere))
OPR 0,18 sağa shift POP A, POP B, PUSH (B sağa shift (A kere))
OPR 0,19 arttır POP A, PUSH (A+1)
OPR 0,20 eksilt POP A, PUSH (A-1)
OPR 0,21 kopyala POP A, PUSH A, PUSH A
LOD L,D adresten yükle A'yi (L,D)'den yükle, PUSH A
LOD 255,0 stack üzerindeki adresten yükle POP A, POP adres, A'nin alt byte'ini adrese yükle
LODX L,D indexli yükle POP index, A'yi (L,D)+index'ten yükle, PUSH A
STO L,D stack üzerinden adrese yükle POP A, A'yi (L,D)'ye yükle
STO 255,0 stack üzerinden stack'teki adrese yükle POP A, POP adres, A'nin alt byte'ini adrese yükle
STOX L,D indexli yükle POP index, POP A, A'yi (L,D)+index'e yükle
CAL L,A çağır (L,A)'yı çağır
CAL 255,0 stack'teki adresi çağır POP adres, PUSH dönüş adresi (=pc), adrese dallan
INT 0,N T ye N ekle T=T+N
JMP 0,A dallan (0,A)'ya dallan
JPC 0,A doğruysa dallan POP A, eğer (A and 1) = 0 ise (0,A)'ya dallan
CSP 0,0 karakter girişi INPUT A, PUSH A
CSP 0,1 karakter çıkışı POP A, OUTPUT A
CSP 0,2 tamsayı girişi INPUT A#, PUSH A
CSP 0,3 tamsayı çıkışı POP A, OUTPUT A#
CSP 0,8 string çıkışı POP A, FOR I := 1 to A DO BEGIN POP B, OUTPUT B, END

Not: doğru=1, yanlış=0

POP X 'in anlamı: T'nin gösterdiği elemanı X'e yükle, T'yi 1 azalt.
PUSH X 'in anlamı: T'yi 1 arttır, X'i T'nin gösterdiği elemana yükle.

Temel Pascal yapıları için kod şablonları:

 

Pascal deyimi p-code karşılığı
x+10*y[5] LOD x
  LIT 10
LIT 5
LODX Y
OPR *
OPR +
   
a:=expr; (expr)
  STO a
   
p^=expr; (expr)
  STO 255 p
   
if expr then stmt1 else stmt2; (expr)
  JPC 0,1b1
(stmt1)
JMP 1b2
1b1: (stmt2)
1b2: ...
   
for i=expr1 to expr2 do stmt; (expr1)
  STO I
(expr2)
1b1: OPR CPY
LOD I
OPR >=
JPC 0,1b2
(stmt)
LOD I
OPR INC
STO I
JMP 1b1
1b2: INT -1
   
while expr do stmt 1b1: (expr)
  JPC 0,1b2
(stmt)
JMP 1b1
1b2: ...
   
case expr of (expr)
c1b1,c1b2: stmt1; OPR CPY
c1b3 : stmt2; LIT c1b1
else stmt3 end; OPR =
  JPC 1,1b1
OPR CPY
LIT c1b2
OPR =
JPC 0,1b2
1b1: (stmt1)
JMP 1b4
1b2: OPR CPY
LIT c1b3
OPR =
JPC 0,1b3
(stmt2)
JMP 1b4
1b3: (stmt3)
1b4: INT -1
   
repeat stmt until expr; 1b1: (stmt)
  (expr)
JPC 0,1b1
   
i=funca(expr1,expr2); INT 1
  (expr1)
(expr2)
CAL funca
INT -2

 

Derleyicide kullanılan veri yapıları:

Bağlı listeler, dinamik diziler oluşturmak için kullanılır. Listeyi oluşturacak her bir hucre için tek tek C'deki malloc() fonksiyonu ile dinamik bellek ayırılmalı ve bir sonraki hücreyle bağlantısı kurulmalıdır.

Tek bağlı liste:

l AG00088_.gif (337 bytes) d s AG00041_.gif (503 bytes) d s AG00084_.gif (503 bytes) ... AG00084_.gif (503 bytes) d s=0


Burada d'ler liste içinde sıralı sekilde tutulan bilgiler, s'ler de bir sonraki hücreyi gösteren pointer'lardır. İlk hücrenin adresi l'de tutulur. En son hücrede s'nin değeri NULL, yani 0'dir.C dilinde şu şekilde tanımlanır:

struct snode
    {
    int d;
    struct snode *s;    /* sonraki */
    } *l;

çift bağlı liste:

    ö=0 AG00091_.gif (505 bytes) ö AG00091_.gif (505 bytes) ... AG00091_.gif (505 bytes) ö    
l AG00088_.gif (337 bytes) d   d       d AG00091_.gif (505 bytes) son
    s AG00041_.gif (503 bytes) s AG00084_.gif (503 bytes) ... AG00084_.gif (503 bytes) s=0    

Burada d'ler liste içinde sıralı şekilde tutulan bilgiler, s'ler bir sonraki hücreyi gosteren, ö'ler ise bir önceki hücreyi gösteren pointer'lardır. İlk hücrenin adresi l'de,istenirse son hücrenin adresi son'da tutulur.En baş hücrede ö'nün değeri, en son hücrede s'nin değerleri NULL, yani 0'dir. C dilinde şu şekilde tanımlanır:

struct snode
    {
    int d;
struct snode *ö; /* önceki */
    struct snode *s;    /* sonraki */
    } *l, *son;

Dinamik Stack :

Çift bağlı liste, stack olarak kullanılırsa, son hücreyi gösteren
pointer da stack'in üstünü östermiş olur. Böylece büyüklüğü dinamik
olarak değişen bir stack yapısı elde edilir.

Derleyicide kullanılan bazı değişken ve fonksiyonlar:

symblocktop: sembol tablosundaki (stack'teki) en üst elemanı gösteren pointer'dır.
pushsymblock(): sembol stack'ine yeni sembol bloku yükler. (stack'i 1 blok büyültür)
popsymblock(): sembol stack'inden bir sembol bloku atar. (stack'i 1 blok küçültür.
instconst(), instlabel(), insttype(), instvar():
Sembol stack'inin üstündeki bloka sırasıyla yeni sabit, etiket, tip ve değişken ekler.
makeptrtype(), makeenumtype(), makerangetype(), makeidtype(), makerectype(), makearraytype(), makeuniontype(),
makesettype(), makefiletype(): Çeşitli tip hücreleri oluşturur. Değişkenlerin tipleri,sembol tablosunda birbirine bağlı tip hücreleri şeklinde tutulur. Tip hücresi şöyle tanımlıdır:

struct     stype
           {
           char typetype; /* TENUM, TID, TREC, TUNION, */
                          /* TFILE, TSET, TRANGE, TARR */
           void *restptr;
           };

Burada typetype TENUM, TID, TREC, TUNION, TARR, TFILE, TSET ve TRANGE sabitlerinden birini tutar. restptr, her tip için farklı türden bilgileri içeren bir yapıyı gösterir. Bu yapılar şöyledir:

typetype restptr
-------------------------------------------------------------------
TENUM -->id0-->id1-->...-->idN (bağlı liste, id'ler strig olup, ayrıca sembol tablosuna 0'dan N'ye kadar sabitler olarak kaydedilir)

TRANGE -->altsınır,üstsınır,tip (tip karakter veya nümeriktir)
Örnek: VAR r:(A..Z) altsınır=A, üstsınır=Z, tip=karakter

TID -->id (id: eşdeğer tipin ismi)
Örnek: TYPE sayıtipi = INTEGER; (INTEGER'in eşdegeri bir tip)
VAR i:sayıtipi id="sayıtipi"

TARRAY -->tip,inextipi1-->...-->indextipiN (bağlı liste,N elemanlı dizideki indislerin tipleri)

TREC ve TUNION -->fields1-->...-->fieldsN (bağlı liste, her bir fields da,aynı tipten sahaların oluşturduğu bir bağlı liste ve tip çiftidir)
Örnek: VAR u: UNION
f1,f2,f3: REAL; fields1->(f1,f2,f3,REAL)
i1,i2: INTEGER; fields2->(i1->i2,INTEGER)
END;

TPTR -->tip (pointerın gösterdiği tip)
Örnek: VAR p: ^ tip

TFILE -->tip Örnek: VAR f: FILE OF CHAR; (tip=CHAR)

TSET -->kümetipi Örnek: VAR s: SET OF INTEGER;
-------------------------------------------------------------------

instproc(),instfunc(): Sembol tablosunun üstündeki bloka prosedür ve fonksiyon ekler.

addXnode(): X'lerden oluşan bağlı listeye yeni hücre ekler.
Bu tür fonksiyonlar: addidnode(), addtypenode(), addfieldsnode(),addfparsec(), addexprnode(), addclinenode(),addconstnode(), addvarnode()

linkNcodes(): P-kodlardan oluşan N adet bağlı listeyi birleştirip, tek bir bağlı liste haline getirir.

codeXXX(): XXX P-kodunu üretir. Üretilen kod aslında veri kısmı P-kod olan tek elemanlı bir bağlı listedir. Kod üretimi sırasında değişik zamanlarda üretilen kod parçaları (bağlı listeler), linkNcodes() ile farklı aşamalarda birleştirilir ve en sonunda tek parça haline getirilir bu bağlı listenin adresi program isimli değişkene atanır.

Geyiğimiz bu kadar, PROGRAM DOSYALARI - ÇOK YAKINDA !!!