#!/usr/bin/python import re from sys import exit # class Token # contains type and data values for an expression class Token: def __repr__(self): return self.__str__() def __str__(self): if self.type == self.T_BOOL: if self.data: return ":true:" return ":false:" elif self.type == self.T_ERR: return ":error:" else: return str(self.data) def __init__(self, type, data): self.type = type self.data = data T_BOOL, T_INT, T_STRING, T_NAME, T_ERR, T_PRIM, T_APP = range(7) P_ADD, P_SUB, P_MUL, P_DIV, P_REM, P_NEG, P_BINDS, P_BIND, P_LOAD, P_POP, P_EXC, P_DUP, P_QUIT = range(13) type = None data = None # proper remainders and the like def div_proper(dividend, divisor): quot = dividend / divisor rem = dividend % divisor # push an error onto the stack def push_error(stack): stack.append(Token(Token.T_ERR, False)) # keywordtoken : takes a keyword and turns it into a token # i feel like i should be able to refactor this def keywordtoken(str): if str == "add": return Token(Token.T_PRIM, Token.P_ADD) elif str == "sub": return Token(Token.T_PRIM, Token.P_SUB) elif str == "mul": return Token(Token.T_PRIM, Token.P_MUL) elif str == "div": return Token(Token.T_PRIM, Token.P_DIV) elif str == "rem": return Token(Token.T_PRIM, Token.P_REM) elif str == "neg": return Token(Token.T_PRIM, Token.P_NEG) elif str == "binds": return Token(Token.T_PRIM, Token.P_BINDS) elif str == "bind": return Token(Token.T_PRIM, Token.P_BIND) elif str == "load": return Token(Token.T_PRIM, Token.P_LOAD) elif str == "pop": return Token(Token.T_PRIM, Token.P_POP) elif str == "exc": return Token(Token.T_PRIM, Token.P_EXC) elif str == "dup": return Token(Token.T_PRIM, Token.P_DUP) elif str == "quit": return Token(Token.T_PRIM, Token.P_QUIT) elif str == ":true:": return Token(Token.T_BOOL, True) elif str == ":false:": return Token(Token.T_BOOL, False) elif str == ":error:": return Token(Token.T_ERR, False) # lex : takes a string and turns it into a list of tokens def lex(str): # create token array tokens = [] # scan the string until we run out of tokens while len(str) > 0: # match whitespace m = re.search(r"^\s+", str) if m: str = str[m.end():] # this is where the magic happens. the substring that was just matched is removed continue # match keywords m = re.search(r"^(add|sub|mul|div|rem|neg|binds|bind|load|pop|exc|dup|quit|:true:|:false:|:error:)(\s|$)", str) if m: tokens.append(keywordtoken(m.group(1))) str = str[m.end():] continue; # match numbers m = re.search(r"^(-?\d+)(\s|$)", str) if m: tokens.append(Token(Token.T_INT, int(m.group(1)))) str = str[m.end():] continue; # match strings m = re.search(r"^\"([^\"]+?)\"(\s|$)", str) if m: tokens.append(Token(Token.T_STRING, m.group(1))) str = str[m.end():] continue # match names m = re.search(r"(^[a-zA-Z][a-zA-Z0-9]*)(\s|$)", str) if m: tokens.append(Token(Token.T_NAME, m.group(1))) str = str[m.end():] continue # nothing matched, syntax error; throw an error and discard everything up to the next whitespace push_error(tokens) m = re.search(r"(\s|$)", str) str = str[m.end():] # all done return tokens # primitive binary math ops def p_binmath(stack,op): if len(stack) < 2 or stack[-1].type != Token.T_INT or stack[-2].type != Token.T_INT: push_error(stack) return if op == Token.P_ADD: stack[-2].data += stack[-1].data elif op == Token.P_SUB: stack[-2].data -= stack[-1].data elif op == Token.P_MUL: stack[-2].data *= stack[-1].data elif op == Token.P_REM: stack[-2].data %= stack[-1].data elif op == Token.P_DIV: if stack[-1].data == 0: push_error(stack) return else: stack[-2].data //= stack[-1].data stack.pop() # primitive negate def p_neg(stack): if len(stack) < 1 or stack[-1].type != Token.T_INT: push_error(stack) return stack[-1].data = - stack[-1].data # primitive pop def p_pop(stack): if len(stack) < 1: push_error(stack) return stack.pop() # primitive duplicate def p_dup(stack): if len(stack) < 1: push_error(stack) return stack.append(Token(stack[-1].type, stack[-1].data)) # primitive exchange def p_exc(stack): if len(stack) < 2: push_error(stack) return stack.append(stack.pop(-2)) # primitive bind def p_bind(stack, binds): if len(stack) < 2 or stack[-2].type != Token.T_NAME or stack[-2].data in binds: push_error(stack) return binds[stack[-2].data] = Token(stack[-1].type, stack[-1].data) # copy token into bindings p_exc(stack) # swap arguments stack.pop() # pop off name, leaving value on stack # primitive load def p_load(stack, binds): if len(stack) < 1 or stack[-1].type != Token.T_STRING: push_error(stack) return try: # open up the file or bail out file = open(stack[-1].data, "r") except IOError: stack.append(Token(Token.T_BOOL, False)) # load evaluated as false return stack.pop() # pop off path argument gram(stack, binds, lex(file.read())) # send file contents off to lexer/grammar stack.append(Token(Token.T_BOOL, True)) # load evaluated as true def gram(stack, binds, lexed): for token in lexed: if token.type == Token.T_PRIM: # evaluate primitive operations if token.data in [Token.P_ADD, Token.P_SUB, Token.P_MUL, Token.P_DIV, Token.P_REM]: # binary math ops p_binmath(stack, token.data) if token.data == Token.P_NEG: # neg (negate integer) p_neg(stack) if token.data == Token.P_BINDS: # binds (show bindings) for bind in binds: print bind, "=>", binds[bind] if token.data == Token.P_BIND: # bind (create binding) p_bind(stack, binds) if token.data == Token.P_LOAD: # load (load file) p_load(stack, binds) if token.data == Token.P_POP: # pop (remove top element from stack) p_pop(stack) if token.data == Token.P_EXC: # exc (exchange top two elements of stack) p_exc(stack) if token.data == Token.P_DUP: # dup (duplicate top element of stack) p_dup(stack) if token.data == Token.P_QUIT: # quit exit(0) elif token.type == Token.T_NAME: # evaluate name bindings if token.data in binds: # if this binding exists stack.append(Token(binds[token.data].type, binds[token.data].data)) # copy bound token onto stack else: # otherwise push name literal onto stack stack.append(token) else: # push all other values onto stack stack.append(token) def repl(): stack = list() binds = dict() while True: line = raw_input("repl> ") # get some input gram(stack, binds, lex(line)) # apply grammar for token in reversed(stack): # show new stack print token