Created
November 15, 2016 21:39
-
-
Save 10se1ucgo/89cfff8b25a0b7bae40e836be532782a to your computer and use it in GitHub Desktop.
hi this is an interpreter but it's written in python so it's basically cheating right
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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