Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
from __future__ import division from pyparsing import (Literal,CaselessLiteral,Word,Combine,Group,Optional, ZeroOrMore,Forward,nums,alphas,oneOf) import math import operator
__author__='Paul McGuire' __version__ = '$Revision: 0.0 $' __date__ = '$Date: 2009-03-20 $' __source__='''http://pyparsing.wikispaces.com/file/view/fourFn.py http://pyparsing.wikispaces.com/message/view/home/15549426 ''' __note__=''' Script from: http://stackoverflow.com/questions/2371436/evaluating-a-mathematical-expression-in-a-string Original note:
All I've done is rewrap Paul McGuire's fourFn.py as a class, so I can use it more easily in other places. '''
class NumericStringParser(object): ''' Most of this code comes from the fourFn.py pyparsing example
''' def pushFirst(self, strg, loc, toks ): self.exprStack.append( toks[0] ) def pushUMinus(self, strg, loc, toks ): if toks and toks[0]=='-': self.exprStack.append( 'unary -' ) def __init__(self): """ expop :: '^' multop :: '*' | '/' addop :: '+' | '-' integer :: ['+' | '-'] '0'..'9'+ atom :: PI | E | real | fn '(' expr ')' | '(' expr ')' factor :: atom [ expop factor ]* term :: factor [ multop factor ]* expr :: term [ addop term ]* """ point = Literal( "." ) e = CaselessLiteral( "E" ) fnumber = Combine( Word( "+-"+nums, nums ) + Optional( point + Optional( Word( nums ) ) ) + Optional( e + Word( "+-"+nums, nums ) ) ) ident = Word(alphas, alphas+nums+"_$") plus = Literal( "+" ) minus = Literal( "-" ) mult = Literal( "*" ) div = Literal( "/" ) lpar = Literal( "(" ).suppress() rpar = Literal( ")" ).suppress() addop = plus | minus multop = mult | div expop = Literal( "^" ) pi = CaselessLiteral( "PI" ) expr = Forward() atom = ((Optional(oneOf("- +")) + (pi|e|fnumber|ident+lpar+expr+rpar).setParseAction(self.pushFirst)) | Optional(oneOf("- +")) + Group(lpar+expr+rpar) ).setParseAction(self.pushUMinus) # by defining exponentiation as "atom [ ^ factor ]..." instead of # "atom [ ^ atom ]...", we get right-to-left exponents, instead of left-to-right # that is, 2^3^2 = 2^(3^2), not (2^3)^2. factor = Forward() factor << atom + ZeroOrMore( ( expop + factor ).setParseAction( self.pushFirst ) ) term = factor + ZeroOrMore( ( multop + factor ).setParseAction( self.pushFirst ) ) expr << term + ZeroOrMore( ( addop + term ).setParseAction( self.pushFirst ) ) # addop_term = ( addop + term ).setParseAction( self.pushFirst ) # general_term = term + ZeroOrMore( addop_term ) | OneOrMore( addop_term) # expr << general_term self.bnf = expr # map operator symbols to corresponding arithmetic operations epsilon = 1e-12 self.opn = { "+" : operator.add, "-" : operator.sub, "*" : operator.mul, "/" : operator.truediv, "^" : operator.pow } self.fn = { "sin" : math.sin, "cos" : math.cos, "tan" : math.tan, "abs" : abs, "trunc" : lambda a: int(a), "round" : round, "sgn" : lambda a: abs(a)>epsilon and cmp(a,0) or 0} def evaluateStack(self, s ): op = s.pop() if op == 'unary -': return -self.evaluateStack( s ) if op in "+-*/^": op2 = self.evaluateStack( s ) op1 = self.evaluateStack( s ) return self.opn[op]( op1, op2 ) elif op == "PI": return math.pi # 3.1415926535 elif op == "E": return math.e # 2.718281828 elif op in self.fn: return self.fn[op]( self.evaluateStack( s ) ) elif op[0].isalpha(): return 0 else: return float( op ) def eval(self,num_string,parseAll=True): self.exprStack=[] results=self.bnf.parseString(num_string,parseAll) val=self.evaluateStack( self.exprStack[:] ) return val |