2025-07-22 15:56:40 +02:00
|
|
|
class Interpreter:
|
|
|
|
|
def __init__(self, ast):
|
|
|
|
|
self.ast = ast
|
2025-07-22 16:46:57 +02:00
|
|
|
self.scope_stack = [{}]
|
2025-07-22 16:27:04 +02:00
|
|
|
self.functions = {}
|
2025-07-22 15:56:40 +02:00
|
|
|
|
2025-07-22 16:46:57 +02:00
|
|
|
@property
|
|
|
|
|
def current_scope(self):
|
|
|
|
|
return self.scope_stack[-1]
|
|
|
|
|
|
2025-07-22 15:56:40 +02:00
|
|
|
def interpret(self):
|
|
|
|
|
for node in self.ast:
|
|
|
|
|
self.visit(node)
|
|
|
|
|
|
|
|
|
|
def visit(self, node):
|
|
|
|
|
method_name = f'visit_{type(node).__name__}'
|
|
|
|
|
method = getattr(self, method_name, self.no_visit_method)
|
|
|
|
|
return method(node)
|
|
|
|
|
|
|
|
|
|
def no_visit_method(self, node):
|
|
|
|
|
raise Exception(f'No visit_{type(node).__name__} method defined')
|
|
|
|
|
|
2025-07-22 16:12:23 +02:00
|
|
|
def visit_Number(self, node):
|
|
|
|
|
return node.value
|
|
|
|
|
|
2025-07-22 16:17:32 +02:00
|
|
|
def visit_String(self, node):
|
|
|
|
|
return node.value
|
|
|
|
|
|
2025-07-22 16:52:55 +02:00
|
|
|
def visit_Boolean(self, node):
|
|
|
|
|
return node.value
|
|
|
|
|
|
2025-07-22 16:12:23 +02:00
|
|
|
def visit_BinOp(self, node):
|
2025-07-22 16:17:32 +02:00
|
|
|
left_val = self.visit(node.left)
|
|
|
|
|
right_val = self.visit(node.right)
|
2025-07-22 16:12:23 +02:00
|
|
|
if node.op.type == 'PLUS':
|
2025-07-22 16:17:32 +02:00
|
|
|
return left_val + right_val
|
2025-07-22 16:12:23 +02:00
|
|
|
elif node.op.type == 'MINUS':
|
2025-07-22 16:17:32 +02:00
|
|
|
return left_val - right_val
|
2025-07-22 16:12:23 +02:00
|
|
|
elif node.op.type == 'MUL':
|
2025-07-22 16:17:32 +02:00
|
|
|
return left_val * right_val
|
2025-07-22 16:12:23 +02:00
|
|
|
elif node.op.type == 'DIV':
|
2025-07-22 16:17:32 +02:00
|
|
|
return left_val / right_val
|
|
|
|
|
|
|
|
|
|
def visit_Comparison(self, node):
|
|
|
|
|
left_val = self.visit(node.left)
|
|
|
|
|
right_val = self.visit(node.right)
|
|
|
|
|
if node.op.type == 'EQ':
|
|
|
|
|
return left_val == right_val
|
|
|
|
|
elif node.op.type == 'NEQ':
|
|
|
|
|
return left_val != right_val
|
|
|
|
|
elif node.op.type == 'GT':
|
|
|
|
|
return left_val > right_val
|
|
|
|
|
elif node.op.type == 'GTE':
|
|
|
|
|
return left_val >= right_val
|
|
|
|
|
elif node.op.type == 'LT':
|
|
|
|
|
return left_val < right_val
|
|
|
|
|
elif node.op.type == 'LTE':
|
|
|
|
|
return left_val <= right_val
|
2025-07-22 16:12:23 +02:00
|
|
|
|
2025-07-22 15:56:40 +02:00
|
|
|
def visit_Print(self, node):
|
2025-07-22 16:12:23 +02:00
|
|
|
value_to_print = self.visit(node.value)
|
|
|
|
|
print(value_to_print)
|
|
|
|
|
|
|
|
|
|
def visit_Assign(self, node):
|
|
|
|
|
var_name = node.left.value
|
|
|
|
|
value = self.visit(node.right)
|
2025-07-22 16:46:57 +02:00
|
|
|
self.current_scope[var_name] = value
|
2025-07-22 16:12:23 +02:00
|
|
|
|
|
|
|
|
def visit_Variable(self, node):
|
|
|
|
|
var_name = node.value
|
2025-07-22 16:46:57 +02:00
|
|
|
# Search up the scope stack for the variable
|
|
|
|
|
for scope in reversed(self.scope_stack):
|
|
|
|
|
if var_name in scope:
|
|
|
|
|
return scope[var_name]
|
|
|
|
|
raise NameError(f"name '{var_name}' is not defined")
|
2025-07-22 16:17:32 +02:00
|
|
|
|
|
|
|
|
def visit_Block(self, node):
|
|
|
|
|
for statement in node.statements:
|
|
|
|
|
self.visit(statement)
|
|
|
|
|
|
|
|
|
|
def visit_IfStatement(self, node):
|
|
|
|
|
condition_result = self.visit(node.condition)
|
|
|
|
|
if condition_result:
|
|
|
|
|
self.visit(node.if_block)
|
|
|
|
|
elif node.else_block:
|
|
|
|
|
self.visit(node.else_block)
|
2025-07-22 16:22:49 +02:00
|
|
|
|
|
|
|
|
def visit_WhileStatement(self, node):
|
|
|
|
|
while self.visit(node.condition):
|
|
|
|
|
self.visit(node.body)
|
2025-07-22 16:27:04 +02:00
|
|
|
|
|
|
|
|
def visit_FunctionDefinition(self, node):
|
|
|
|
|
self.functions[node.name] = {
|
|
|
|
|
'parameters': node.parameters,
|
|
|
|
|
'body': node.body
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def visit_FunctionCall(self, node):
|
|
|
|
|
func_name = node.name
|
|
|
|
|
if func_name not in self.functions:
|
|
|
|
|
raise NameError(f"Function '{func_name}' is not defined")
|
|
|
|
|
|
|
|
|
|
func_info = self.functions[func_name]
|
2025-07-22 16:46:57 +02:00
|
|
|
|
|
|
|
|
# Evaluate arguments
|
|
|
|
|
evaluated_arguments = [self.visit(arg) for arg in node.arguments]
|
|
|
|
|
|
|
|
|
|
# Create a new scope for the function call
|
|
|
|
|
new_scope = {}
|
|
|
|
|
for i, param_name in enumerate(func_info['parameters']):
|
|
|
|
|
new_scope[param_name] = evaluated_arguments[i]
|
|
|
|
|
|
|
|
|
|
self.scope_stack.append(new_scope)
|
2025-07-22 16:27:04 +02:00
|
|
|
|
|
|
|
|
# Execute function body
|
|
|
|
|
try:
|
|
|
|
|
self.visit(func_info['body'])
|
|
|
|
|
except ReturnValue as e:
|
2025-07-22 16:46:57 +02:00
|
|
|
self.scope_stack.pop() # Pop the function scope
|
2025-07-22 16:27:04 +02:00
|
|
|
return e.value
|
2025-07-22 16:46:57 +02:00
|
|
|
finally:
|
|
|
|
|
# Ensure scope is popped even if no return or an error occurs
|
|
|
|
|
if len(self.scope_stack) > 1: # Don't pop global scope
|
|
|
|
|
self.scope_stack.pop()
|
2025-07-22 16:27:04 +02:00
|
|
|
|
|
|
|
|
def visit_ReturnStatement(self, node):
|
|
|
|
|
raise ReturnValue(self.visit(node.value))
|
|
|
|
|
|
|
|
|
|
class ReturnValue(Exception):
|
|
|
|
|
def __init__(self, value):
|
|
|
|
|
self.value = value
|