All pastes #960308 Raw Edit

timmeh

public text v1 · immutable
#960308 ·published 2008-03-28 00:25 UTC
rendered paste body
import 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)