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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

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