PDS-x BASIC yeni askim.
Pdsx benim yarattigim bir dil. Pythonda kodlamayi seviyorum ama indentation error hatalarindan biktim usandim artik. Hayir neyin nereden basladigini bile anlamakta zorlaniyorum. Kafamda bozuktu. Bende Neden Basic gibi bir Python olmasin ki dedim.
ben once bayagi kolaya kactim
bu yeni projem. pdsX interpreter versiyon 5
import re # regex her bir seyi ayirt etmeye gerekli
import random # rastgele sayi uretecem ya lazim olur
import sqlite3 # Basic ISAM varya onu sqlite ile taklit edecegim
import numpy as np # Fonksiyonlarini taklit edecegim, veri yapilarini calacagim
import pandas as pd # Fonksiyonlarini taklit edecegim, veri yapilarini calacagim
import scipy.stats as stats # Fonksiyonlarini taklit edecegim, veri yapilarini calacagim, istatistik kolayca yaparim
from collections import defaultdict, namedtuple
from types import SimpleNamespace
import os #her python dosya islemlerinin olmazsa olmazi
import logging # loglamak bu kadar kolay olmasaydi koymazdim. Hata bulmada cok yararli.
import time # interpreterin zaman isleri icin
import sys # Gerekli bu olmadan python ne ki?
import argparse # Bu da gereklilik. Kod satirindan komut calistirmanin anahtari
import struct
# Hata loglama için logging ayarı
logging.basicConfig(filename='interpreter_errors.log', level=logging.ERROR, format='%(asctime)s - %(message)s')
class pdsXInterpreter:
def __init__(self):
self.global_vars = {} # GLOBAL değişkenler
self.shared_vars = defaultdict(list) # DIM SHARED değişkenler
self.local_scopes = [{}] # Yerel kapsam yığını
self.types = {} # TYPE tanımları
self.classes = {} # CLASS tanımları
self.functions = {} # FUNCTION tanımları
self.subs = {} # SUB tanımları
self.labels = {} # Etiketler
self.program = [] # Program satırları
self.program_counter = 0
self.call_stack = [] # GOSUB, SUB, FUNCTION için yığın
self.running = False
self.db_connections = {} # Veritabanı bağlantıları
self.file_handles = {} # Dosya kolları
self.error_handler = None # ON ERROR GOTO için
self.debug_mode = False # DEBUG modu
self.loop_stack = [] # DO...LOOP, FOR...NEXT için yığın
self.select_stack = [] # SELECT CASE için yığın
self.if_stack = [] # IF...THEN...ELSE için yığın
self.data_list = [] # DATA komutları için liste
self.data_pointer = 0 # READ komutu için işaretçi
self.transaction_active = {} # Transaction durumları
self.type_table = { # Veri tipleri tablosu
"STRING": str,
"INTEGER": int,
"LONG": int,
"SINGLE": float,
"DOUBLE": float,
"BYTE": int,
"SHORT": int,
"UNSIGNED INTEGER": int,
"CHAR": str,
"LIST": list,
"DICT": dict,
"SET": set,
"TUPLE": tuple,
"ARRAY": np.array,
"DATAFRAME": pd.DataFrame,
"POINTER": None, # Bellek simülasyonu
"STRUCT": dict, # Basit struct simülasyonu
"UNION": None, # Tek değer depolama
"ENUM": dict,
"VOID": None,
"BITFIELD": int
}
self.function_table = {
"MID$": lambda s, start, length: s[start-1:start-1+length],
"LEN": len,
"RND": random.random,
"ABS": abs,
"INT": int,
"LEFT$": lambda s, n: s[:n],
"RIGHT$": lambda s, n: s[-n:],
"LTRIM$": lambda s: s.lstrip(),
"RTRIM$": lambda s: s.rstrip(),
"STRING$": lambda n, c: c * n,
"SPACE$": lambda n: " " * n,
"INSTR": lambda start, s, sub: s.find(sub, start-1) + 1,
"UCASE$": lambda s: s.upper(),
"LCASE$": lambda s: s.lower(),
"STR$": lambda n: str(n),
"SQR": np.sqrt,
"SIN": np.sin,
"COS": np.cos,
"TAN": np.tan,
"LOG": np.log,
"EXP": np.exp,
"ATN": np.arctan,
"FIX": lambda x: int(x),
"ROUND": lambda x, n=0: round(x, n),
"SGN": lambda x: -1 if x < 0 else (1 if x > 0 else 0),
"MOD": lambda x, y: x % y,
"MIN": lambda *args: min(args),
"MAX": lambda *args: max(args),
"TIMER": lambda: time.time(),
"DATE$": lambda: time.strftime("%m-%d-%Y"),
"TIME$": lambda: time.strftime("%H:%M:%S"),
"INKEY$": lambda: input()[:1],
"ENVIRON$": lambda var: os.environ.get(var, ""),
"COMMAND$": lambda: " ".join(sys.argv[1:]),
"CSRLIN": lambda: 1, # Basit simülasyon
"POS": lambda x: 1, # Basit simülasyon
"VAL": lambda s: float(s) if s.replace(".", "").isdigit() else 0,
"ASC": lambda c: ord(c[0]),
"MEAN": np.mean,
"MEDIAN": np.median,
"MODE": lambda x: stats.mode(x)[0][0],
"STD": np.std,
"VAR": np.var,
"SUM": np.sum,
"PROD": np.prod,
"PERCENTILE": np.percentile,
"QUANTILE": np.quantile,
"CORR": lambda x, y: np.corrcoef(x, y)[0, 1],
"COV": np.cov,
"DESCRIBE": lambda df: df.describe(),
"GROUPBY": lambda df, col: df.groupby(col),
"FILTER": lambda df, cond: df.query(cond),
"SORT": lambda df, col: df.sort_values(col),
"HEAD": lambda df, n=5: df.head(n),
"TAIL": lambda df, n=5: df.tail(n),
"MERGE": lambda df1, df2, on: pd.merge(df1, df2, on=on),
"TTEST": lambda sample1, sample2: stats.ttest_ind(sample1, sample2),
"CHISQUARE": lambda observed: stats.chisquare(observed),
"ANOVA": lambda *groups: stats.f_oneway(*groups),
"REGRESS": lambda x, y: stats.linregress(x, y),
"EOF": lambda n: self.file_handles[n].eof(),
"LOC": lambda n: self.file_handles[n].tell(),
"LOF": lambda n: os.path.getsize(self.file_handles[n].name),
"FREEFILE": lambda: min(set(range(1, 100)) - set(self.file_handles.keys())),
"CHR$": lambda n: chr(n),
"INPUT$": lambda n, f: self.file_handles[f].read(n),
"MKI$": lambda n: struct.pack("i", n).decode('latin1'),
"MKS$": lambda n: struct.pack("f", n).decode('latin1'),
"MKD$": lambda n: struct.pack("d", n).decode('latin1')
}
def current_scope(self):
return self.local_scopes[-1]
def parse_program(self, code):
self.program = []
current_sub = None
current_function = None
current_type = None
current_class = None
type_fields = {}
class_methods = {}
lines = code.split("\n")
i = 0
while i < len(lines):
line = lines[i].strip()
if not line:
i += 1
continue
line_upper = line.upper()
if line_upper.startswith("SUB "):
sub_name = line[4:].split("(")[0].strip()
self.subs[sub_name] = i + 1
current_sub = sub_name
i += 1
elif line_upper.startswith("FUNCTION "):
func_name = line[8:].split("(")[0].strip()
self.functions[func_name] = i + 1
current_function = func_name
i += 1
elif line_upper.startswith("TYPE "):
type_name = line[5:].strip()
current_type = type_name
type_fields[type_name] = []
i += 1
elif line_upper.startswith("END TYPE"):
self.types[current_type] = namedtuple(current_type, [f[0] for f in type_fields[current_type]])
current_type = None
i += 1
elif current_type:
match = re.match(r"(\w+)\s+AS\s+(\w+)", line, re.IGNORECASE)
if match:
field_name, field_type = match.groups()
type_fields[current_type].append((field_name, field_type))
else:
raise Exception(f"TYPE tanımı hatası: {line}")
i += 1
elif line_upper.startswith("CLASS "):
class_name = line[6:].strip()
current_class = class_name
class_methods[class_name] = {}
i += 1
elif line_upper.startswith("END CLASS"):
class_def = type(current_class, (), {
**class_methods[current_class],
'_vars': {},
'__init__': lambda self: None
})
self.classes[current_class] = class_def
current_class = None
i += 1
elif current_class and line_upper.startswith("SUB "):
method_name = line[4:].split("(")[0].strip()
class_methods[current_class][method_name] = lambda self, *args: self._vars.get(method_name, lambda: None)(*args)
i += 1
elif line_upper == "END SUB" or line_upper == "END FUNCTION":
current_sub = None
current_function = None
i += 1
elif line_upper.startswith("LABEL "):
label_name = line[6:].strip()
self.labels[label_name] = i
i += 1
elif line_upper.startswith("DATA "):
data_items = line[5:].split(",")
self.data_list.extend([item.strip() for item in data_items])
i += 1
else:
if current_sub or current_function:
self.program.append((line, current_sub or current_function))
else:
self.program.append((line, None))
i += 1
def evaluate_expression(self, expr, scope_name=None):
expr = expr.strip()
namespace = {}
namespace.update(self.global_vars)
namespace.update(self.current_scope())
for var in self.shared_vars:
if scope_name in self.shared_vars[var] or not self.shared_vars[var]:
namespace[var] = self.shared_vars[var]
namespace.update(self.function_table)
namespace["np"] = np
namespace["pd"] = pd
namespace["stats"] = stats
try:
return eval(expr, namespace)
except Exception as e:
raise Exception(f"İfade değerlendirme hatası: {expr}, Hata: {str(e)}")
def execute_command(self, command, scope_name=None):
command = command.strip()
if not command:
return None
command_upper = command.upper()
try:
# Hata Yönetimi
if command_upper.startswith("ON ERROR GOTO"):
match = re.match(r"ON ERROR GOTO\s+(\w+)", command, re.IGNORECASE)
if match:
label = match.group(1)
if label in self.labels:
self.error_handler = self.labels[label]
else:
raise Exception(f"Etiket bulunamadı: {label}")
return None
else:
raise Exception("ON ERROR GOTO komutunda sözdizimi hatası")
if command_upper == "RESUME":
if self.error_handler is not None:
return self.error_handler
else:
raise Exception("RESUME için hata işleyicisi tanımlı değil")
if command_upper == "DEBUG ON":
self.debug_mode = True
return None
if command_upper == "DEBUG OFF":
self.debug_mode = False
return None
# WHILE...WEND Desteği
if command_upper.startswith("WHILE"):
match = re.match(r"WHILE\s+(.+)", command, re.IGNORECASE)
if match:
condition = match.group(1)
self.loop_stack.append({
"start": self.program_counter,
"type": "WHILE",
"condition": condition
})
if not self.evaluate_expression(condition, scope_name):
while self.program_counter < len(self.program) and \
self.program[self.program_counter][0].upper() != "WEND":
self.program_counter += 1
return None
else:
raise Exception("WHILE komutunda sözdizimi hatası")
if command_upper == "WEND":
if self.loop_stack and self.loop_stack[-1]["type"] == "WHILE":
loop_info = self.loop_stack[-1]
if self.evaluate_expression(loop_info["condition"], scope_name):
return loop_info["start"]
else:
self.loop_stack.pop()
return None
else:
raise Exception("WEND için eşleşen WHILE bulunamadı")
# FOR...NEXT Desteği
if command_upper.startswith("FOR"):
match = re.match(r"FOR\s+(\w+)\s*=\s*(.+)\s+TO\s+(.+)(?:\s+STEP\s+(.+))?", command, re.IGNORECASE)
if match:
var_name, start_expr, end_expr, step_expr = match.groups()
start = self.evaluate_expression(start_expr, scope_name)
end = self.evaluate_expression(end_expr, scope_name)
step = self.evaluate_expression(step_expr, scope_name) if step_expr else 1
if var_name in self.global_vars:
self.global_vars[var_name] = start
else:
self.current_scope()[var_name] = start
self.loop_stack.append({
"start": self.program_counter,
"type": "FOR",
"var": var_name,
"end": end,
"step": step
})
return None
else:
raise Exception("FOR komutunda sözdizimi hatası")
if command_upper.startswith("NEXT"):
if self.loop_stack and self.loop_stack[-1]["type"] == "FOR":
loop_info = self.loop_stack[-1]
var_name = loop_info["var"]
current_value = self.global_vars.get(var_name, self.current_scope().get(var_name))
current_value += loop_info["step"]
if var_name in self.global_vars:
self.global_vars[var_name] = current_value
else:
self.current_scope()[var_name] = current_value
if (loop_info["step"] > 0 and current_value <= loop_info["end"]) or \
(loop_info["step"] < 0 and current_value >= loop_info["end"]):
return loop_info["start"]
else:
self.loop_stack.pop()
return None
else:
raise Exception("NEXT için eşleşen FOR bulunamadı")
# DO...LOOP Desteği
if command_upper.startswith("DO"):
match = re.match(r"DO\s+(WHILE|UNTIL)?\s*(.+)?", command, re.IGNORECASE)
if match:
loop_type, condition = match.groups()
self.loop_stack.append({
"start": self.program_counter,
"type": loop_type or "NONE",
"condition": condition or "True"
})
return None
else:
raise Exception("DO komutunda sözdizimi hatası")
if command_upper.startswith("LOOP"):
match = re.match(r"LOOP\s+(WHILE|UNTIL)?\s*(.+)?", command, re.IGNORECASE)
if match and self.loop_stack:
loop_type, condition = match.groups()
loop_info = self.loop_stack[-1]
if loop_type and condition:
cond_result = self.evaluate_expression(condition, scope_name)
if (loop_type == "WHILE" and cond_result) or (loop_type == "UNTIL" and not cond_result):
return loop_info["start"]
else:
self.loop_stack.pop()
elif loop_info["type"] == "WHILE":
if self.evaluate_expression(loop_info["condition"], scope_name):
return loop_info["start"]
else:
self.loop_stack.pop()
elif loop_info["type"] == "UNTIL":
if not self.evaluate_expression(loop_info["condition"], scope_name):
return loop_info["start"]
else:
self.loop_stack.pop()
else:
return loop_info["start"]
return None
else:
raise Exception("LOOP için eşleşen DO bulunamadı")
# SELECT CASE Desteği
if command_upper.startswith("SELECT CASE"):
match = re.match(r"SELECT CASE\s+(.+)", command, re.IGNORECASE)
if match:
expr = match.group(1)
value = self.evaluate_expression(expr, scope_name)
self.select_stack.append({"value": value, "matched": False, "start": self.program_counter})
return None
else:
raise Exception("SELECT CASE komutunda sözdizimi hatası")
if command_upper.startswith("CASE"):
if not self.select_stack:
raise Exception("CASE için eşleşen SELECT CASE bulunamadı")
select_info = self.select_stack[-1]
match = re.match(r"CASE\s+(.+)", command, re.IGNORECASE)
if match:
case_expr = match.group(1)
if case_expr.upper() == "ELSE":
if not select_info["matched"]:
select_info["matched"] = True
else:
while self.program_counter < len(self.program) and \
self.program[self.program_counter][0].upper() != "END SELECT":
self.program_counter += 1
else:
case_value = self.evaluate_expression(case_expr, scope_name)
if select_info["value"] == case_value and not select_info["matched"]:
select_info["matched"] = True
else:
while self.program_counter < len(self.program) and \
self.program[self.program_counter][0].upper() not in ("CASE", "END SELECT"):
self.program_counter += 1
return None
else:
raise Exception("CASE komutunda sözdizimi hatası")
if command_upper == "END SELECT":
if self.select_stack:
self.select_stack.pop()
return None
else:
raise Exception("END SELECT için eşleşen SELECT CASE bulunamadı")
# IF...THEN...ELSE...END IF Desteği
if command_upper.startswith("IF"):
match = re.match(r"IF\s+(.+)\s+THEN", command, re.IGNORECASE)
if match:
condition = match.group(1)
cond_result = self.evaluate_expression(condition, scope_name)
self.if_stack.append({"condition": cond_result, "start": self.program_counter, "else_found": False})
if not cond_result:
while self.program_counter < len(self.program) and \
self.program[self.program_counter][0].upper() not in ("ELSE", "END IF"):
self.program_counter += 1
return None
else:
raise Exception("IF komutunda sözdizimi hatası")
if command_upper == "ELSE":
if not self.if_stack:
raise Exception("ELSE için eşleşen IF bulunamadı")
if_info = self.if_stack[-1]
if if_info["condition"] or if_info["else_found"]:
while self.program_counter < len(self.program) and \
self.program[self.program_counter][0].upper() != "END IF":
self.program_counter += 1
if_info["else_found"] = True
return None
if command_upper == "END IF":
if self.if_stack:
self.if_stack.pop()
return None
else:
raise Exception("END IF için eşleşen IF bulunamadı")
# Değişken ve Veri Yönetimi
if command_upper.startswith("DEFINT"):
match = re.match(r"DEFINT\s+(\w+)", command, re.IGNORECASE)
if match:
var_name = match.group(1)
self.current_scope()[var_name] = 0
return None
else:
raise Exception("DEFINT komutunda sözdizimi hatası")
if command_upper.startswith("DEFSNG"):
match = re.match(r"DEFSNG\s+(\w+)", command, re.IGNORECASE)
if match:
var_name = match.group(1)
self.current_scope()[var_name] = 0.0
return None
else:
raise Exception("DEFSNG komutunda sözdizimi hatası")
if command_upper.startswith("DEFDBL"):
match = re.match(r"DEFDBL\s+(\w+)", command, re.IGNORECASE)
if match:
var_name = match.group(1)
self.current_scope()[var_name] = 0.0
return None
else:
raise Exception("DEFDBL komutunda sözdizimi hatası")
if command_upper.startswith("DEFSTR"):
match = re.match(r"DEFSTR\s+(\w+)", command, re.IGNORECASE)
if match:
var_name = match.group(1)
self.current_scope()[var_name] = ""
return None
else:
raise Exception("DEFSTR komutunda sözdizimi hatası")
if command_upper.startswith("GLOBAL"):
match = re.match(r"GLOBAL\s+(\w+)\s+AS\s+(\w+)", command, re.IGNORECASE)
if match:
var_name, var_type = match.groups()
self.global_vars[var_name] = self.type_table.get(var_type, None)()
return None
else:
raise Exception("GLOBAL komutunda sözdizimi hatası")
if command_upper.startswith("DIM SHARED"):
match = re.match(r"DIM SHARED\s+(.+)\s+AS\s+(\w+)", command, re.IGNORECASE)
if match:
scopes, var_type = match.groups()
var_name = scopes.split(",")[-1].strip()
scope_list = [s.strip() for s in scopes.split(",")[:-1]]
self.shared_vars[var_name] = scope_list
return None
else:
raise Exception("DIM SHARED komutunda sözdizimi hatası")
if command_upper.startswith("DIM"):
match = re.match(r"DIM\s+(\w+)\s+AS\s+(\w+)", command, re.IGNORECASE)
if match:
var_name, var_type = match.groups()
if var_type in self.types:
self.current_scope()[var_name] = self.types[var_type](*[None for _ in self.types[var_type]._fields])
elif var_type in self.classes:
self.current_scope()[var_name] = self.classes[var_type]()
elif var_type == "ARRAY":
self.current_scope()[var_name] = np.array([])
elif var_type == "DATAFRAME":
self.current_scope()[var_name] = pd.DataFrame()
elif var_type == "STRING":
self.current_scope()[var_name] = ""
elif var_type in ("INTEGER", "LONG"):
self.current_scope()[var_name] = 0
elif var_type in ("SINGLE", "DOUBLE"):
self.current_scope()[var_name] = 0.0
elif var_type == "BYTE":
self.current_scope()[var_name] = 0
elif var_type == "SHORT":
self.current_scope()[var_name] = 0
elif var_type == "UNSIGNED INTEGER":
self.current_scope()[var_name] = 0
elif var_type == "CHAR":
self.current_scope()[var_name] = ''
elif var_type == "LIST":
self.current_scope()[var_name] = []
elif var_type == "DICT":
self.current_scope()[var_name] = {}
elif var_type == "SET":
self.current_scope()[var_name] = set()
elif var_type == "TUPLE":
self.current_scope()[var_name] = ()
else:
raise Exception(f"Tanımlanamayan veri tipi: {var_type}")
return None
else:
raise Exception("DIM komutunda sözdizimi hatası")
if command_upper.startswith("LET"):
match = re.match(r"LET\s+(\w+)\s*=\s*(.+)", command, re.IGNORECASE)
if match:
var_name, expr = match.groups()
value = self.evaluate_expression(expr, scope_name)
if var_name in self.global_vars:
self.global_vars[var_name] = value
elif var_name in self.shared_vars and (scope_name in self.shared_vars[var_name] or not self.shared_vars[var_name]):
self.shared_vars[var_name] = value
elif var_name in self.current_scope():
self.current_scope()[var_name] = value
else:
raise Exception(f"Tanımlanmamış değişken: {var_name}")
return None
else:
raise Exception("LET komutunda sözdizimi hatası")
# Girdi/Çıktı
if command_upper.startswith("PRINT"):
print_str = command[5:].strip()
parts = re.split(r'([;,])', print_str)
output = ""
for j in range(0, len(parts), 2):
arg = parts[j].strip()
if arg:
value = self.evaluate_expression(arg, scope_name)
output += str(value)
if j + 1 < len(parts) and parts[j+1] == ',':
output += " "
if print_str.strip().endswith(';'):
print(output, end='')
else:
print(output)
return None
if command_upper.startswith("INPUT"):
match = re.match(r"INPUT\s+\"(.+)\"?,\s*(\w+)", command, re.IGNORECASE)
if match:
prompt, var_name = match.groups()
if prompt:
value = input(prompt + " ")
else:
value = input("> ")
self.current_scope()[var_name] = value
return None
else:
raise Exception("INPUT komutunda sözdizimi hatası")
if command_upper.startswith("LINE INPUT"):
match = re.match(r"LINE INPUT\s+\"(.+)\"?,\s*(\w+)", command, re.IGNORECASE)
if match:
prompt, var_name = match.groups()
if prompt:
value = input(prompt + " ")
else:
value = input("> ")
self.current_scope()[var_name] = value
return None
else:
raise Exception("LINE INPUT komutunda sözdizimi hatası")
if command_upper.startswith("WRITE"):
match = re.match(r"WRITE\s+(.+)", command, re.IGNORECASE)
if match:
expr = match.group(1)
value = self.evaluate_expression(expr, scope_name)
print(f'"{value}"' if isinstance(value, str) else value)
return None
else:
raise Exception("WRITE komutunda sözdizimi hatası")
# Atama
if re.match(r"\w+\s*=\s*.+", command, re.IGNORECASE):
match = re.match(r"(\w+)\s*=\s*(.+)", command, re.IGNORECASE)
if match:
var_name, expr = match.groups()
value = self.evaluate_expression(expr, scope_name)
if var_name in self.global_vars:
self.global_vars[var_name] = value
elif var_name in self.shared_vars and (scope_name in self.shared_vars[var_name] or not self.shared_vars[var_name]):
self.shared_vars[var_name] = value
elif var_name in self.current_scope():
self.current_scope()[var_name] = value
else:
raise Exception(f"Tanımlanmamış değişken: {var_name}")
return None
# Alt Programlar
if command_upper.startswith("GOTO"):
match = re.match(r"GOTO\s+(\w+)", command, re.IGNORECASE)
if match:
label = match.group(1)
if label in self.labels:
return self.labels[label]
else:
raise Exception(f"Etiket bulunamadı: {label}")
else:
raise Exception("GOTO komutunda sözdizimi hatası")
if command_upper.startswith("GOSUB"):
match = re.match(r"GOSUB\s+(\w+)", command, re.IGNORECASE)
if match:
label = match.group(1)
if label in self.labels:
self.call_stack.append(self.program_counter)
return self.labels[label]
else:
raise Exception(f"Etiket bulunamadı: {label}")
else:
raise Exception("GOSUB komutunda sözdizimi hatası")
if command_upper == "RETURN":
if self.call_stack:
return self.call_stack.pop()
else:
raise Exception("RETURN için eşleşen GOSUB bulunamadı")
if command_upper.startswith("CALL"):
match = re.match(r"CALL\s+(\w+)", command, re.IGNORECASE)
if match:
sub_name = match.group(1)
if sub_name in self.subs:
self.call_stack.append(self.program_counter)
self.local_scopes.append({})
return self.subs[sub_name]
else:
raise Exception(f"Alt program bulunamadı: {sub_name}")
else:
raise Exception("CALL komutunda sözdizimi hatası")
# Fonksiyon Çağrısı
if re.match(r"\w+\s*\(.+\)", command, re.IGNORECASE):
match = re.match(r"(\w+)\s*\((.+)\)", command, re.IGNORECASE)
if match:
func_name, args_str = match.groups()
if func_name in self.functions:
self.call_stack.append(self.program_counter)
self.local_scopes.append({})
return self.functions[func_name]
elif func_name in self.function_table:
args_tuple = self.evaluate_expression(f"({args_str})", scope_name)
return self.function_table[func_name](*args_tuple)
else:
raise Exception(f"Fonksiyon bulunamadı: {func_name}")
else:
raise Exception("Fonksiyon çağrısında sözdizimi hatası")
# Dosya İşlemleri
if command_upper.startswith("OPEN"):
if "FOR ISAM" not in command_upper:
match = re.match(r"OPEN\s+\"(.+)\"\s+FOR\s+(INPUT|OUTPUT|APPEND|BINARY)\s+AS\s+#(\d+)", command, re.IGNORECASE)
if match:
file_path, mode, file_num = match.groups()
mode_map = {"INPUT": "r", "OUTPUT": "w", "APPEND": "a", "BINARY": "rb+"}
self.file_handles[int(file_num)] = open(file_path, mode_map[mode])
return None
else:
raise Exception("OPEN komutunda sözdizimi hatası")
if command_upper.startswith("PRINT #"):
match = re.match(r"PRINT\s+#(\d+),\s*(.+)", command, re.IGNORECASE)
if match:
file_num, data = match.groups()
file = self.file_handles.get(int(file_num))
if file:
file.write(str(self.evaluate_expression(data, scope_name)) + "\n")
file.flush()
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("PRINT # komutunda sözdizimi hatası")
if command_upper.startswith("INPUT #"):
match = re.match(r"INPUT\s+#(\d+),\s*(\w+)", command, re.IGNORECASE)
if match:
file_num, var_name = match.groups()
file = self.file_handles.get(int(file_num))
if file:
self.current_scope()[var_name] = file.readline().strip()
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("INPUT # komutunda sözdizimi hatası")
if command_upper.startswith("LINE INPUT #"):
match = re.match(r"LINE INPUT\s+#(\d+),\s*(\w+)", command, re.IGNORECASE)
if match:
file_num, var_name = match.groups()
file = self.file_handles.get(int(file_num))
if file:
self.current_scope()[var_name] = file.readline().strip()
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("LINE INPUT # komutunda sözdizimi hatası")
if command_upper.startswith("SEEK"):
match = re.match(r"SEEK\s+#(\d+),\s*(.+)", command, re.IGNORECASE)
if match:
file_num, position = match.groups()
file = self.file_handles.get(int(file_num))
if file:
file.seek(self.evaluate_expression(position, scope_name))
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("SEEK komutunda sözdizimi hatası")
if command_upper.startswith("GET #"):
match = re.match(r"GET\s+#(\d+),\s*(.+),\s*(\w+)", command, re.IGNORECASE)
if match:
file_num, position, var_name = match.groups()
file = self.file_handles.get(int(file_num))
if file:
file.seek(self.evaluate_expression(position, scope_name))
self.current_scope()[var_name] = file.read(1) # Basit okuma
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("GET # komutunda sözdizimi hatası")
if command_upper.startswith("PUT #"):
match = re.match(r"PUT\s+#(\d+),\s*(.+),\s*(.+)", command, re.IGNORECASE)
if match:
file_num, position, data = match.groups()
file = self.file_handles.get(int(file_num))
if file:
file.seek(self.evaluate_expression(position, scope_name))
file.write(str(self.evaluate_expression(data, scope_name)))
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("PUT # komutunda sözdizimi hatası")
if command_upper.startswith("CLOSE"):
match = re.match(r"CLOSE\s+#(\d+)", command, re.IGNORECASE)
if match:
file_num = match.group(1)
num = int(file_num)
if num in self.file_handles:
self.file_handles[num].close()
del self.file_handles[num]
elif num in self.db_connections:
self.db_connections[num].close()
del self.db_connections[num]
else:
raise Exception(f"Kapatılacak dosya #{file_num} bulunamadı")
return None
else:
raise Exception("CLOSE komutunda sözdizimi hatası")
if command_upper.startswith("KILL"):
match = re.match(r"KILL\s+\"(.+)\"", command, re.IGNORECASE)
if match:
file_name = match.group(1)
os.remove(file_name)
return None
else:
raise Exception("KILL komutunda sözdizimi hatası")
if command_upper.startswith("NAME"):
match = re.match(r"NAME\s+\"(.+)\"\s+AS\s+\"(.+)\"", command, re.IGNORECASE)
if match:
old_name, new_name = match.groups()
os.rename(old_name, new_name)
return None
else:
raise Exception("NAME komutunda sözdizimi hatası")
if command_upper.startswith("FILES"):
match = re.match(r"FILES\s+\"(.+)\"", command, re.IGNORECASE)
if match:
pattern = match.group(1)
print("\n".join(os.listdir(pattern)))
return None
else:
raise Exception("FILES komutunda sözdizimi hatası")
if command_upper.startswith("CHDIR"):
match = re.match(r"CHDIR\s+\"(.+)\"", command, re.IGNORECASE)
if match:
path = match.group(1)
os.chdir(path)
return None
else:
raise Exception("CHDIR komutunda sözdizimi hatası")
if command_upper.startswith("MKDIR"):
match = re.match(r"MKDIR\s+\"(.+)\"", command, re.IGNORECASE)
if match:
path = match.group(1)
os.mkdir(path)
return None
else:
raise Exception("MKDIR komutunda sözdizimi hatası")
if command_upper.startswith("RMDIR"):
match = re.match(r"RMDIR\s+\"(.+)\"", command, re.IGNORECASE)
if match:
path = match.group(1)
os.rmdir(path)
return None
else:
raise Exception("RMDIR komutunda sözdizimi hatası")
# Veritabanı İşlemleri
if command_upper.startswith("OPEN") and "FOR ISAM" in command_upper:
match = re.match(r"OPEN\s+\"(.+)\"\s+FOR\s+ISAM\s+AS\s+#(\d+)", command, re.IGNORECASE)
if match:
db_file, file_num = match.groups()
conn = sqlite3.connect(db_file)
self.db_connections[int(file_num)] = conn
self.transaction_active[int(file_num)] = False
return None
else:
raise Exception("OPEN FOR ISAM komutunda sözdizimi hatası")
if command_upper.startswith("DEFINE TABLE"):
match = re.match(r"DEFINE TABLE\s+(\w+)\s*\((.+)\)", command, re.IGNORECASE)
if match:
table_name, fields = match.groups()
fields = [f.strip() for f in fields.split(",")]
columns = []
for field in fields:
match_field = re.match(r"(\w+)\s+AS\s+(\w+)(?:\s+CHECK\((.+)\))?", field, re.IGNORECASE)
if match_field:
col_name, col_type, constraint = match_field.groups()
sql_type = {"STRING": "TEXT", "INTEGER": "INTEGER", "DOUBLE": "REAL"}.get(col_type.upper(), "TEXT")
column_def = f"{col_name} {sql_type}"
if constraint:
column_def += f" CHECK({constraint})"
columns.append(column_def)
else:
raise Exception(f"Alan tanımı hatası: {field}")
sql = f"CREATE TABLE IF NOT EXISTS {table_name} ({', '.join(columns)})"
for conn in self.db_connections.values():
conn.execute(sql)
conn.commit()
return None
else:
raise Exception("DEFINE TABLE komutunda sözdizimi hatası")
if command_upper.startswith("PUT"):
match = re.match(r"PUT\s+#(\d+),\s*(AUTOKEY|\w+),\s*(.+)", command, re.IGNORECASE)
if match:
file_num, key, value = match.groups()
conn = self.db_connections.get(int(file_num))
if conn:
if key.upper() == "AUTOKEY":
conn.execute("INSERT INTO data (value) VALUES (?)", (value,))
else:
conn.execute("INSERT OR REPLACE INTO data (key, value) VALUES (?, ?)", (key, value))
if not self.transaction_active.get(int(file_num), False):
conn.commit()
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("PUT komutunda sözdizimi hatası")
if command_upper.startswith("GET"):
match = re.match(r"GET\s+#(\d+),\s*(\w+),\s*(\w+)", command, re.IGNORECASE)
if match:
file_num, key, var_name = match.groups()
conn = self.db_connections.get(int(file_num))
if conn:
cursor = conn.execute("SELECT value FROM data WHERE key = ?", (key,))
result = cursor.fetchone()
self.current_scope()[var_name] = result[0] if result else None
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("GET komutunda sözdizimi hatası")
if command_upper.startswith("DELETE"):
match = re.match(r"DELETE\s+#(\d+),\s*(\w+)", command, re.IGNORECASE)
if match:
file_num, key = match.groups()
conn = self.db_connections.get(int(file_num))
if conn:
conn.execute("DELETE FROM data WHERE key = ?", (key,))
if not self.transaction_active.get(int(file_num), False):
conn.commit()
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("DELETE komutunda sözdizimi hatası")
if command_upper.startswith("SELECT"):
match = re.match(r"SELECT\s+(.+)\s+FROM\s+(\w+)\s*(?:WHERE\s+(.+))?\s+INTO\s+(\w+)", command, re.IGNORECASE)
if match:
columns, table, where, var_name = match.groups()
for num, conn in self.db_connections.items():
query = f"SELECT {columns} FROM {table}"
if where:
query += f" WHERE {where}"
df = pd.read_sql_query(query, conn)
self.current_scope()[var_name] = df
break
return None
else:
raise Exception("SELECT komutunda sözdizimi hatası")
if command_upper == "BEGIN TRANSACTION":
match = re.match(r"BEGIN TRANSACTION\s+#(\d+)", command, re.IGNORECASE)
if match:
file_num = int(match.group(1))
if file_num in self.db_connections:
self.transaction_active[file_num] = True
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("BEGIN TRANSACTION komutunda sözdizimi hatası")
if command_upper == "COMMIT":
match = re.match(r"COMMIT\s+#(\d+)", command, re.IGNORECASE)
if match:
file_num = int(match.group(1))
if file_num in self.db_connections:
self.db_connections[file_num].commit()
self.transaction_active[file_num] = False
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("COMMIT komutunda sözdizimi hatası")
if command_upper == "ROLLBACK":
match = re.match(r"ROLLBACK\s+#(\d+)", command, re.IGNORECASE)
if match:
file_num = int(match.group(1))
if file_num in self.db_connections:
self.db_connections[file_num].rollback()
self.transaction_active[file_num] = False
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("ROLLBACK komutunda sözdizimi hatası")
if command_upper.startswith("INDEX"):
match = re.match(r"INDEX\s+(\w+)\s+ON\s+(\w+)", command, re.IGNORECASE)
if match:
table, column = match.groups()
for conn in self.db_connections.values():
conn.execute(f"CREATE INDEX idx_{column} ON {table} ({column})")
conn.commit()
return None
else:
raise Exception("INDEX komutunda sözdizimi hatası")
# Diğer Komutlar
if command_upper.startswith("RANDOMIZE"):
random.seed()
return None
if command_upper.startswith("READ"):
match = re.match(r"READ\s+(\w+)", command, re.IGNORECASE)
if match:
var_name = match.group(1)
if self.data_pointer < len(self.data_list):
self.current_scope()[var_name] = self.data_list[self.data_pointer]
self.data_pointer += 1
else:
raise Exception("DATA listesi tükendi")
return None
else:
raise Exception("READ komutunda sözdizimi hatası")
if command_upper.startswith("RESTORE"):
self.data_pointer = 0
return None
if command_upper.startswith("LOAD"):
match = re.match(r"LOAD\s+\"(.+)\"", command, re.IGNORECASE)
if match:
file_name = match.group(1)
if file_name.endswith(".basX"):
self.load_program(file_name)
return 0
else:
raise Exception("Dosya uzantısı .basX olmalı")
else:
raise Exception("LOAD komutunda sözdizimi hatası")
if command_upper.startswith("SAVE"):
match = re.match(r"SAVE\s+\"(.+)\"", command, re.IGNORECASE)
if match:
file_name = match.group(1)
with open(file_name, "w") as f:
f.write("\n".join([line[0] for line in self.program]))
return None
else:
raise Exception("SAVE komutunda sözdizimi hatası")
if command_upper == "RUN":
self.run()
return None
if command_upper == "END":
self.running = False
return None
raise Exception(f"Bilinmeyen komut: {command}")
except Exception as e:
error_msg = f"PDSX Hatası: {str(e)}, Satır {self.program_counter + 1}"
print(error_msg)
logging.error(error_msg)
if self.error_handler and self.debug_mode:
print(f"Hata işleyicisine gidiliyor: {self.error_handler}")
if self.error_handler:
self.program_counter = self.error_handler
else:
self.running = False
return None
def load_program(self, file_name):
try:
with open(file_name, "r") as f:
code = f.read()
self.parse_program(code)
except Exception as e:
error_msg = f"Dosya yükleme hatası: {str(e)}"
print(error_msg)
logging.error(error_msg)
def run(self):
self.running = True
self.program_counter = 0
while self.running and self.program_counter < len(self.program):
command, scope = self.program[self.program_counter]
if self.debug_mode:
print(f"Satır {self.program_counter + 1}: {command}")
input("Devam etmek için Enter'a basın...")
try:
next_line = self.execute_command(command, scope)
if next_line is not None:
self.program_counter = next_line
else:
self.program_counter += 1
if scope and self.program_counter < len(self.program) and self.program[self.program_counter][0].upper() in ("END SUB", "END FUNCTION"):
if self.call_stack:
self.local_scopes.pop()
self.program_counter = self.call_stack.pop()
else:
self.running = False
except Exception as e:
error_msg = f"Çalıştırma hatası: {str(e)}"
print(error_msg)
logging.error(error_msg)
self.running = False
for conn in self.db_connections.values():
conn.close()
for file in self.file_handles.values():
file.close()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="pdsX Interpreter")
parser.add_argument("file", nargs='?', help="The .basX file to run")
args = parser.parse_args()
interpreter = pdsXInterpreter()
if args.file:
if args.file.endswith(".basX"):
interpreter.load_program(args.file)
interpreter.run()
else:
print("Hata: Dosya uzantısı .basX olmalı")
else:
print("Kullanım: pdsX dosya_adi.basX")
```
Bu program, bir BASIC diline (özellikle QBasic/QuickBASIC ve MS-DOS BASIC komutlarını) ilham alan, ancak Python ile implemente edilmiş **pdsX Interpreter**'dır. Bu yorumlar, her satırın ne yaptığını, sınıfların ve fonksiyonların amacını detaylıca açıklamaktadır. Yorumlar, kodun mantığını tamamen anlamak için yapılmıştır.
---
### 📜 **GENEL AÇIKLAMA: pdsXInterpreter Sınıfı**
Bu sınıf, bir **BASIC dili yorumlayıcısı** (interpreter) simüle etmektedir.
- **Amacı:** `.basX` uzantılı dosyalarda yazılmış BASIC benzeri kodları okuyup, satır satır yorumlamak ve çalıştırmak.
- **Temel Özellikler:** Değişken tanımlama (`DIM`, `GLOBAL`), alt programlar (`SUB`, `FUNCTION`), veritabanı işlemleri (`OPEN FOR ISAM`), dosya I/O, matematiksel fonksiyonlar, döngüler (`FOR`, `WHILE`, `DO...LOOP`), karar yapıları (`IF...THEN`, `SELECT CASE`), DATA/READ, hata yönetimi (`ON ERROR GOTO`) gibi temel BASIC özelliklerini destekler.
- **Kullanım:** Python kütüphanelerini (`numpy`, `pandas`, `sqlite3`, `scipy`) kullanarak BASIC'in "fonksiyonel" davranışlarını taklit eder. Örneğin, `PRINT #1, x` komutu bir dosyaya yazarken, `SELECT ... INTO df` komutu bir SQLite tablosunu pandas DataFrame'e yükler.
- **Yapı:** Kod, tek bir sınıf içinde tüm yorumlayıcı mantığını barındırır. Bu, basitlik ve entegrasyon sağlar ama genişletilebilirliği zorlaştırır.
---
## ✅ **SINIF: `pdsXInterpreter`**
```python
import re # Regex: Metinleri parçalamak, komutları analiz etmek için gerekli (örn: "FOR i=1 TO 10" parse etmek)
import random # Rastgele sayı üretmek için (RND fonksiyonu için)
import sqlite3 # Veritabanı (ISAM) işlemlerini gerçekleştirmek için (OPEN FOR ISAM, DEFINE TABLE, GET/PUT)
import numpy as np # Matematiksel fonksiyonlar (SIN, COS, LOG, MEAN, STD, SUM vb.) ve ARRAY tipi için
import pandas as pd # Veri çerçeveleri (DATAFRAME) ve SQL sorguları (SELECT ... INTO df) için
import scipy.stats as stats # İstatistiksel fonksiyonlar (CORR, TTEST, CHISQUARE, REGRESS vb.) için
from collections import defaultdict, namedtuple
# defaultdict: SHARED değişkenler için (birden fazla kapsamda paylaşılabilir)
# namedtuple: TYPE tanımları için yapısal veri tipleri oluşturmak için (örn: TYPE Point AS (x AS INTEGER, y AS INTEGER))
from types import SimpleNamespace # Kullanılmıyor gibi görünüyor, muhtemelen eski bir deneme
import os # Dosya sistemi işlemleri (OPEN, CLOSE, KILL, NAME, FILES, CHDIR, MKDIR, RMDIR)
import logging # Hata loglama için: interpreter_errors.log dosyasına hataları kaydeder
import time # Zaman fonksiyonları (TIMER, DATE$, TIME$)
import sys # Komut satırı argümanları (COMMAND$), program çalışırken sistem bilgileri için
import argparse # Komut satırından .basX dosyası almak için (argparse.ArgumentParser)
import struct # MKI$, MKS$, MKD$ fonksiyonları için byte paketleme/depaketleme (binary veri)
# Hata loglama için logging ayarı
logging.basicConfig(filename='interpreter_errors.log', level=logging.ERROR, format='%(asctime)s - %(message)s')
```
---
### 🔧 **`__init__` METODU: Sınıfın Kurulumu (Constructor)**
```python
class pdsXInterpreter:
def __init__(self):
# GLOBAL DEĞİŞKENLER: Program boyunca erişilebilir, her scope'da geçerli
self.global_vars = {}
# DIM SHARED DEĞİŞKENLER: Birden fazla SUB/FUNCTION tarafından paylaşılan değişkenler.
# Anahtar: değişken adı, Değer: hangi isimli scope'larda paylaşıldığı listesi (örn: ["MAIN", "SUB1"])
self.shared_vars = defaultdict(list)
# YEREL KAPSAMLAR: SUB/FUNCTION çağrıldığında yeni bir scope açılır. Stack yapısı.
# Her çağrıda son eleman ([-1]) geçerli scope'tur.
self.local_scopes = [{}]
# TYPE TANIMLARI: STRUCT benzeri yapılar. namedtuple ile oluşturulur.
# Örnek: TYPE Point AS (x AS INTEGER, y AS INTEGER) → self.types["Point"] = <namedtuple>
self.types = {}
# CLASS TANIMLARI: OOP sınıf tanımı (sınırlı destek). Lambda metotlarla simüle edilir.
# Örnek: CLASS MyClass ... END CLASS → self.classes["MyClass"] = <type object>
self.classes = {}
# FUNCTION TANIMLARI: Tanımlanan kullanıcı fonksiyonlarının başlangıç satır numaraları
# KEY: fonksiyon adı, VALUE: program listesindeki satır indeksi
self.functions = {}
# SUB TANIMLARI: Alt programların başlangıç satır numaraları
self.subs = {}
# ETİKETLER (LABEL): GOTO/GOSUB için kullanılan etiketlerin satır numaraları
self.labels = {}
# PROGRAM SATIRLARI: Parse edilen tüm kod satırları. Tuple: (komut_str, scope_ismi)
# Scope_ismi: Eğer bir SUB/FUNCTION içindese, o fonksiyonun adı, aksi halde None
self.program = []
# PROGRAM SAYACI: Şu an yürütülen satırın indeksi
self.program_counter = 0
# ÇAĞRI YIĞINI (CALL STACK): GOSUB, CALL, FUNCTION çağrısında geri dönülmesi gereken adresi saklar
self.call_stack = []
# YÜRÜTME DURUMU: True ise program çalışıyor, False ise durdu
self.running = False
# VERİTABANI BAĞLANTILARI: OPEN FOR ISAM komutuyla açılan SQLite bağlantıları
# KEY: dosya numarası (#1, #2), VALUE: sqlite3.Connection nesnesi
self.db_connections = {}
# DOSYA KOLLARI: OPEN komutuyla açılan dosya handle'ları
# KEY: dosya numarası (#1, #2), VALUE: Python file nesnesi
self.file_handles = {}
# HATA İŞLEYİCİSİ: ON ERROR GOTO ile belirlenen etiket satır numarası
# Eğer hata oluşursa, program_counter bu değere atlanır
self.error_handler = None
# DEBUG MODU: True ise her satırda input() ile durdurulur, debug çıktısı verilir
self.debug_mode = False
# DÖNGÜ YIĞINI: DO...LOOP, WHILE...WEND, FOR...NEXT döngülerinin başlangıç noktalarını ve koşulları tutar
self.loop_stack = []
# SELECT CASE YIĞINI: SELECT CASE bloklarının durumunu (değer, eşleşti mi?) tutar
self.select_stack = []
# IF...THEN...ELSE YIĞINI: IF bloklarının durumunu (koşul sonucu, ELSE bulundu mu?) tutar
self.if_stack = []
# DATA LİSTESİ: DATA komutlarıyla eklenen verilerin listesi
# Örnek: DATA 10, "Hello", 3.14 → ["10", "Hello", "3.14"]
self.data_list = []
# DATA İŞARETCİSİ: READ komutunda hangi veri okunacak olduğunu gösterir (sıfırdan başlar)
self.data_pointer = 0
# TRANSAKSİYON DURUMLARI: OPEN FOR ISAM sonrası BEGIN TRANSACTION ile başlatılan işlemler
# KEY: db dosya numarası, VALUE: bool (True = transaction aktif)
self.transaction_active = {}
# VERİ TİPLERİ TABLOSU: BASIC veri tiplerini Python tiplerine eşler
# Örnek: "INTEGER" → int, "STRING" → str, "ARRAY" → np.array, "DATAFRAME" → pd.DataFrame
self.type_table = {
"STRING": str,
"INTEGER": int,
"LONG": int,
"SINGLE": float,
"DOUBLE": float,
"BYTE": int,
"SHORT": int,
"UNSIGNED INTEGER": int,
"CHAR": str,
"LIST": list,
"DICT": dict,
"SET": set,
"TUPLE": tuple,
"ARRAY": np.array,
"DATAFRAME": pd.DataFrame,
"POINTER": None, # Bellek adresi simülasyonu (gerçekleşmiyor)
"STRUCT": dict, # STRUCT: key-value olarak saklanır
"UNION": None, # Tek değer depolama (simülasyon yok)
"ENUM": dict, # Enum: sözlük olarak simüle edilebilir
"VOID": None,
"BITFIELD": int # Bit operasyonları için (ancak uygulanmamış)
}
# FONKSİYON TABLOSU: Built-in fonksiyonlar ve onların Python karşılıkları
# KEY: fonksiyon adı (örn: "MID$", "LEN"), VALUE: lambda veya referans fonksiyon
# Bu fonksiyonlar, execute_command içinde eval() ile çağrılır.
self.function_table = {
"MID$": lambda s, start, length: s[start-1:start-1+length], # BASIC MID$(str, start, len) → Python slicing
"LEN": len, # String uzunluğu
"RND": random.random, # 0.0 - 1.0 arası rastgele sayı
"ABS": abs,
"INT": int, # Tam sayıya çevirir (yukarı yuvarlama değil!)
"LEFT$": lambda s, n: s[:n],
"RIGHT$": lambda s, n: s[-n:],
"LTRIM$": lambda s: s.lstrip(),
"RTRIM$": lambda s: s.rstrip(),
"STRING$": lambda n, c: c * n, # "A" * 5 → "AAAAA"
"SPACE$": lambda n: " " * n,
"INSTR": lambda start, s, sub: s.find(sub, start-1) + 1, # 1-tabanlı indeks!
"UCASE$": lambda s: s.upper(),
"LCASE$": lambda s: s.lower(),
"STR$": lambda n: str(n),
"SQR": np.sqrt,
"SIN": np.sin,
"COS": np.cos,
"TAN": np.tan,
"LOG": np.log,
"EXP": np.exp,
"ATN": np.arctan,
"FIX": lambda x: int(x), # Ondalıklı kısmı keser
"ROUND": lambda x, n=0: round(x, n),
"SGN": lambda x: -1 if x < 0 else (1 if x > 0 else 0),
"MOD": lambda x, y: x % y,
"MIN": lambda *args: min(args),
"MAX": lambda *args: max(args),
"TIMER": lambda: time.time(), # Unix zaman damgası (saniye)
"DATE$": lambda: time.strftime("%m-%d-%Y"),
"TIME$": lambda: time.strftime("%H:%M:%S"),
"INKEY$": lambda: input()[:1], # Klavyeden ilk karakteri okur (BASIC'te beklemeyen INKEY$)
"ENVIRON$": lambda var: os.environ.get(var, ""), # Ortam değişkeni okuma
"COMMAND$": lambda: " ".join(sys.argv[1:]), # Komut satırı argümanları
"CSRLIN": lambda: 1, # Ekran satırı konumu (simülasyon: hep 1)
"POS": lambda x: 1, # Ekran sütunu (simülasyon)
"VAL": lambda s: float(s) if s.replace(".", "").isdigit() else 0, # String→Sayı dönüşümü
"ASC": lambda c: ord(c[0]), # Karakterin ASCII değeri
"MEAN": np.mean,
"MEDIAN": np.median,
"MODE": lambda x: stats.mode(x)[0][0], # En çok tekrar eden değer
"STD": np.std,
"VAR": np.var,
"SUM": np.sum,
"PROD": np.prod,
"PERCENTILE": np.percentile,
"QUANTILE": np.quantile,
"CORR": lambda x, y: np.corrcoef(x, y)[0, 1], # Korelasyon katsayısı
"COV": np.cov, # Kovaryans matrisi döner
"DESCRIBE": lambda df: df.describe(), # Pandas describe()
"GROUPBY": lambda df, col: df.groupby(col),
"FILTER": lambda df, cond: df.query(cond), # Pandas query ile filtreleme
"SORT": lambda df, col: df.sort_values(col),
"HEAD": lambda df, n=5: df.head(n),
"TAIL": lambda df, n=5: df.tail(n),
"MERGE": lambda df1, df2, on: pd.merge(df1, df2, on=on),
"TTEST": lambda sample1, sample2: stats.ttest_ind(sample1, sample2),
"CHISQUARE": lambda observed: stats.chisquare(observed),
"ANOVA": lambda *groups: stats.f_oneway(*groups),
"REGRESS": lambda x, y: stats.linregress(x, y),
"EOF": lambda n: self.file_handles[n].eof(), # Dosya sonu kontrolü (Python'da EOF yok, simülasyon eksik!)
"LOC": lambda n: self.file_handles[n].tell(), # Dosya imleci konumu
"LOF": lambda n: os.path.getsize(self.file_handles[n].name), # Dosya boyutu
"FREEFILE": lambda: min(set(range(1, 100)) - set(self.file_handles.keys())), # Kullanılmayan en küçük dosya numarası
"CHR$": lambda n: chr(n), # ASCII kodundan karakter
"INPUT$": lambda n, f: self.file_handles[f].read(n), # Dosyadan n bayt oku
"MKI$": lambda n: struct.pack("i", n).decode('latin1'), # 32-bit tamsayıyı stringe dönüştür
"MKS$": lambda n: struct.pack("f", n).decode('latin1'), # 32-bit float
"MKD$": lambda n: struct.pack("d", n).decode('latin1') # 64-bit double
}
```
---
### 🔍 **`current_scope()` METODU: Geçerli Yerel Kapsamı Döndürür**
```python
def current_scope(self):
return self.local_scopes[-1] # Yerel scope stack'inin en üstündeki (son) sözlüğü döndürür
```
> **Amacı:** `DIM`, `LET`, `READ` gibi komutlar için geçerli değişkenlerin bulunduğu kapsama erişim sağlar.
> **Kullanımı:** `self.current_scope()[var_name] = value` şeklinde değişken atanır.
> **Not:** `global_vars` ve `shared_vars` burada dahil değildir — ayrı işlem yapılır.
---
### 📚 **`parse_program(code)` METODU: Kodu Satır Satır Analiz Edip Program Listesine Çevirir**
```python
def parse_program(self, code):
self.program = [] # Önceki programı temizle
current_sub = None # Şu an işlenen SUB'un adı
current_function = None # Şu an işlenen FUNCTION'un adı
current_type = None # Şu an işlenen TYPE'nin adı
current_class = None # Şu an işlenen CLASS'ın adı
type_fields = {} # TYPE içindeki alanları saklar: {"Point": [("x", "INTEGER"), ("y", "INTEGER")]}
class_methods = {} # CLASS içindeki metotları saklar: {"MyClass": {"method1": lambda ...}}
lines = code.split("\n") # Kodu satırlara ayır
i = 0 # Satır indeksi
while i < len(lines):
line = lines[i].strip() # Boşlukları temizle
if not line: # Boş satır atla
i += 1
continue
line_upper = line.upper()
# SUB TANIMI: SUB Adı(...) → SUB'nin başlangıç satırını kaydet
if line_upper.startswith("SUB "):
sub_name = line[4:].split("(")[0].strip() # "SUB MySub(a,b)" → "MySub"
self.subs[sub_name] = i + 1 # Sonraki satırda kod başlar
current_sub = sub_name # Artık SUB içindesin
i += 1
# FUNCTION TANIMI: FUNCTION Adı(...) → Fonksiyonun başlangıç satırını kaydet
elif line_upper.startswith("FUNCTION "):
func_name = line[8:].split("(")[0].strip()
self.functions[func_name] = i + 1
current_function = func_name
i += 1
# TYPE TANIMI: TYPE Point → Tipin adını kaydet
elif line_upper.startswith("TYPE "):
type_name = line[5:].strip()
current_type = type_name
type_fields[type_name] = [] # Alan listesini başlat
i += 1
# END TYPE: TYPE tanımının bitişi → namedtuple oluştur
elif line_upper == "END TYPE":
# namedtuple: (isim, alan_adları_listesi)
field_names = [f[0] for f in type_fields[current_type]] # ["x", "y"]
self.types[current_type] = namedtuple(current_type, field_names)
current_type = None
i += 1
# TYPE İÇİNDEKİ ALAN TANIMLARI: x AS INTEGER
elif current_type:
match = re.match(r"(\w+)\s+AS\s+(\w+)", line, re.IGNORECASE)
if match:
field_name, field_type = match.groups()
type_fields[current_type].append((field_name, field_type))
else:
raise Exception(f"TYPE tanımı hatası: {line}")
i += 1
# CLASS TANIMI: CLASS MyClass
elif line_upper.startswith("CLASS "):
class_name = line[6:].strip()
current_class = class_name
class_methods[class_name] = {} # Boş metod listesi
i += 1
# END CLASS: Class tanımının bitişi
elif line_upper.startswith("END CLASS"):
# Dinamik olarak sınıf oluştur: type(class_name, (base,), methods_dict)
class_def = type(current_class, (), {
**class_methods[current_class], # Metodları ekle
'_vars': {}, # İç değişkenler için boş bir sözlük
'__init__': lambda self: None # Boş constructor
})
self.classes[current_class] = class_def
current_class = None
i += 1
# CLASS İÇİNDEKİ SUB (METOT): SUB MethodName(...)
elif current_class and line_upper.startswith("SUB "):
method_name = line[4:].split("(")[0].strip()
# Lambda: self._vars[method_name] fonksiyonunu çağırır (ama henüz atanmadı!)
# Bu bir 'hata' içeriyor: Metot tanımı geçersiz çünkü lambda self._vars[method_name] çalışmaz!
class_methods[current_class][method_name] = lambda self, *args: self._vars.get(method_name, lambda: None)(*args)
i += 1
# END SUB / END FUNCTION: SUB/FUNCTION bloğunun sonu
elif line_upper == "END SUB" or line_upper == "END FUNCTION":
current_sub = None
current_function = None
i += 1
# ETİKET TANIMI: LABEL MyLabel
elif line_upper.startswith("LABEL "):
label_name = line[6:].strip()
self.labels[label_name] = i # Etiketin satır numarası
i += 1
# DATA KOMUTU: DATA 10, "abc", 3.14
elif line_upper.startswith("DATA "):
data_items = line[5:].split(",")
self.data_list.extend([item.strip() for item in data_items])
i += 1
# GENEL KOMUTLAR: SUB/FUNCTION dışındaki satırlar
else:
if current_sub or current_function:
# SUB/FUNCTION içindeki satır: (satır, scope_ismi)
self.program.append((line, current_sub or current_function))
else:
# Ana program satırı: (satır, None)
self.program.append((line, None))
i += 1
```
> **⚠️ UYARI:** `CLASS` içindeki `SUB` metotlarının tanımı **yanlış**!
> `lambda self, *args: self._vars.get(method_name, lambda: None)(*args)`
> Burada `_vars[method_name]` hiçbir zaman atanmaz. Bu nedenle metotlar çalışmaz.
> **Düzeltme önerisi:** `class_methods[current_class][method_name] = lambda self, *args: ...` yerine,
> gerçek bir fonksiyon tanımı olmalı ve `__init__` içinde `self.method_name = ...` yapılmalı.
---
### 🧮 **`evaluate_expression(expr, scope_name=None)` METODU: İfadeyi Yorumlar ve Sonucu Döndürür**
```python
def evaluate_expression(self, expr, scope_name=None):
expr = expr.strip()
namespace = {} # Değişkenlerin ve fonksiyonların bulunduğu isim alanı
# Global değişkenleri ekle
namespace.update(self.global_vars)
# Mevcut (yerel) scope değişkenlerini ekle
namespace.update(self.current_scope())
# SHARED değişkenleri ekle: scope_name ile eşleşenleri veya global shared olanları
for var in self.shared_vars:
if scope_name in self.shared_vars[var] or not self.shared_vars[var]:
namespace[var] = self.shared_vars[var]
# Built-in fonksiyonları ekle
namespace.update(self.function_table)
# Numpy, Pandas, Scipy modüllerini ekle (eval() içinde kullanabilmek için)
namespace["np"] = np
namespace["pd"] = pd
namespace["stats"] = stats
try:
return eval(expr, namespace) # Python'da ifadeyi dinamik olarak değerlendir
except Exception as e:
raise Exception(f"İfade değerlendirme hatası: {expr}, Hata: {str(e)}")
```
> **Amacı:** `"x + y * 2"`, `"RND() * 10"`, `"LEN(name)"`, `"SUM(array)"` gibi ifadeleri çalıştırır.
> **Güvenlik:** `eval()` tehlikeli olabilir ama bu bir eğitim/deneysel yorumlayıcı olduğu için kabul edilmiştir.
> **Scope Yönetimi:** `scope_name` parametresi, SUB/FUNCTION içindeki `SHARED` değişkenlerin doğru şekilde bağlanmasını sağlar.
---
### ⚙️ **`execute_command(command, scope_name=None)` METODU: Bir Komutu Yorumlar ve Çalıştırır**
Bu metot, programın kalbi. Her satır burada işlenir.
**Her komut bloğu ayrı ayrı analiz edilmiştir.**
#### ❗ **HATA YÖNETİMİ (ON ERROR GOTO, RESUME)**
```python
# Hata Yönetimi
if command_upper.startswith("ON ERROR GOTO"):
match = re.match(r"ON ERROR GOTO\s+(\w+)", command, re.IGNORECASE)
if match:
label = match.group(1)
if label in self.labels:
self.error_handler = self.labels[label] # Hata olduğunda bu satıra git
else:
raise Exception(f"Etiket bulunamadı: {label}")
return None
else:
raise Exception("ON ERROR GOTO komutunda sözdizimi hatası")
if command_upper == "RESUME":
if self.error_handler is not None:
return self.error_handler # Hata işlemeden sonra buraya dön
else:
raise Exception("RESUME için hata işleyicisi tanımlı değil")
```
#### 🔄 **DÖNGÜLER**
##### WHILE...WEND
```python
if command_upper.startswith("WHILE"):
match = re.match(r"WHILE\s+(.+)", command, re.IGNORECASE)
if match:
condition = match.group(1)
self.loop_stack.append({
"start": self.program_counter, # Döngünün başlangıç satırı
"type": "WHILE",
"condition": condition # Koşul ifadesi
})
if not self.evaluate_expression(condition, scope_name):
# Koşul yanlışsa, WEND'e kadar atla
while self.program_counter < len(self.program) and \
self.program[self.program_counter][0].upper() != "WEND":
self.program_counter += 1
return None
else:
raise Exception("WHILE komutunda sözdizimi hatası")
if command_upper == "WEND":
if self.loop_stack and self.loop_stack[-1]["type"] == "WHILE":
loop_info = self.loop_stack[-1]
if self.evaluate_expression(loop_info["condition"], scope_name):
return loop_info["start"] # Başa dön
else:
self.loop_stack.pop() # Döngü bitti
return None
else:
raise Exception("WEND için eşleşen WHILE bulunamadı")
```
##### FOR...NEXT
```python
if command_upper.startswith("FOR"):
match = re.match(r"FOR\s+(\w+)\s*=\s*(.+)\s+TO\s+(.+)(?:\s+STEP\s+(.+))?", command, re.IGNORECASE)
if match:
var_name, start_expr, end_expr, step_expr = match.groups()
start = self.evaluate_expression(start_expr, scope_name)
end = self.evaluate_expression(end_expr, scope_name)
step = self.evaluate_expression(step_expr, scope_name) if step_expr else 1
# Değişkeni mevcut scope'a ata
if var_name in self.global_vars:
self.global_vars[var_name] = start
else:
self.current_scope()[var_name] = start
self.loop_stack.append({
"start": self.program_counter,
"type": "FOR",
"var": var_name,
"end": end,
"step": step
})
return None
else:
raise Exception("FOR komutunda sözdizimi hatası")
if command_upper.startswith("NEXT"):
if self.loop_stack and self.loop_stack[-1]["type"] == "FOR":
loop_info = self.loop_stack[-1]
var_name = loop_info["var"]
current_value = self.global_vars.get(var_name, self.current_scope().get(var_name))
current_value += loop_info["step"]
if var_name in self.global_vars:
self.global_vars[var_name] = current_value
else:
self.current_scope()[var_name] = current_value
# Döngü şartı kontrolü
if (loop_info["step"] > 0 and current_value <= loop_info["end"]) or \
(loop_info["step"] < 0 and current_value >= loop_info["end"]):
return loop_info["start"] # Başa dön
else:
self.loop_stack.pop() # Döngü bitti
return None
else:
raise Exception("NEXT için eşleşen FOR bulunamadı")
```
##### DO...LOOP
```python
if command_upper.startswith("DO"):
match = re.match(r"DO\s+(WHILE|UNTIL)?\s*(.+)?", command, re.IGNORECASE)
if match:
loop_type, condition = match.groups()
self.loop_stack.append({
"start": self.program_counter,
"type": loop_type or "NONE", # NONE: DO...LOOP sonsuz
"condition": condition or "True" # Varsayılan koşul
})
return None
else:
raise Exception("DO komutunda sözdizimi hatası")
if command_upper.startswith("LOOP"):
match = re.match(r"LOOP\s+(WHILE|UNTIL)?\s*(.+)?", command, re.IGNORECASE)
if match and self.loop_stack:
loop_type, condition = match.groups()
loop_info = self.loop_stack[-1]
if loop_type and condition:
cond_result = self.evaluate_expression(condition, scope_name)
if (loop_type == "WHILE" and cond_result) or (loop_type == "UNTIL" and not cond_result):
return loop_info["start"] # Koşul sağlanıyorsa dön
else:
self.loop_stack.pop() # Döngü bitti
elif loop_info["type"] == "WHILE":
if self.evaluate_expression(loop_info["condition"], scope_name):
return loop_info["start"]
else:
self.loop_stack.pop()
elif loop_info["type"] == "UNTIL":
if not self.evaluate_expression(loop_info["condition"], scope_name):
return loop_info["start"]
else:
self.loop_stack.pop()
else:
return loop_info["start"] # DO...LOOP (sonsuz)
return None
else:
raise Exception("LOOP için eşleşen DO bulunamadı")
```
#### 🧩 **SELECT CASE**
```python
if command_upper.startswith("SELECT CASE"):
match = re.match(r"SELECT CASE\s+(.+)", command, re.IGNORECASE)
if match:
expr = match.group(1)
value = self.evaluate_expression(expr, scope_name)
self.select_stack.append({"value": value, "matched": False, "start": self.program_counter})
return None
else:
raise Exception("SELECT CASE komutunda sözdizimi hatası")
if command_upper.startswith("CASE"):
if not self.select_stack:
raise Exception("CASE için eşleşen SELECT CASE bulunamadı")
select_info = self.select_stack[-1]
match = re.match(r"CASE\s+(.+)", command, re.IGNORECASE)
if match:
case_expr = match.group(1)
if case_expr.upper() == "ELSE":
if not select_info["matched"]:
select_info["matched"] = True # Else bloğuna gir
else:
# Zaten bir CASE matched, ELSE'yi atla
while self.program_counter < len(self.program) and \
self.program[self.program_counter][0].upper() != "END SELECT":
self.program_counter += 1
else:
case_value = self.evaluate_expression(case_expr, scope_name)
if select_info["value"] == case_value and not select_info["matched"]:
select_info["matched"] = True # Eşleşme bulundu
else:
# Eşleşme yoksa, CASE veya END SELECT'e kadar atla
while self.program_counter < len(self.program) and \
self.program[self.program_counter][0].upper() not in ("CASE", "END SELECT"):
self.program_counter += 1
return None
else:
raise Exception("CASE komutunda sözdizimi hatası")
if command_upper == "END SELECT":
if self.select_stack:
self.select_stack.pop()
return None
else:
raise Exception("END SELECT için eşleşen SELECT CASE bulunamadı")
```
#### 🧭 **IF...THEN...ELSE...END IF**
```python
if command_upper.startswith("IF"):
match = re.match(r"IF\s+(.+)\s+THEN", command, re.IGNORECASE)
if match:
condition = match.group(1)
cond_result = self.evaluate_expression(condition, scope_name)
self.if_stack.append({"condition": cond_result, "start": self.program_counter, "else_found": False})
if not cond_result:
# Koşul yanlışsa, ELSE veya END IF'e kadar atla
while self.program_counter < len(self.program) and \
self.program[self.program_counter][0].upper() not in ("ELSE", "END IF"):
self.program_counter += 1
return None
else:
raise Exception("IF komutunda sözdizimi hatası")
if command_upper == "ELSE":
if not self.if_stack:
raise Exception("ELSE için eşleşen IF bulunamadı")
if_info = self.if_stack[-1]
if if_info["condition"] or if_info["else_found"]:
# IF doğruydu veya ELSE zaten göründü → END IF'e kadar atla
while self.program_counter < len(self.program) and \
self.program[self.program_counter][0].upper() != "END IF":
self.program_counter += 1
if_info["else_found"] = True # ELSE bulundu
return None
if command_upper == "END IF":
if self.if_stack:
self.if_stack.pop()
return None
else:
raise Exception("END IF için eşleşen IF bulunamadı")
```
#### 📦 **DEĞİŞKEN TANIMLAMA (DEFINT, DEFSNG, DEFDBL, DEFSTR, GLOBAL, DIM, DIM SHARED)**
```python
if command_upper.startswith("DEFINT"):
match = re.match(r"DEFINT\s+(\w+)", command, re.IGNORECASE)
if match:
var_name = match.group(1)
self.current_scope()[var_name] = 0 # Integer varsayılan değeri
return None
else:
raise Exception("DEFINT komutunda sözdizimi hatası")
if command_upper.startswith("DEFSNG"):
match = re.match(r"DEFSNG\s+(\w+)", command, re.IGNORECASE)
if match:
var_name = match.group(1)
self.current_scope()[var_name] = 0.0 # Single
return None
else:
raise Exception("DEFSNG komutunda sözdizimi hatası")
if command_upper.startswith("DEFDBL"):
match = re.match(r"DEFDBL\s+(\w+)", command, re.IGNORECASE)
if match:
var_name = match.group(1)
self.current_scope()[var_name] = 0.0 # Double
return None
else:
raise Exception("DEFDBL komutunda sözdizimi hatası")
if command_upper.startswith("DEFSTR"):
match = re.match(r"DEFSTR\s+(\w+)", command, re.IGNORECASE)
if match:
var_name = match.group(1)
self.current_scope()[var_name] = "" # String
return None
else:
raise Exception("DEFSTR komutunda sözdizimi hatası")
if command_upper.startswith("GLOBAL"):
match = re.match(r"GLOBAL\s+(\w+)\s+AS\s+(\w+)", command, re.IGNORECASE)
if match:
var_name, var_type = match.groups()
# Type_table'dan tipi al, örneğin "INTEGER" → int() → 0
self.global_vars[var_name] = self.type_table.get(var_type, None)()
return None
else:
raise Exception("GLOBAL komutunda sözdizimi hatası")
if command_upper.startswith("DIM SHARED"):
match = re.match(r"DIM SHARED\s+(.+)\s+AS\s+(\w+)", command, re.IGNORECASE)
if match:
scopes, var_type = match.groups()
# Örnek: "DIM SHARED MAIN, SUB1, VARNAME AS INTEGER" → VARNAME şeffaf değişken
var_name = scopes.split(",")[-1].strip()
scope_list = [s.strip() for s in scopes.split(",")[:-1]]
self.shared_vars[var_name] = scope_list # Bu değişken bu scope'larda paylaşılır
return None
else:
raise Exception("DIM SHARED komutunda sözdizimi hatası")
if command_upper.startswith("DIM"):
match = re.match(r"DIM\s+(\w+)\s+AS\s+(\w+)", command, re.IGNORECASE)
if match:
var_name, var_type = match.groups()
if var_type in self.types: # STRUCT/TYPE
# namedtuple örneğini oluştur
self.current_scope()[var_name] = self.types[var_type](*[None for _ in self.types[var_type]._fields])
elif var_type in self.classes: # CLASS instance
self.current_scope()[var_name] = self.classes[var_type]()
elif var_type == "ARRAY":
self.current_scope()[var_name] = np.array([])
elif var_type == "DATAFRAME":
self.current_scope()[var_name] = pd.DataFrame()
elif var_type == "STRING":
self.current_scope()[var_name] = ""
elif var_type in ("INTEGER", "LONG"):
self.current_scope()[var_name] = 0
elif var_type in ("SINGLE", "DOUBLE"):
self.current_scope()[var_name] = 0.0
elif var_type == "BYTE":
self.current_scope()[var_name] = 0
elif var_type == "SHORT":
self.current_scope()[var_name] = 0
elif var_type == "UNSIGNED INTEGER":
self.current_scope()[var_name] = 0
elif var_type == "CHAR":
self.current_scope()[var_name] = ''
elif var_type == "LIST":
self.current_scope()[var_name] = []
elif var_type == "DICT":
self.current_scope()[var_name] = {}
elif var_type == "SET":
self.current_scope()[var_name] = set()
elif var_type == "TUPLE":
self.current_scope()[var_name] = ()
else:
raise Exception(f"Tanımlanamayan veri tipi: {var_type}")
return None
else:
raise Exception("DIM komutunda sözdizimi hatası")
```
#### 💬 **ATAMA VE DEĞİŞKEN YÖNETİMİ (LET, DEĞİŞKEN = İFADE)**
```python
if command_upper.startswith("LET"):
match = re.match(r"LET\s+(\w+)\s*=\s*(.+)", command, re.IGNORECASE)
if match:
var_name, expr = match.groups()
value = self.evaluate_expression(expr, scope_name)
if var_name in self.global_vars:
self.global_vars[var_name] = value
elif var_name in self.shared_vars and (scope_name in self.shared_vars[var_name] or not self.shared_vars[var_name]):
self.shared_vars[var_name] = value
elif var_name in self.current_scope():
self.current_scope()[var_name] = value
else:
raise Exception(f"Tanımlanmamış değişken: {var_name}")
return None
else:
raise Exception("LET komutunda sözdizimi hatası")
# Direkt atama: x = 5
if re.match(r"\w+\s*=\s*.+", command, re.IGNORECASE):
match = re.match(r"(\w+)\s*=\s*(.+)", command, re.IGNORECASE)
if match:
var_name, expr = match.groups()
value = self.evaluate_expression(expr, scope_name)
if var_name in self.global_vars:
self.global_vars[var_name] = value
elif var_name in self.shared_vars and (scope_name in self.shared_vars[var_name] or not self.shared_vars[var_name]):
self.shared_vars[var_name] = value
elif var_name in self.current_scope():
self.current_scope()[var_name] = value
else:
raise Exception(f"Tanımlanmamış değişken: {var_name}")
return None
```
#### 🖨️ **GİRİŞ/ÇIKIŞ (PRINT, INPUT, LINE INPUT, WRITE)**
```python
if command_upper.startswith("PRINT"):
print_str = command[5:].strip()
parts = re.split(r'([;,])', print_str) # , ve ; ile böl
output = ""
for j in range(0, len(parts), 2):
arg = parts[j].strip()
if arg:
value = self.evaluate_expression(arg, scope_name)
output += str(value)
if j + 1 < len(parts) and parts[j+1] == ',':
output += " "
if print_str.strip().endswith(';'):
print(output, end='') # Satır sonu olmadan yaz
else:
print(output) # Satır sonu ile yaz
return None
if command_upper.startswith("INPUT"):
match = re.match(r"INPUT\s+\"(.+)\"?,\s*(\w+)", command, re.IGNORECASE)
if match:
prompt, var_name = match.groups()
if prompt:
value = input(prompt + " ")
else:
value = input("> ")
self.current_scope()[var_name] = value
return None
else:
raise Exception("INPUT komutunda sözdizimi hatası")
if command_upper.startswith("LINE INPUT"):
match = re.match(r"LINE INPUT\s+\"(.+)\"?,\s*(\w+)", command, re.IGNORECASE)
if match:
prompt, var_name = match.groups()
if prompt:
value = input(prompt + " ")
else:
value = input("> ")
self.current_scope()[var_name] = value
return None
else:
raise Exception("LINE INPUT komutunda sözdizimi hatası")
if command_upper.startswith("WRITE"):
match = re.match(r"WRITE\s+(.+)", command, re.IGNORECASE)
if match:
expr = match.group(1)
value = self.evaluate_expression(expr, scope_name)
# String ise çift tırnakla, değilse normal yaz
print(f'"{value}"' if isinstance(value, str) else value)
return None
else:
raise Exception("WRITE komutunda sözdizimi hatası")
```
#### 🔄 **ALT PROGRAMLAR (GOTO, GOSUB, RETURN, CALL)**
```python
if command_upper.startswith("GOTO"):
match = re.match(r"GOTO\s+(\w+)", command, re.IGNORECASE)
if match:
label = match.group(1)
if label in self.labels:
return self.labels[label] # Etikete git
else:
raise Exception(f"Etiket bulunamadı: {label}")
else:
raise Exception("GOTO komutunda sözdizimi hatası")
if command_upper.startswith("GOSUB"):
match = re.match(r"GOSUB\s+(\w+)", command, re.IGNORECASE)
if match:
label = match.group(1)
if label in self.labels:
self.call_stack.append(self.program_counter) # Geri dönecek adres
return self.labels[label] # Etiket satırına git
else:
raise Exception(f"Etiket bulunamadı: {label}")
else:
raise Exception("GOSUB komutunda sözdizimi hatası")
if command_upper == "RETURN":
if self.call_stack:
return self.call_stack.pop() # GOSUB'dan dönen adres
else:
raise Exception("RETURN için eşleşen GOSUB bulunamadı")
if command_upper.startswith("CALL"):
match = re.match(r"CALL\s+(\w+)", command, re.IGNORECASE)
if match:
sub_name = match.group(1)
if sub_name in self.subs:
self.call_stack.append(self.program_counter)
self.local_scopes.append({}) # Yeni scope aç
return self.subs[sub_name] # SUB'un başlangıç satırına git
else:
raise Exception(f"Alt program bulunamadı: {sub_name}")
else:
raise Exception("CALL komutunda sözdizimi hatası")
```
#### 📌 **FONKSİYON ÇAĞRILARI (FUNC(...))**
```python
if re.match(r"\w+\s*\(.+\)", command, re.IGNORECASE):
match = re.match(r"(\w+)\s*\((.+)\)", command, re.IGNORECASE)
if match:
func_name, args_str = match.groups()
if func_name in self.functions:
# Kullanıcı tanımlı fonksiyon
self.call_stack.append(self.program_counter)
self.local_scopes.append({})
return self.functions[func_name] # Fonksiyonun başlangıç satırına git
elif func_name in self.function_table:
# Built-in fonksiyon: "MID$("hello",1,3)" → eval("('hello',1,3)")
args_tuple = self.evaluate_expression(f"({args_str})", scope_name)
# args_tuple bir tuple olmalı: ('hello', 1, 3)
return self.function_table[func_name](*args_tuple) # Fonksiyonu çağır ve sonucu döndür
else:
raise Exception(f"Fonksiyon bulunamadı: {func_name}")
else:
raise Exception("Fonksiyon çağrısında sözdizimi hatası")
```
#### 📁 **DOSYA İŞLEMLERİ (OPEN, PRINT#, INPUT#, CLOSE, KILL, NAME, FILES, CHDIR, MKDIR, RMDIR)**
```python
if command_upper.startswith("OPEN"):
if "FOR ISAM" not in command_upper:
match = re.match(r"OPEN\s+\"(.+)\"\s+FOR\s+(INPUT|OUTPUT|APPEND|BINARY)\s+AS\s+#(\d+)", command, re.IGNORECASE)
if match:
file_path, mode, file_num = match.groups()
mode_map = {"INPUT": "r", "OUTPUT": "w", "APPEND": "a", "BINARY": "rb+"}
self.file_handles[int(file_num)] = open(file_path, mode_map[mode])
return None
else:
raise Exception("OPEN komutunda sözdizimi hatası")
if command_upper.startswith("PRINT #"):
match = re.match(r"PRINT\s+#(\d+),\s*(.+)", command, re.IGNORECASE)
if match:
file_num, data = match.groups()
file = self.file_handles.get(int(file_num))
if file:
file.write(str(self.evaluate_expression(data, scope_name)) + "\n")
file.flush() # Hemen yaz
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("PRINT # komutunda sözdizimi hatası")
if command_upper.startswith("INPUT #"):
match = re.match(r"INPUT\s+#(\d+),\s*(\w+)", command, re.IGNORECASE)
if match:
file_num, var_name = match.groups()
file = self.file_handles.get(int(file_num))
if file:
self.current_scope()[var_name] = file.readline().strip()
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("INPUT # komutunda sözdizimi hatası")
if command_upper.startswith("LINE INPUT #"):
# Aynı(INPUT #) ama tam satır okur
match = re.match(r"LINE INPUT\s+#(\d+),\s*(\w+)", command, re.IGNORECASE)
if match:
file_num, var_name = match.groups()
file = self.file_handles.get(int(file_num))
if file:
self.current_scope()[var_name] = file.readline().strip()
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("LINE INPUT # komutunda sözdizimi hatası")
if command_upper.startswith("SEEK"):
match = re.match(r"SEEK\s+#(\d+),\s*(.+)", command, re.IGNORECASE)
if match:
file_num, position = match.groups()
file = self.file_handles.get(int(file_num))
if file:
file.seek(self.evaluate_expression(position, scope_name))
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("SEEK komutunda sözdizimi hatası")
if command_upper.startswith("GET #"):
match = re.match(r"GET\s+#(\d+),\s*(.+),\s*(\w+)", command, re.IGNORECASE)
if match:
file_num, position, var_name = match.groups()
file = self.file_handles.get(int(file_num))
if file:
file.seek(self.evaluate_expression(position, scope_name))
self.current_scope()[var_name] = file.read(1) # Sadece 1 bayt oku
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("GET # komutunda sözdizimi hatası")
if command_upper.startswith("PUT #"):
match = re.match(r"PUT\s+#(\d+),\s*(.+),\s*(.+)", command, re.IGNORECASE)
if match:
file_num, position, data = match.groups()
file = self.file_handles.get(int(file_num))
if file:
file.seek(self.evaluate_expression(position, scope_name))
file.write(str(self.evaluate_expression(data, scope_name)))
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("PUT # komutunda sözdizimi hatası")
if command_upper.startswith("CLOSE"):
match = re.match(r"CLOSE\s+#(\d+)", command, re.IGNORECASE)
if match:
file_num = match.group(1)
num = int(file_num)
if num in self.file_handles:
self.file_handles[num].close()
del self.file_handles[num]
elif num in self.db_connections:
self.db_connections[num].close()
del self.db_connections[num]
else:
raise Exception(f"Kapatılacak dosya #{file_num} bulunamadı")
return None
else:
raise Exception("CLOSE komutunda sözdizimi hatası")
if command_upper.startswith("KILL"):
match = re.match(r"KILL\s+\"(.+)\"", command, re.IGNORECASE)
if match:
file_name = match.group(1)
os.remove(file_name)
return None
else:
raise Exception("KILL komutunda sözdizimi hatası")
if command_upper.startswith("NAME"):
match = re.match(r"NAME\s+\"(.+)\"\s+AS\s+\"(.+)\"", command, re.IGNORECASE)
if match:
old_name, new_name = match.groups()
os.rename(old_name, new_name)
return None
else:
raise Exception("NAME komutunda sözdizimi hatası")
if command_upper.startswith("FILES"):
match = re.match(r"FILES\s+\"(.+)\"", command, re.IGNORECASE)
if match:
pattern = match.group(1)
print("\n".join(os.listdir(pattern))) # Belirtilen dizindeki dosyaları yazdır
return None
else:
raise Exception("FILES komutunda sözdizimi hatası")
if command_upper.startswith("CHDIR"):
match = re.match(r"CHDIR\s+\"(.+)\"", command, re.IGNORECASE)
if match:
path = match.group(1)
os.chdir(path)
return None
else:
raise Exception("CHDIR komutunda sözdizimi hatası")
if command_upper.startswith("MKDIR"):
match = re.match(r"MKDIR\s+\"(.+)\"", command, re.IGNORECASE)
if match:
path = match.group(1)
os.mkdir(path)
return None
else:
raise Exception("MKDIR komutunda sözdizimi hatası")
if command_upper.startswith("RMDIR"):
match = re.match(r"RMDIR\s+\"(.+)\"", command, re.IGNORECASE)
if match:
path = match.group(1)
os.rmdir(path)
return None
else:
raise Exception("RMDIR komutunda sözdizimi hatası")
```
#### 🗄️ **VERİTABANI İŞLEMLERİ (OPEN FOR ISAM, DEFINE TABLE, GET, PUT, DELETE, SELECT, BEGIN TRANSACTION, COMMIT, ROLLBACK, INDEX)**
```python
if command_upper.startswith("OPEN") and "FOR ISAM" in command_upper:
match = re.match(r"OPEN\s+\"(.+)\"\s+FOR\s+ISAM\s+AS\s+#(\d+)", command, re.IGNORECASE)
if match:
db_file, file_num = match.groups()
conn = sqlite3.connect(db_file)
self.db_connections[int(file_num)] = conn
self.transaction_active[int(file_num)] = False # Başlangıçta transaksiyon kapalı
return None
else:
raise Exception("OPEN FOR ISAM komutunda sözdizimi hatası")
if command_upper.startswith("DEFINE TABLE"):
match = re.match(r"DEFINE TABLE\s+(\w+)\s*\((.+)\)", command, re.IGNORECASE)
if match:
table_name, fields = match.groups()
fields = [f.strip() for f in fields.split(",")]
columns = []
for field in fields:
match_field = re.match(r"(\w+)\s+AS\s+(\w+)(?:\s+CHECK\((.+)\))?", field, re.IGNORECASE)
if match_field:
col_name, col_type, constraint = match_field.groups()
sql_type = {"STRING": "TEXT", "INTEGER": "INTEGER", "DOUBLE": "REAL"}.get(col_type.upper(), "TEXT")
column_def = f"{col_name} {sql_type}"
if constraint:
column_def += f" CHECK({constraint})"
columns.append(column_def)
else:
raise Exception(f"Alan tanımı hatası: {field}")
sql = f"CREATE TABLE IF NOT EXISTS {table_name} ({', '.join(columns)})"
for conn in self.db_connections.values(): # Tüm bağlantılara uygula
conn.execute(sql)
conn.commit()
return None
else:
raise Exception("DEFINE TABLE komutunda sözdizimi hatası")
if command_upper.startswith("PUT"):
match = re.match(r"PUT\s+#(\d+),\s*(AUTOKEY|\w+),\s*(.+)", command, re.IGNORECASE)
if match:
file_num, key, value = match.groups()
conn = self.db_connections.get(int(file_num))
if conn:
if key.upper() == "AUTOKEY":
conn.execute("INSERT INTO data (value) VALUES (?)", (value,))
else:
conn.execute("INSERT OR REPLACE INTO data (key, value) VALUES (?, ?)", (key, value))
if not self.transaction_active.get(int(file_num), False):
conn.commit()
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("PUT komutunda sözdizimi hatası")
if command_upper.startswith("GET"):
match = re.match(r"GET\s+#(\d+),\s*(\w+),\s*(\w+)", command, re.IGNORECASE)
if match:
file_num, key, var_name = match.groups()
conn = self.db_connections.get(int(file_num))
if conn:
cursor = conn.execute("SELECT value FROM data WHERE key = ?", (key,))
result = cursor.fetchone()
self.current_scope()[var_name] = result[0] if result else None
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("GET komutunda sözdizimi hatası")
if command_upper.startswith("DELETE"):
match = re.match(r"DELETE\s+#(\d+),\s*(\w+)", command, re.IGNORECASE)
if match:
file_num, key = match.groups()
conn = self.db_connections.get(int(file_num))
if conn:
conn.execute("DELETE FROM data WHERE key = ?", (key,))
if not self.transaction_active.get(int(file_num), False):
conn.commit()
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("DELETE komutunda sözdizimi hatası")
if command_upper.startswith("SELECT"):
match = re.match(r"SELECT\s+(.+)\s+FROM\s+(\w+)\s*(?:WHERE\s+(.+))?\s+INTO\s+(\w+)", command, re.IGNORECASE)
if match:
columns, table, where, var_name = match.groups()
for num, conn in self.db_connections.items():
query = f"SELECT {columns} FROM {table}"
if where:
query += f" WHERE {where}"
df = pd.read_sql_query(query, conn)
self.current_scope()[var_name] = df
break
return None
else:
raise Exception("SELECT komutunda sözdizimi hatası")
if command_upper == "BEGIN TRANSACTION":
match = re.match(r"BEGIN TRANSACTION\s+#(\d+)", command, re.IGNORECASE)
if match:
file_num = int(match.group(1))
if file_num in self.db_connections:
self.transaction_active[file_num] = True
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("BEGIN TRANSACTION komutunda sözdizimi hatası")
if command_upper == "COMMIT":
match = re.match(r"COMMIT\s+#(\d+)", command, re.IGNORECASE)
if match:
file_num = int(match.group(1))
if file_num in self.db_connections:
self.db_connections[file_num].commit()
self.transaction_active[file_num] = False
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("COMMIT komutunda sözdizimi hatası")
if command_upper == "ROLLBACK":
match = re.match(r"ROLLBACK\s+#(\d+)", command, re.IGNORECASE)
if match:
file_num = int(match.group(1))
if file_num in self.db_connections:
self.db_connections[file_num].rollback()
self.transaction_active[file_num] = False
else:
raise Exception(f"Dosya #{file_num} açık değil")
return None
else:
raise Exception("ROLLBACK komutunda sözdizimi hatası")
if command_upper.startswith("INDEX"):
match = re.match(r"INDEX\s+(\w+)\s+ON\s+(\w+)", command, re.IGNORECASE)
if match:
table, column = match.groups()
for conn in self.db_connections.values():
conn.execute(f"CREATE INDEX idx_{column} ON {table} ({column})")
conn.commit()
return None
else:
raise Exception("INDEX komutunda sözdizimi hatası")
```
#### 🧠 **DİĞER KOMUTLAR (RANDOMIZE, READ, RESTORE, LOAD, SAVE, RUN, END)**
```python
if command_upper.startswith("RANDOMIZE"):
random.seed() # Rastgele sayı üreticiyi sistem saatiyle başlat
return None
if command_upper.startswith("READ"):
match = re.match(r"READ\s+(\w+)", command, re.IGNORECASE)
if match:
var_name = match.group(1)
if self.data_pointer < len(self.data_list):
self.current_scope()[var_name] = self.data_list[self.data_pointer]
self.data_pointer += 1
else:
raise Exception("DATA listesi tükendi")
return None
else:
raise Exception("READ komutunda sözdizimi hatası")
if command_upper.startswith("RESTORE"):
self.data_pointer = 0 # DATA listesini baştan okumaya hazırla
return None
if command_upper.startswith("LOAD"):
match = re.match(r"LOAD\s+\"(.+)\"", command, re.IGNORECASE)
if match:
file_name = match.group(1)
if file_name.endswith(".basX"):
self.load_program(file_name) # Yeni programı yükle
return 0 # Yeni programın ilk satırına git
else:
raise Exception("Dosya uzantısı .basX olmalı")
else:
raise Exception("LOAD komutunda sözdizimi hatası")
if command_upper.startswith("SAVE"):
match = re.match(r"SAVE\s+\"(.+)\"", command, re.IGNORECASE)
if match:
file_name = match.group(1)
with open(file_name, "w") as f:
f.write("\n".join([line[0] for line in self.program]))
return None
else:
raise Exception("SAVE komutunda sözdizimi hatası")
if command_upper == "RUN":
self.run() # Programı yeniden çalıştır
return None
if command_upper == "END":
self.running = False # Yürütme durdurulur
return None
```
#### ⚠️ **GENEL HATA YÖNETİMİ**
```python
raise Exception(f"Bilinmeyen komut: {command}")
```
> **Not:** Yukarıdaki tüm komutlar `try-except` bloğu içinde çağrılır. Eğer hata oluşursa:
> - Hata loglanır (`logging.error`)
> - Konsola yazılır
> - `error_handler` varsa, ona atlanır
> - Yoksa program sonlandırılır
---
### 🚀 **`load_program(file_name)` METODU: .basX Dosyasını Okur ve Parse Eder**
```python
def load_program(self, file_name):
try:
with open(file_name, "r") as f:
code = f.read()
self.parse_program(code) # Kodu parse et
except Exception as e:
error_msg = f"Dosya yükleme hatası: {str(e)}"
print(error_msg)
logging.error(error_msg)
```
> **Amacı:** `.basX` dosyasını okuyup, `parse_program` ile program listesine dönüştürür.
---
### ▶️ **`run()` METODU: Programı Yürütür**
```python
def run(self):
self.running = True
self.program_counter = 0 # Satır sayacını sıfırla
while self.running and self.program_counter < len(self.program):
command, scope = self.program[self.program_counter]
if self.debug_mode:
print(f"Satır {self.program_counter + 1}: {command}")
input("Devam etmek için Enter'a basın...") # Debug modunda dur
try:
next_line = self.execute_command(command, scope) # Komutu çalıştır
if next_line is not None:
self.program_counter = next_line # GOTO, GOSUB, LOOP gibi yönlendirme
else:
self.program_counter += 1 # Normalde bir sonraki satır
# SUB/FUNCTION sonu geldiyse, scope'u kapat ve geri dön
if scope and self.program_counter < len(self.program) and self.program[self.program_counter][0].upper() in ("END SUB", "END FUNCTION"):
if self.call_stack:
self.local_scopes.pop() # Yerel scope kapat
self.program_counter = self.call_stack.pop() # GOSUB/CALL'dan dön
else:
self.running = False # Hata durumu?
except Exception as e:
error_msg = f"Çalıştırma hatası: {str(e)}"
print(error_msg)
logging.error(error_msg)
self.running = False # Hata durumunda dur
# Temizlik: Bağlantıları ve dosyaları kapat
for conn in self.db_connections.values():
conn.close()
for file in self.file_handles.values():
file.close()
```
> **Program döngüsü burada çalışır.**
> `program_counter` her seferinde güncellenir.
> `next_line` bir yönlendirme (goto, gosub, döngü başı) ise, `program_counter` ona atlanır.
> `END` veya hata durumunda döngü sonlanır.
---
### 🎯 **ANA PROGRAM (main)**
```python
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="pdsX Interpreter")
parser.add_argument("file", nargs='?', help="The .basX file to run")
args = parser.parse_args()
interpreter = pdsXInterpreter()
if args.file:
if args.file.endswith(".basX"):
interpreter.load_program(args.file)
interpreter.run()
else:
print("Hata: Dosya uzantısı .basX olmalı")
else:
print("Kullanım: pdsX dosya_adi.basX")
```
> **Komut satırından `.basX` dosyası verilirse**, program yüklenir ve çalıştırılır.
> Aksi halde kullanım bilgisi verilir.
---
## ✅ **GENEL DEĞERLENDİRME: Güçlü ve Zayıf Yanlar**
### ✅ **GÜÇLÜ YANLAR**
- **Kapsamlı:** BASIC'in büyük bir kısmını (dosya, veritabanı, grafik simülasyonu, istatistik) destekler.
- **Modern Python teknolojileriyle entegre:** `numpy`, `pandas`, `sqlite3` kullanımı çok güçlü.
- **Yapısal:** `stack` kullanarak SCOPE, GOSUB, DÖNGÜ yönetimi akıllıca yapılmış.
- **Loglama ve hata yönetimi** profesyonel düzeyde.
- **Yorum satırları ve açıklama** çok iyi.
### ⚠️ **ZAYIF YANLAR / HATALAR**
1. **`CLASS` metotları çalışmaz!**
```python
class_methods[current_class][method_name] = lambda self, *args: self._vars.get(method_name, lambda: None)(*args)
```
→ `_vars[method_name]` hiçbir zaman atanmaz. Bu bir **bug**.
**Düzeltme:** Metotları doğrudan sınıfın `__dict__`'ine eklemeli veya `setattr` ile tanımlamalı.
2. **`EOF()` fonksiyonu** `self.file_handles[n].eof()` → Python'da `file.eof()` yoktur!
**Düzeltme:** `file.tell() >= os.path.getsize(file.name)` gibi bir kontrol yapılmalı.
3. **`INKEY$`** `input()[:1]` → Klavyeden bekler. Gerçek BASIC'te beklemez.
**Düzeltme:** `msvcrt.getch()` gibi platform-spesifik çözüm gerekir.
4. **`MKI$/MKS$/MKD$`** `decode('latin1')` → Binary veriyi stringe çevirir ama bu veri genellikle binary dosyalarda saklanır.
**Düzeltme:** `bytes` olarak saklamak daha doğru olur.
5. **`EXECUTE_COMMAND` çok uzun** → 700+ satır. Bölünmelidir (örneğin: `handle_loop`, `handle_files`, `handle_db`).
6. **`DATA` listesi global** → SUB/FUNCTION içinde `READ` kullandığında aynı listeyi kullanır. Bu doğru, ama dokümantasyon eksik.
---
## 🏁 SONUÇ
Bu program, **BASIC dilinin modern bir Python tabanlı simülasyonudur**.
**Amaç:** Eğitim, eski BASIC kodlarını çalıştırmak, veya bir domain-specific language (DSL) geliştirmektir.
**Geliştirme potansiyeli çok yüksek**, ancak bazı kritik hatalar düzeltildiğinde, gerçekten güçlü bir araç haline gelebilir.
> **Öneri:** Kodu modüller halinde bölmek (parser.py, executor.py, db.py, io.py),
> `CLASS` sistemini düzeltmek,
> `EOF`, `INKEY$`, `POKE`, `PEEK` gibi fonksiyonları tamamlamak,
> `DEBUG` moduna `step into` desteği eklemek.
**İyi bir çalışma!** 👏

Yorumlar