Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
hi this is an interpreter but it's written in python so it's basically cheating right
import enum
import operator
import shlex
from collections import namedtuple, deque
class TokenType(enum.Enum):
IDENT = 0
NUM = 1
OP = 2
PUNC = 3
EOF = 4
class Op(enum.Enum):
ASSIGN = 0
ADD = operator.add
SUB = operator.sub
MUL = operator.mul
FDIV = operator.floordiv
TDIV = operator.truediv
MOD = operator.mod
POW = operator.pow
class Punc(enum.Enum):
FUNC = 0
LPAREN = 1
RPAREN = 2
Token = namedtuple('Token', ['type', 'value'])
class Interpreter(object):
def __init__(self):
self.globals = {}
self.funcs = {}
self.lex = None
self.token = None
self.next_token = None
self.token_stack = deque()
def get_token(self):
# Use shlex to turn input into Tokens.
# maybe i should write my own lexer : ^ )
if self.token_stack:
return self.token_stack.popleft()
token = self.lex.get_token()
if token == '=':
return Token(TokenType.OP, Op.ASSIGN)
elif token == '+':
return Token(TokenType.OP, Op.ADD)
elif token == '-':
return Token(TokenType.OP, Op.SUB)
elif token == '*':
return Token(TokenType.OP, Op.MUL)
elif token == '/':
# look ahead for double /, signifying floor div.
next = self.lex.get_token()
if next == '/':
return Token(TokenType.OP, Op.FDIV)
self.lex.push_token(next)
return Token(TokenType.OP, Op.TDIV)
elif token == '%':
return Token(TokenType.OP, Op.MOD)
elif token == '(':
return Token(TokenType.PUNC, Punc.LPAREN)
elif token == ')':
return Token(TokenType.PUNC, Punc.RPAREN)
elif token.isdigit():
# look ahead for a floating point value.
dot, num = self.lex.get_token(), self.lex.get_token()
if dot == '.' and num.isdigit:
return Token(TokenType.NUM, float(token + '.' + num)) # kek
self.lex.push_token(num)
self.lex.push_token(dot)
return Token(TokenType.NUM, int(token))
elif token.isalnum():
return Token(TokenType.IDENT, token)
elif token == self.lex.eof:
return Token(TokenType.EOF, self.lex.eof)
else:
raise SyntaxError("Unrecognized token {}".format(token))
def next(self):
self.token = self.next_token
self.next_token = self.get_token()
def format_conds(self, conds):
ret = []
ret.append(str(conds[0]))
for x in conds[1:]:
ret.append(' or {cond}'.format(cond=x))
return ''.join(ret)
def next_if(self, attr='type', *conds, throw=False):
# next_if advances the current Token if the attr of the next token
# matches the attrs inside `conds`.
# If throw is True, it will throw a SyntaxError if the Token is not valid.
token = self.next_token
if not token or getattr(token, attr) not in conds:
if throw:
raise SyntaxError("Invalid syntax, expected {0}, recieved {1} instead".format(self.format_conds(conds), token))
return False
self.next()
return True
def load_var(self):
# Load a variable by name from the global scope.
# If non existant, raises a NameError
value = self.globals.get(self.token.value)
if value is None:
raise NameError('"{x}" is not defined.'.format(x=self.token.value))
return value
def program(self):
# Entry point for the parser.
# If input is empty, return an empty string.
if self.next_if('type', TokenType.EOF):
return ''
value = self.expression()
if self.token and self.token.type != TokenType.IDENT and self.next_token.value == Op.ASSIGN:
raise SyntaxError("Cannot assign to constant")
if not (self.token.type == TokenType.EOF, self.next_if('type', TokenType.EOF)):
raise SyntaxError("Invalid syntax")
return value
def expression(self):
# expression = term [ ('+' | '-') term ]
lvalue = self.term()
# if self.token.type == TokenType.EOF
while self.next_if('value', Op.ADD, Op.SUB):
op = self.token.value.value
rvalue = self.term()
lvalue = op(lvalue, rvalue)
return lvalue
def term(self):
# term = unary [ ('*' | '/' | '//' | '%') unary ]
lvalue = self.unary()
while self.next_if('value', Op.MUL, Op.TDIV, Op.FDIV, Op.MOD):
op = self.token.value.value
rvalue = self.unary()
lvalue = op(lvalue, rvalue)
return lvalue
def unary(self):
# i forgot the EBNF syntax for this
# handle unary +/-
if self.next_if('value', Op.ADD, Op.SUB):
op = self.token.value.value
return op(0, self.unary())
return self.factor()
def factor(self):
# factor = variable | constant | "(" , expression , ")"
lvalue = self.variable()
# handle any constants
if self.next_if('type', TokenType.NUM):
lvalue = self.token.value
# handle any expressions inside parens
if self.next_if('value', Punc.LPAREN):
lvalue = self.expression()
self.next_if('value', Punc.RPAREN, throw=True)
return lvalue
def variable(self):
# handle any variables/assignment
while self.next_if('type', TokenType.IDENT):
lvalue = self.token
if self.next_if('value', Op.ASSIGN):
ret = self.expression()
self.globals[lvalue.value] = ret
return ret
else:
return self.load_var()
if self.next_token and self.next_token.value == Op.ASSIGN:
raise SyntaxError("don't question mark lol")
return ''
def input(self, expr):
# print(expr)
self.lex = shlex.shlex(expr)
self.next()
return self.program()
if __name__ == "__main__":
interp = Interpreter()
while True:
print(interp.input(input("> ")))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment