160 lines
4.6 KiB
Python
160 lines
4.6 KiB
Python
import sys
|
|
import os
|
|
import re
|
|
|
|
# ──────── Interpreter State ────────
|
|
|
|
class Frame:
|
|
def __init__(self, function_name, locals=None):
|
|
self.function_name = function_name
|
|
self.locals = locals or {}
|
|
|
|
class InterpreterState:
|
|
def __init__(self):
|
|
self.stack = [] # Call stack
|
|
self.functions = {} # name → AST
|
|
|
|
def push_frame(self, name):
|
|
self.stack.append(Frame(name))
|
|
|
|
def pop_frame(self):
|
|
return self.stack.pop()
|
|
|
|
def current_frame(self):
|
|
return self.stack[-1] if self.stack else None
|
|
|
|
def debug_bt(self):
|
|
print("\nBacktrace:")
|
|
for i, frame in enumerate(reversed(self.stack)):
|
|
print(f" #{i} {frame.function_name}")
|
|
print()
|
|
|
|
def debug_vars(self):
|
|
frame = self.current_frame()
|
|
print("\nVariables:")
|
|
if frame:
|
|
for k, v in frame.locals.items():
|
|
print(f" {k} = {v}")
|
|
else:
|
|
print(" No active frame.")
|
|
print()
|
|
|
|
# ──────── C Parser (toy) ────────
|
|
|
|
def parse_c_file(filename, included=None):
|
|
if included is None:
|
|
included = set()
|
|
|
|
abs_path = os.path.abspath(filename)
|
|
if abs_path in included:
|
|
return {}
|
|
|
|
included.add(abs_path)
|
|
|
|
try:
|
|
with open(abs_path) as f:
|
|
lines = f.readlines()
|
|
except FileNotFoundError:
|
|
print(f"Error: File '{filename}' not found.")
|
|
return {}
|
|
|
|
ast = {}
|
|
current_fn = None
|
|
body = []
|
|
|
|
for line in lines:
|
|
line = line.strip()
|
|
if line.startswith("#include"):
|
|
match = re.match(r'#include\s+"(.+?)"', line)
|
|
if match:
|
|
included_ast = parse_c_file(os.path.join(os.path.dirname(abs_path), match.group(1)), included)
|
|
ast.update(included_ast)
|
|
elif line.startswith("void"):
|
|
fn_match = re.match(r'void\s+(\w+)\s*\(\s*\)\s*{', line)
|
|
if fn_match:
|
|
if current_fn:
|
|
ast[current_fn] = body
|
|
body = []
|
|
current_fn = fn_match.group(1)
|
|
elif line == "}":
|
|
if current_fn:
|
|
ast[current_fn] = body
|
|
current_fn = None
|
|
body = []
|
|
elif re.match(r'int\s+\w+\s*=\s*\d+;', line):
|
|
var_match = re.match(r'int\s+(\w+)\s*=\s*(\d+);', line)
|
|
body.append(('decl', var_match.group(1), int(var_match.group(2))))
|
|
elif re.match(r'printf\(".*",\s*\w+\);', line):
|
|
print_match = re.match(r'printf\(".*",\s*(\w+)\);', line)
|
|
body.append(('print', print_match.group(1)))
|
|
elif re.match(r'\w+\(\);', line):
|
|
call_match = re.match(r'(\w+)\(\);', line)
|
|
body.append(('call', call_match.group(1)))
|
|
return ast
|
|
|
|
# ──────── Interpreter Executor ────────
|
|
|
|
class Interpreter:
|
|
def __init__(self, state):
|
|
self.state = state
|
|
|
|
def run_function(self, name):
|
|
if name not in self.state.functions:
|
|
print(f"Function '{name}' not defined.")
|
|
return
|
|
self.state.push_frame(name)
|
|
body = self.state.functions[name]
|
|
for stmt in body:
|
|
self.exec_stmt(stmt)
|
|
self.state.pop_frame()
|
|
|
|
def exec_stmt(self, stmt):
|
|
frame = self.state.current_frame()
|
|
if stmt[0] == 'decl':
|
|
_, var, val = stmt
|
|
frame.locals[var] = val
|
|
elif stmt[0] == 'print':
|
|
var = stmt[1]
|
|
print(frame.locals.get(var, 'undefined'))
|
|
elif stmt[0] == 'call':
|
|
self.run_function(stmt[1])
|
|
|
|
# ──────── Debugger CLI ────────
|
|
|
|
def debugger_loop(state, interp):
|
|
print("Mini C Interpreter Debugger")
|
|
print("Commands: run, bt, vars, quit")
|
|
while True:
|
|
cmd = input("(debug) ").strip()
|
|
if cmd == "run":
|
|
interp.run_function("main")
|
|
elif cmd == "bt":
|
|
state.debug_bt()
|
|
elif cmd == "vars":
|
|
state.debug_vars()
|
|
elif cmd == "quit":
|
|
break
|
|
else:
|
|
print("Unknown command. Use: run, bt, vars, quit")
|
|
|
|
# ──────── Entry Point ────────
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
print("Usage: python c_interpreter.py <file.c>")
|
|
return
|
|
|
|
filename = sys.argv[1]
|
|
ast = parse_c_file(filename)
|
|
if not ast:
|
|
print("No functions parsed.")
|
|
return
|
|
|
|
state = InterpreterState()
|
|
state.functions = ast
|
|
interp = Interpreter(state)
|
|
debugger_loop(state, interp)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|