Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #! /usr/bin/python
- import re
- import sys
- class ParseError:
- pass
- class UnknownToken(ParseError):
- def __init__(self, where):
- self.where = where
- def show(self):
- print "Unknown token \"", self.where, "\""
- class SyntaxError(ParseError):
- def __init__(self, msg, where):
- self.msg = msg
- self.where = where
- def show(self):
- print self.msg
- class Token:
- def __init__(self, Comma = False,
- Plus = False,
- Minus= False,
- Mul= False,
- Div= False,
- Circum = False,
- LParen= False,
- RParen= False,
- EOL= False,
- Number = False,
- Value = 0.0):
- self.Comma = Comma
- self.Plus = Plus
- self.Minus = Minus
- self.Mul = Mul
- self.Div = Div
- self.LParen= LParen
- self.RParen= RParen
- self.EOL = EOL
- self.Number= Number
- self.Value = Value
- self.Circum= Circum
- class Scanner:
- def __init__(self, string):
- self.string = filter(lambda x: not x.isspace(), string)
- self.next_sym = 0
- self.end = len(self.string)
- self.float_re = re.compile("^(?P<float>[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?).*")
- def getNextToken(self):
- if self.next_sym == self.end:
- return Token(EOL = True)
- c = self.string[self.next_sym]
- self.next_sym += 1
- if c == ',':
- return Token(Comma = True)
- elif c == '+':
- return Token(Plus = True)
- elif c == '-':
- return Token(Minus = True)
- elif c == '*':
- return Token(Mul = True)
- elif c == '/':
- return Token(Div = True)
- elif c == '^':
- return Token(Circum = True)
- elif c == '(':
- return Token(LParen = True)
- elif c == ')':
- return Token(RParen = True)
- self.next_sym -= 1
- try:
- a = self.float_re.match(self.string[self.next_sym:])
- if a != None:
- self.next_sym += a.end('float')
- return Token(Number = True, Value = float(a.group('float')))
- except IndexError:
- pass
- raise UnknownToken(self.string[self.next_sym:])
- def __iter__(self):
- return self
- def next(self):
- if self.next_sym == self.end:
- raise StopIteration
- return self.getNextToken()
- class Lexer:
- def __init__(self, scanner):
- self.tokens = [x for x in scanner]
- self.lookahead = 0
- def readFirstToken(self):
- return self.tokens[self.lookahead]
- def popToken(self):
- if self.lookahead < len(self.tokens) - 1:
- self.lookahead += 1
- def pushToken(self):
- if self.lookahead > 0:
- self.lookahead -= 1
- def expr(lexer):
- t = term(lexer)
- if not t:
- return False
- f = rest_e(lexer)
- return f(t)
- def rest_e(lexer):
- tok = lexer.readFirstToken()
- if tok.Minus:
- lexer.popToken()
- t = term(lexer)
- if not t:
- raise SyntaxError("Bad expression syntax", lexer)
- return lambda x: rest_e(lexer)(x - t)
- elif tok.Plus:
- lexer.popToken()
- t = term(lexer)
- if not t:
- raise SyntaxError("Bad expression syntax", lexer)
- return lambda x: rest_e(lexer)(x + t)
- else:
- return lambda x: x
- def term(lexer):
- t = factor(lexer)
- if not t:
- return False
- f = rest_t(lexer)
- return f(t)
- def rest_t(lexer):
- tok = lexer.readFirstToken()
- if tok.Mul:
- lexer.popToken()
- f = factor(lexer)
- if not f:
- raise SyntaxError("Bad expression syntax", lexer)
- return lambda x: rest_t(lexer)(x * f)
- elif tok.Div:
- lexer.popToken()
- f = factor(lexer)
- if not f:
- raise SyntaxError("Bad expression syntax", lexer)
- return lambda x: rest_t(lexer)(x / f)
- elif tok.Circum:
- lexer.popToken()
- f = factor(lexer)
- if not f:
- raise SyntaxError("Bad expression syntax", lexer)
- return lambda x: rest_t(lexer)(x ** f)
- else:
- return lambda x: x
- def sign_factor(lexer):
- tok = lexer.readFirstToken()
- if tok.Minus:
- lexer.popToken()
- return factor(lexer)*-1
- return False
- def factor(lexer):
- r = sign_factor(lexer)
- if r == False:
- r = parentheses(lexer)
- if r == False:
- return number(lexer)
- else:
- return r
- else:
- return r
- def parentheses(lexer):
- if not lexer.readFirstToken().LParen:
- return False
- lexer.popToken()
- expr_ret = expr(lexer)
- if not expr_ret:
- lexer.pushToken()
- return False
- if not lexer.readFirstToken().RParen:
- raise SyntaxError("""forgot ")"? """, lexer)
- lexer.popToken()
- return expr_ret
- def number(lexer):
- tok = lexer.readFirstToken()
- if tok.Number:
- lexer.popToken()
- return tok.Value
- return False
- def calc(str):
- return expr(Lexer(Scanner(str)))
- if len(sys.argv) < 2:
- print "usage: calc <expression>"
- else:
- try:
- print calc(sys.argv[1])
- except ParseError, error:
- error.show()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement