Lexical Analiz 3
Bugünkü yazılar biraz spam gibi oldu kusura bakmayın, ama lexical analiz 2'deki lexer'a yapılabileceğinden bahsettiğim birkaç eklemeyi de göstereyim dedim.
Satır Sayma
Satır sayma konusu çok kolay, _lexInitial
ve _lexString
yeni satırla karşılaştıklarında, bir değişkeni artıracak.
Ayrıca, gönderdiğimiz tokenlere satır bilgisi de ekleyeceğiz.
_lexIndentation
biraz spoiler oldu, ama onun dışında açıklayacak birşey yok.
İşleçler
İşleçlerle karşılaşacak tek state, _lexInitial
. Bunda yapacağımız bir değişiklik işimizi görecek.
|
class Lexer(Thread):
|
|
operators = "+-*/^=~:"
|
|
def _lexInitial(self):
|
|
"Starting State"
|
|
while True:
|
|
# Yukarılar aynı
|
|
elif current in self.operators:
|
|
self._pos += 1
|
|
self._emit(current)
|
Keywordler
Keyword dediğimiz şey, aslında önceden bizim belirlediğimiz bir name. _lexName
düzenlenerek keyword'lerimizi
token olarak belirtebiliriz.
Indentation
Geldik zurnanın zırt dediği yere! Önce kodları görelim:
İlk bakışta göze korkunç geliyor ama çok birşeyi yok aslında. Öncelikle, __init__
içerisinde indentlevels
adında bir liste
tanımladık. Bunu yapmamızın nedeni, önceki girintileme seviyelerini akılda tutmak. Bunu yapmazsak, doğru sayıya çıkıntılama (girintilemenin tersi :) )
yapamayız. Bir örnekle inceleyelim:
yasar arabacı osman hebele yine yasar
Eğer lexer'ımız yukarıdaki yazıyı okuyorsa, son satıra geldiği zaman iki kere "DEDENT" tokeni vermeli. Aksi halde parser sıkıntıya girer. Evet, henüz bir parser yok ortada, ama olunca bu işimize yarayacak.
_lexIndentation
'a gelen tek state _lexInitial
. _lexInitial
yeni bir satır gördüğünde işi indentation state'ine paslayacak.
Bu state'in ilk işi o satırdaki girinti seviyesini hesaplamak. Bunu yapmak için, boşluk, tab veya yeni satır haricinde ilk karakteri görene kadar sayıyor. Her boşluk 1, tab 4 girintileme değerine sahip. Yeni satır ise, girintileme değerini sıfırlıyor. Böylece, bir girintileme seviyesi içindeki boş satırları da hesaba katmış oluyoruz.
Bu state o satırdaki girintileme seviyesini belirlediği zaman, bir önceki girintileme seviyesi ile karşılaştırma yapıyor. Eğer
şu anki girintileme seviyesi, öncekinden büyükse, bir INDENT tokeni verip, kontrolü _lexInitial
state'ine veriyor.
Eğer o anki girintileme, öncekinden daha küçükse, yani bir çıkıntılama (:)) tespit edilmişse, bunu daha önceki seviyelerle karşılaştırıyor. Eğer daha önceki seviyelerden biriyle eşleşiyorsa, o seviyeye dönene kadar DEDENT tokeni veriyor. Eğer bir eşleşme yoksa, Exception veriyor. Tıpkı Python dilinin yaptığı gibi.
Eğer o anki girintileme, önceki ile aynıysa, kontrolü _lexInitial
devralıyor, ve işlem devam ediyor.
Düzeltilmesi gereken bir şey daha var aslında. Okunacak girdi bittiği zaman, girintilemeyi sıfırlamak gerek.
Burada olup biten de oldukça açık. Son olarak, lexer'ımızın son halini bir test edelim.
Çıktısı:
if(line 1) if NAME(line 1) myname =(line 1) = =(line 1) = STRING(line 1) "yasar" :(line 1) : INDENT(line 2) NUMBER(line 2) 12 +(line 2) + NUMBER(line 2) 12 =(line 2) = NUMBER(line 2) 28 INDENT(line 3) STRING(line 3) "Another indent babe!" DEDENT(line 4) DEDENT(line 4) STRING(line 4) "Lets dedent by two!" INDENT(line 5) NAME(line 5) What NAME(line 5) happens NAME(line 5) when NAME(line 5) we NAME(line 5) finish NAME(line 5) in NAME(line 5) indented NAME(line 5) block DEDENT(line 5)
Artık bir parser'a girişmenin vakti geldi gibi. Parser işine girip girmeyeceğimden emin değilim. Ama buraya kadar okuduysanız bile, lexer konusunda sizin için eğitsel bir deneyim olmuştur diye ümit ediyorum.