Herkese selamlar TurkHackTeam ailesi,
Bu konumuzda, önceki konuda yüzeysel olarak girdiğim ama bu konuda biraz daha detaya gireceğim "Python ile Programlama Dili Yapma" serisinin ikinci kısmını anlatacağım. Bu konuda;
1. Kullanıcıdan gelen verileri anlamlı parçalara yani token adı verilen birimlere ayıracağız,
2. Bu ayırılan tokenleri sözdizimi çözücü yani parser ile anlamlı bir yapıya çevireceğiz,
3. Sonra bu yapıyı bizim anlayacağımız şekilde yorumlayan yani ınterpreter ekleyeceğiz.
Şimdiden iyi okumalar
LEXER(Sözcük Ayrıştırıcı) OLUŞTURMA
Buradaki ilk adımımız kullanıcıdan gelen veriyi (örneğin 5 + 3) küçük anlamlı parçalara yani token adı verilen birimlere ayırma işlemini yapacağız. Bunlar sayılar, semboller ve anahtarlar olabilir. Şimdi ilk önce VS kodu açıp yeni bir dosya oluşturdum:

Sonrasında içine şu kodları yazıyoruz:
Programı çalıştırdığımızda ise:

Şimdi ilk adımı tamamlamış olduk. Şimdi sırada ikinci adım var.
PARSER (Sözdizimi Çözücü)
Lexer bize çıktıda sadece token vermişti. Ama biz bu tokeni anlamlı bir yapıya çevireceğiz. Çünkü tek başına bu pek bir anlam ifade etmiyor. Yani şu ifadeyi:
Şuna benzer bir yapıya çevireceğiz:
Bu konumuzda, önceki konuda yüzeysel olarak girdiğim ama bu konuda biraz daha detaya gireceğim "Python ile Programlama Dili Yapma" serisinin ikinci kısmını anlatacağım. Bu konuda;
1. Kullanıcıdan gelen verileri anlamlı parçalara yani token adı verilen birimlere ayıracağız,
2. Bu ayırılan tokenleri sözdizimi çözücü yani parser ile anlamlı bir yapıya çevireceğiz,
3. Sonra bu yapıyı bizim anlayacağımız şekilde yorumlayan yani ınterpreter ekleyeceğiz.
Şimdiden iyi okumalar
LEXER(Sözcük Ayrıştırıcı) OLUŞTURMA
Buradaki ilk adımımız kullanıcıdan gelen veriyi (örneğin 5 + 3) küçük anlamlı parçalara yani token adı verilen birimlere ayırma işlemini yapacağız. Bunlar sayılar, semboller ve anahtarlar olabilir. Şimdi ilk önce VS kodu açıp yeni bir dosya oluşturdum:

Sonrasında içine şu kodları yazıyoruz:
Python:
# Token sınıfını oluşturuyoruz
class Token:
def __init__(self, type_, value=None):
self.type = type_
self.value = value
def __repr__(self):
if self.value is not None:
return f"{self.type}:{self.value}"
return f"{self.type}"
# Lexer sınıfını oluşturuyoruz
class Lexer:
def __init__(self, text):
self.text = text
self.pos = 0
self.current_char = self.text[self.pos] if self.text else None
def advance(self):
"""Bir sonraki karaktere geç."""
self.pos += 1
self.current_char = self.text[self.pos] if self.pos < len(self.text) else None
def generate_tokens(self):
tokens = []
while self.current_char is not None:
if self.current_char.isspace():
self.advance()
elif self.current_char.isdigit():
tokens.append(self.make_number())
elif self.current_char == '+':
tokens.append(Token("PLUS"))
self.advance()
elif self.current_char == '-':
tokens.append(Token("MINUS"))
self.advance()
elif self.current_char == '*':
tokens.append(Token("MUL"))
self.advance()
elif self.current_char == '/':
tokens.append(Token("DIV"))
self.advance()
else:
raise Exception(f"Bilinmeyen karakter: {self.current_char}")
return tokens
def make_number(self):
"""Sayıları birleştir (örneğin 123 gibi)."""
num_str = ""
while self.current_char is not None and self.current_char.isdigit():
num_str += self.current_char
self.advance()
return Token("NUMBER", int(num_str))
# test edelim
if __name__ == "__main__":
text = "12 + 24 - 3 * 2"
lexer = Lexer(text)
tokens = lexer.generate_tokens()
print(tokens)
Programı çalıştırdığımızda ise:

Şimdi ilk adımı tamamlamış olduk. Şimdi sırada ikinci adım var.
PARSER (Sözdizimi Çözücü)
Lexer bize çıktıda sadece token vermişti. Ama biz bu tokeni anlamlı bir yapıya çevireceğiz. Çünkü tek başına bu pek bir anlam ifade etmiyor. Yani şu ifadeyi:
Kod:
12 + 24 - 3 * 2
Şuna benzer bir yapıya çevireceğiz:
Kod:
(-)[/COLOR][/CENTER]
[COLOR=rgb(255, 255, 255)][CENTER] / \
(+) (*)
/ \ / \
12 24 3 2
Şimdi ilk önce yeni bir dosya oluşturuyorum:

Şimdi bu dosyaya şu kodları yazıyorum:
Python:
from lexer import Token # kendi oluşturduğumuz dosyayı import ettik
# AST düğüm sınıfları
class NumberNode:
def __init__(self, value):
self.value = value
def __repr__(self):
return f"{self.value}"
class BinOpNode:
def __init__(self, left, op, right):
self.left = left
self.op = op
self.right = right
def __repr__(self):
return f"({self.left} {self.op.type} {self.right})"
# Parser sınıfı
class Parser:
def __init__(self, tokens):
self.tokens = tokens
self.pos = 0
self.current_token = self.tokens[self.pos] if self.tokens else None
def advance(self):
self.pos += 1
if self.pos < len(self.tokens):
self.current_token = self.tokens[self.pos]
else:
self.current_token = None
# Parser giriş noktası
def parse(self):
return self.expr()
# expr → term ((PLUS | MINUS) term)*
def expr(self):
node = self.term()
while self.current_token is not None and self.current_token.type in ("PLUS", "MINUS"):
op = self.current_token
self.advance()
right = self.term()
node = BinOpNode(node, op, right)
return node
# term → factor ((MUL | DIV) factor)*
def term(self):
node = self.factor()
while self.current_token is not None and self.current_token.type in ("MUL", "DIV"):
op = self.current_token
self.advance()
right = self.factor()
node = BinOpNode(node, op, right)
return node
# factor → NUMBER
def factor(self):
token = self.current_token
if token.type == "NUMBER":
self.advance()
return NumberNode(token.value)
raise Exception(f"Beklenmeyen token: {token}")
Kodu yazdıktan sonra yeni bir test dosyası oluşturuyorum:

Sonra bu test dosyasına şu kodları yazıyorum:
Python:
from lexer import Lexer # kendi yazdığımız dosyaları import ettik
from parser import Parser
text = "12 + 24 - 3 * 2"
lexer = Lexer(text)
tokens = lexer.generate_tokens()
parser = Parser(tokens)
tree = parser.parse()
print(tree)
test_parser.py kodunu çalıştırdığımda ise şu çıktıyı alıyorum:

Burada ifadeyi artık doğru şekilde ayrıştırabiliyoruz. Şimdi son ve diğer adımımıza geçelim.
INTERPRETER (Yorumlayıcı)
Şimdi biz anlamlı hale getirdik ama, bu işlemi gerçek bir matematik sonucuna çevireceğiz ki kullanıcı sonucu daha mantıklı sonuçlar ile görebilsin. Şimdi yeni bir dosya oluşturuyorum:

Bu oluşturduğum dosyaya şu kodları yazıyorum:
Python:
from parser import NumberNode, BinOpNode # parser dosyasındaki bazı sınıfları import ediyoruz
class Interpreter:
def visit(self, node):
if isinstance(node, NumberNode):
return self.visit_NumberNode(node)
elif isinstance(node, BinOpNode):
return self.visit_BinOpNode(node)
else:
raise Exception(f"Bilinmeyen düğüm tipi: {type(node)}")
def visit_NumberNode(self, node):
return node.value
def visit_BinOpNode(self, node):
left_val = self.visit(node.left)
right_val = self.visit(node.right)
if node.op.type == "PLUS":
return left_val + right_val
elif node.op.type == "MINUS":
return left_val - right_val
elif node.op.type == "MUL":
return left_val * right_val
elif node.op.type == "DIV":
return left_val / right_val
Daha sonra yeni bir dosya oluşturuyorum:

Daha sonra bu dosyama şu kodları yazıyorum:
Python:
from lexer import Lexer
from parser import Parser
from interpreter import Interpreter # tüm dosyalarımızı import ettik
while True:
text = input(">>> ")
if text.strip() == "":
continue
lexer = Lexer(text)
tokens = lexer.generate_tokens()
parser = Parser(tokens)
tree = parser.parse()
interpreter = Interpreter()
result = interpreter.visit(tree)
print(result)
Kodu çalıştırıyorum ve:

Şimdiden kendi işlemlerini yapan bir dil oluşturduk bile. İleriki konularda daha da geliştirmeye devam edeceğiz. Bir sonraki konuda görüşmek üzere kendinize iyi bakın



