# SimpleCalc.py # # Demonstration of the parsing module, # Sample usage # # $ python SimpleCalc.py # Type in the string to be parse or 'quit' to exit the program # > g=67.89 + 7/5 # 69.29 # > g # 69.29 # > h=(6*g+8.8)-g # 355.25 # > h + 1 # 356.25 # > 87.89 + 7/5 # 89.29 # > ans+10 # 99.29 # > quit # Good bye! # # from __future__ import division # Uncomment the line below for readline support on interactive terminal # import readline import re from pyparsing import Word, alphas, ParseException, Literal, CaselessLiteral \ , Combine, Optional, nums, Or, Forward, ZeroOrMore, StringEnd, alphanums import math # Debugging flag can be set to either "debug_flag=True" or "debug_flag=False" debug_flag=False exprStack = [] varStack = [] variables = {} def pushFirst( str, loc, toks ): exprStack.append( toks[0] ) def assignVar( str, loc, toks ): varStack.append( toks[0] ) # define grammar point = Literal('.') e = CaselessLiteral('E') plusorminus = Literal('+') | Literal('-') number = Word(nums) integer = Combine( Optional(plusorminus) + number ) floatnumber = Combine( integer + Optional( point + Optional(number) ) + Optional( e + integer ) ) ident = Word(alphas,alphanums + '_') plus = Literal( "+" ) minus = Literal( "-" ) mult = Literal( "*" ) div = Literal( "/" ) lpar = Literal( "(" ).suppress() rpar = Literal( ")" ).suppress() addop = plus | minus multop = mult | div expop = Literal( "^" ) assign = Literal( "=" ) expr = Forward() atom = ( ( e | floatnumber | integer | ident ).setParseAction(pushFirst) | ( lpar + expr.suppress() + rpar ) ) factor = Forward() factor << atom + ZeroOrMore( ( expop + factor ).setParseAction( pushFirst ) ) term = factor + ZeroOrMore( ( multop + factor ).setParseAction( pushFirst ) ) expr << term + ZeroOrMore( ( addop + term ).setParseAction( pushFirst ) ) bnf = Optional((ident + assign).setParseAction(assignVar)) + expr pattern = bnf + StringEnd() # map operator symbols to corresponding arithmetic operations opn = { "+" : ( lambda a,b: a + b ), "-" : ( lambda a,b: a - b ), "*" : ( lambda a,b: a * b ), "/" : ( lambda a,b: a / b ), "^" : ( lambda a,b: a ** b ) } # Recursive function that evaluates the stack def evaluateStack( s ): op = s.pop() if op in "+-*/^": op2 = evaluateStack( s ) op1 = evaluateStack( s ) return opn[op]( op1, op2 ) elif op == "PI": return math.pi elif op == "E": return math.e elif re.search('^[a-zA-Z][a-zA-Z0-9_]*$',op): if variables.has_key(op): return variables[op] else: return 0 elif re.search('^[-+]?[0-9]+$',op): return long( op ) else: return float( op ) if __name__ == '__main__': # input_string input_string='' # Display instructions on how to quit the program print "Type in the string to be parse or 'quit' to exit the program" input_string = raw_input("> ") while input_string != 'quit': # Start with a blank exprStack and a blank varStack exprStack = [] varStack = [] if input_string != '': # try parsing the input string try: L=pattern.parseString( input_string ) except ParseException,err: L=['Parse Failure',input_string] # show result of parsing the input string if debug_flag: print input_string, "->", L if len(L)==0 or L[0] != 'Parse Failure': if debug_flag: print "exprStack=", exprStack # calculate result , store a copy in ans , display the result to user result=evaluateStack(exprStack) variables['ans']=result print result # Assign result to a variable if required if debug_flag: print "var=",varStack if len(varStack)==1: variables[varStack.pop()]=result if debug_flag: print "variables=",variables else: print 'Parse Failure' print err.line print " "*(err.column-1) + "^" print err # obtain new input string input_string = raw_input("> ") # if user type 'quit' then say goodbye print "Good bye!"