Skip to content

Instantly share code, notes, and snippets.

@ColinPeppler
Created January 29, 2023 03:28
Show Gist options
  • Save ColinPeppler/8e3dfbd0360b4afaf098de2e44207161 to your computer and use it in GitHub Desktop.
Save ColinPeppler/8e3dfbd0360b4afaf098de2e44207161 to your computer and use it in GitHub Desktop.
Very simple Python interpreter written in Python
# ref: https://www.aosabook.org/en/500L/a-python-interpreter-written-in-python.html
from typing import NamedTuple, Optional, Union, List
from collections import deque
from dataclasses import dataclass
class Instruction(NamedTuple):
"""Represents a single instruction"""
method: str
argument: Optional[Union[str, int]]
@dataclass
class Program:
"""Represents a simple python bytecode program"""
instruction_set: List[Instruction]
data: List[int]
variables: List[str]
class Interpreter:
def __init__(self):
self.stack = deque()
self.symbols = {}
def STORE_NAME(self, name):
val = self.stack.pop()
self.symbols[name] = val
def LOAD_NAME(self, name):
val = self.symbols[name]
self.stack.append(val)
def LOAD_VALUE(self, number):
self.stack.append(number)
def ADD_TWO_VALUES(self):
first_num, second_num = self.stack.pop(), self.stack.pop()
total = first_num + second_num
self.stack.append(total)
def PRINT_ANSWER(self):
answer = self.stack.pop()
print(answer)
def _retrieve_argument(self, method, argument, program):
"""Gets the correct arguments based off the instruction type."""
methods_using_data = "LOAD_VALUE"
methods_using_variables = ("LOAD_NAME", "STORE_NAME")
if method in methods_using_data:
argument = program.data[argument]
elif method in methods_using_variables:
argument = program.variables[argument]
return argument
def run_program(self, program):
instructions = program.instruction_set
for (method, argument) in instructions:
argument = self._retrieve_argument(method, argument, program)
bytecode_method = getattr(self, method) # dynamic lookup
if argument:
bytecode_method(argument)
else:
bytecode_method()
## test it ##
program = Program(
instruction_set=[
("LOAD_VALUE", 0),
("STORE_NAME", 0),
("LOAD_VALUE", 1),
("STORE_NAME", 1),
# ("ADD_TWO_VALUES", None),
("LOAD_NAME", 0),
("LOAD_NAME", 1),
("ADD_TWO_VALUES", None),
("PRINT_ANSWER", None),
],
data=[1, 2],
variables=["a", "b"],
)
interpreter = Interpreter()
interpreter.run_program(program)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment