rendered paste bodyimport sys
class operator:
all_ops = {}
def __init__(self, symbol, precedence, mnemonic):
self.sym = symbol
self.prec = precedence
self.mnem = mnemonic
if operator.all_ops.has_key(symbol):
raise SyntaxError, 'Operator already assigned'
operator.all_ops[symbol] = self
operator('=', 50 , 'POP')
operator('+', 100, 'ADD')
operator('-', 100, 'SUB')
operator('*', 200, 'MUL')
operator('/', 200, 'DIV')
operator('^', 300, 'POW')
operator('@', 50 , 'END')
eq = 0
ay = 1
by = 2
precedence_table = [
# + - * / ^ ( ) @
[ by, by, ay, ay, ay, ay, by, by ], # +
[ by, by, ay, ay, ay, ay, by, by ], # -
[ by, by, by, by, by, ay, by, by ], # *
[ by, by, by, by, by, ay, by, by ], # /
[ by, by, by, by, by, ay, by, by ], # ^
[ ay, ay, ay, ay, ay, ay, eq, by ], # (
[ by, by, by, by, by, eq, by, by ], # )
[ ay, ay, ay, ay, ay, ay, ay, eq ], # @
]
def get_precedence(a, b):
mapping = {'+': 0, '-': 1, '*': 2, '/': 3, '^': 4, '{': 5, ')': 6, '@': 7 }
return precedence_table[mapping[a]][mapping[b]]
class lex:
'''Class to hold lexer related variables'''
digits = '0123456789'
alpha = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
ident = alpha + digits + '_'
ops = '+-*/^'
newline = ';'
addsub = ['+', '-']
muldiv = ['*', '/']
boolops = ['<', '>', '==', '!=']
unknown = 0
number = 1
ident = 2
operator = 3
def __init__(self, code):
self.pos = 0
self.char = code[0]
self.code = code
self.count = len(code)
self.col = 1
self.sym = None
self.symtype = None
self.linebuf = ''
self.line = 1
self.end = False
self.next_symbol()
def isdigit(self):
if self.char in lex.digits:
return True
return False
def isnumber(self):
if sum([1 for i in self.sym if i in lex.digits]) < len(self.sym):
return False
return True
def isalpha(self):
if self.char in lex.alpha:
return True
return False
def isident(self):
if self.char in lex.ident:
return True
return False
def isidentifier(self):
if self.sym[0] in lex.alpha and sum([1 for i in self.sym[1:] if i in lex.ident]) == len(self.sym) - 1:
return True
return False
def isop(self):
if self.char in lex.ops:
return True
return False
def next_char(self):
#print self.char
char = self.char
self.pos += 1
self.col += 1
if self.pos >= self.count:
self.end = True
return
self.char = self.code[self.pos]
self.linebuf += self.char
def next_symbol(self):
while(1):
if self.end:
self.sym = 'END'
return
if self.char in '''
''':
self.next_char()
continue
elif self.char is lex.newline:
self.line += 1
self.col = 1
self.linebuf = ''
self.next_char() #for when there is more than 1 line
elif self.isdigit():
num = ''
while(1):
num += self.char
self.next_char()
if not self.isdigit():
self.sym = num
self.symtype = lex.number
return
elif self.isalpha():
ident = ''
while(1):
ident += self.char
self.next_char()
if not self.isident():
self.sym = ident
self.symtype = lex.ident
return
elif self.isop():
op = ''
while(1):
op += self.char
self.next_char()
if not self.isop():
self.sym = op
self.symtype = lex.operator
return
else:
self.sym = self.char
self.symtype = lex.unknown
self.next_char()
return
class bottom_up:
def __init__(self, out_strm, lexer):
self.op_prec = operator.all_ops
self.out = out_strm
self.lexer = lexer
self.lineidx = 0
self.lblidx = 0
def isnum(self, char):
if char in '0123456789':
return 1
return 0
def isop(self, char):
if char in self.op_prec.keys():
return 1
return 0
def output(self, out):
self.lineidx += 1
self.out.write('''%04d %s
''' % (self.lineidx, out))
def evaluate(self):
#print some stats
print 'Expression : %s' % self.lexer.code
print 'Expression Length : %s' % len(self.lexer.code)
print ''
#stack for operations
ops = ['@']
#loops through all 'tokens'
while(self.lexer.sym <> 'END'):
sym = self.lexer.sym
stype = self.lexer.symtype
#all numbers are immediately output
if stype == lex.number:
self.output('PUSH #%s' % sym)
#if the current operator has a precedence less than the last
#operator put on the stack, pop the last operator and output
#it. push all operators onto the stack
elif stype == lex.operator:
prec = get_precedence(ops[-1], sym)
if prec == ay or prec == eq:
ops.append(sym)
else:
while(get_precedence(ops[-1], sym) == by):
self.output(operator.all_ops[ops.pop()].mnem)
ops.append(sym)
self.lexer.next_symbol()
#any left over operators on the stack need to be popped to the
#output
#print ops
while(len(ops) <> 0):
self.output(self.op_prec[ops.pop()].mnem)
#more stats
print ''
print 'Lines Output : %s' % self.lineidx
import time
lexer = lex('3*4*6*5;')
parser = bottom_up(sys.stdout, lexer)
start = time.clock()
parser.evaluate()
end = time.clock()
print 'Start : %s' % start
print 'End : %s' % end
print 'Time : %s' % str(end - start)