Parser yazılarını takip ettiyseniz, kaynak kodları parse tree'e çevirebilen bir parser yazmıştık. Bu yazıda, onu biraz daha
geliştirip, parse tree'den kod üretmesini sağlayacağız. Kodlar yine aynı yerde.
Daha önce bahsettiğim gibi, parser tree'i birçok farklı şekilde kullanabilirsiniz. Ben kendi parse tree'min kodları C benzeri bir
dile çevirmesini istiyorum.
Bunu yapmak için, parse tree'deki her eleman için, onun nasıl yazdırılacağını gösteren bir fonksiyon atayacağım. Örneğin,
bir if statement'ı için:
|
def ifWriter(self):
|
|
|
|
begin = "if (%s)%s" % (self.first.write(), self.second.write())
|
|
|
|
if not self.third:
|
|
return begin
|
|
|
|
return "%s%selse %s" % (begin, self.parent.outputindentlevel * " ",self.third.write())
|
gibi bir fonksiyon aracılığıyla, bir if statement'ı yazdırabileceğiz. Bunu, if sembolüne metot olarak ekliyorum.
|
statement("if", ifStatement, ifWriter)
|
statement
yardımcı fonksiyonunu şu şekilde düzenledim:
|
def statement(id, std, writer=None):
|
|
self._symbol(id).beginStatement = True
|
|
self._symbol(id).std = std
|
|
if writer:
|
|
self._symbol(id).write = writer
|
Böylece, gerekli write()
metodunu, if sembolüne eklemiş oluyoruz. Bunu, parse tree'de olabilecek her sembol için yapmalıyım.
Birçok sembol için, sadece yardımcı fonksiyonu düzenlemem yeterli olacak.
|
def infix(id, bp):
|
|
def led(self, left):
|
|
self.first = left
|
|
self.second = self.parent.Expression(bp)
|
|
return self
|
|
self._symbol(id, bp).led = led
|
|
self._symbol(id).write = lambda self: self.first.write() + id + self.second.write()
|
|
|
|
def infixr(id, bp):
|
|
def led(self, left):
|
|
self.first = left
|
|
self.second = self.parent.Expression(bp-1)
|
|
return self
|
|
self._symbol(id,bp).led = led
|
|
self._symbol(id).write = lambda self: self.first.write() + id + self.second.write()
|
|
|
|
def literal(id):
|
|
self._symbol(id).nud = lambda self: self
|
|
self._symbol(id).write = lambda self: str(self.value)
|
while
, print
gibi statement'lar için özel fonksiyonlar yazılması gerekiyor. Github deposundaki kodlarda bunlar var, oradan
bakabilirsiniz.
Halletmem gereken birşey daha var. Bir statement listesi, expression statement ve bir kod bloğu, parse tree'de sembol olarak
bulunmuyor. Bu sebeple, onlar için wrapper sınıflar yazıp, onları parse tree'e ekleyeceğim:
|
class BlockWrapper(object):
|
|
def __init__(self, stmts, parent):
|
|
self.stmts = stmts
|
|
self.parent = parent
|
|
def write(self):
|
|
self.parent.outputindentlevel += 1
|
|
inner = self.stmts.write()
|
|
self.parent.outputindentlevel -= 1
|
|
return "{\n%s}\n" % inner
|
|
|
|
class ExpressionStatementWrapper(object):
|
|
|
|
def __init__(self, expr):
|
|
self.expr = expr
|
|
|
|
def write(self):
|
|
return self.expr.write() + ";"
|
|
|
|
|
|
class StatementsWrapper(object):
|
|
def __init__(self, stmts, parent):
|
|
self.stmts = stmts
|
|
self.parent = parent
|
|
|
|
def write(self):
|
|
lvl = self.parent.outputindentlevel
|
|
return "\n".join([" " * lvl + x.write() for x in self.stmts])
|
Son olarak, gerekli yerlerde, bu wrapper sınıfların objelerini parse tree'e eklemem gerekiyor. Statements
ve Block
metotlarını
düzenleyeceğim.
|
def Statement(self):
|
|
t = self.token
|
|
if t.beginStatement:
|
|
self._advance()
|
|
return t.std()
|
|
ex = self.Expression(0)
|
|
self._advance(["NEWLINE","END","DEDENT"])
|
|
return ExpressionStatementWrapper(ex)
|
|
|
|
def Statements(self):
|
|
statements = []
|
|
while True:
|
|
if self.token.id in ["END","DEDENT"]:
|
|
break
|
|
s = self.Statement()
|
|
if s:
|
|
statements.append(s)
|
|
return StatementsWrapper(statements, self)
|
|
|
|
def Block(self):
|
|
self._advance(["INDENT"])
|
|
stmts = self.Statements()
|
|
self._advance(["DEDENT"])
|
|
return BlockWrapper(stmts, self)
|
Parser
sınıfına da parse
ve output
isminde iki yeni metot ekledim:
|
def parse(self):
|
|
self.stmts = self.Statements()
|
|
|
|
def output(self):
|
|
return self.stmts.write()
|
Artık kodları deneyebiliriz:
|
if __name__ == "__main__":
|
|
tokenq = Queue()
|
|
|
|
with open("test.txt") as dosya:
|
|
myinput = dosya.read()
|
|
|
|
mylexer = Lexer(myinput, tokenq)
|
|
mylexer.start()
|
|
|
|
myparser = Parser(tokenq)
|
|
|
|
myparser.parse()
|
|
print myparser.output()
|
Örnek test.txt
|
while True:
|
|
if a > 12:
|
|
break
|
|
if a < 3:
|
|
continue
|
|
else:
|
|
print a
|
|
|
|
osman = 12
|
|
veli = 29 * 12
|
Çıktı:
|
while (True) {
|
|
if (a>12){
|
|
break;}
|
|
|
|
if (a<3){
|
|
continue;}
|
|
else {
|
|
print(a);}
|
|
}
|
|
|
|
osman=12;
|
|
veli=29*12;
|
Evet, gördüğünüz gibi, temelden girip, Python benzeri bir dili, C benzeri bir dile dönüştürebilen bir parser yapmış olduk. Artık burdan
sonrası da size kalmış.