C interpriter

This commit is contained in:
OusmBlueNinja 2025-04-11 15:59:55 -05:00
parent 9fd2bfefec
commit f124ba5585
4 changed files with 178 additions and 1 deletions

View File

@ -5,6 +5,6 @@ Collapsed=0
[Window][Debug]
Pos=12,41
Size=336,392
Size=778,630
Collapsed=0

View File

@ -0,0 +1,14 @@
#include "other.h"
void main() {
int a = 5;
printf("%d", a);
foo();
int i = 10;
while (i != 0)
{
printf("%d", i);
i -= 1;
}
}

View File

@ -0,0 +1,159 @@
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()

View File

@ -0,0 +1,4 @@
void foo() {
int x = 10;
printf("%d", x);
}