]> jfr.im git - irc/rizon/acid.git/blob - pyva/src/main/python/internets/api/calc.py
Import acidictive 4 and pyva plugin
[irc/rizon/acid.git] / pyva / src / main / python / internets / api / calc.py
1 # calculator API
2 from __future__ import division
3 from pyparsing import (
4 Literal, CaselessLiteral, Word, Combine, Group, Optional,
5 ZeroOrMore, Forward, nums, alphas, oneOf)
6 import math
7 import operator
8
9 __author__ = 'Paul McGuire'
10 __version__ = '$Revision: 0.0 $'
11 __date__ = '$Date: 2009-03-20 $'
12 __source__ = '''http://pyparsing.wikispaces.com/file/view/fourFn.py
13 http://pyparsing.wikispaces.com/message/view/home/15549426
14 '''
15 __note__ = '''
16 All I've done is rewrap Paul McGuire's fourFn.py as a class, so I can use it
17 more easily in other places.
18 '''
19
20
21 class NumericStringParser(object):
22
23 '''
24 Most of this code comes from the fourFn.py pyparsing example
25
26 '''
27
28 def pushFirst(self, strg, loc, toks):
29 self.exprStack.append(toks[0])
30
31 def pushUMinus(self, strg, loc, toks):
32 if toks and toks[0] == '-':
33 self.exprStack.append('unary -')
34
35 def __init__(self):
36 """
37 expop :: '^'
38 multop :: '*' | '/'
39 addop :: '+' | '-'
40 integer :: ['+' | '-'] '0'..'9'+
41 atom :: PI | E | real | fn '(' expr ')' | '(' expr ')'
42 factor :: atom [ expop factor ]*
43 term :: factor [ multop factor ]*
44 expr :: term [ addop term ]*
45 """
46 point = Literal(".")
47 e = CaselessLiteral("E")
48 fnumber = Combine(Word("+-" + nums, nums) +
49 Optional(point + Optional(Word(nums))) +
50 Optional(e + Word("+-" + nums, nums)))
51 ident = Word(alphas, alphas + nums + "_$")
52 plus = Literal("+")
53 minus = Literal("-")
54 mult = Literal("*")
55 div = Literal("/")
56 lpar = Literal("(").suppress()
57 rpar = Literal(")").suppress()
58 addop = plus | minus
59 multop = mult | div
60 expop = Literal("^")
61 pi = CaselessLiteral("PI")
62 expr = Forward()
63 atom = ((Optional(oneOf("- +")) +
64 (pi | e | fnumber | ident + lpar + expr + rpar).setParseAction(self.pushFirst))
65 | Optional(oneOf("- +")) + Group(lpar + expr + rpar)
66 ).setParseAction(self.pushUMinus)
67 # by defining exponentiation as "atom [ ^ factor ]..." instead of
68 # "atom [ ^ atom ]...", we get right-to-left exponents, instead of left-to-right
69 # that is, 2^3^2 = 2^(3^2), not (2^3)^2.
70 factor = Forward()
71 factor << atom + \
72 ZeroOrMore((expop + factor).setParseAction(self.pushFirst))
73 term = factor + \
74 ZeroOrMore((multop + factor).setParseAction(self.pushFirst))
75 expr << term + \
76 ZeroOrMore((addop + term).setParseAction(self.pushFirst))
77 # addop_term = ( addop + term ).setParseAction( self.pushFirst )
78 # general_term = term + ZeroOrMore( addop_term ) | OneOrMore( addop_term)
79 # expr << general_term
80 self.bnf = expr
81 # map operator symbols to corresponding arithmetic operations
82 epsilon = 1e-12
83 self.opn = {"+": operator.add,
84 "-": operator.sub,
85 "*": operator.mul,
86 "/": operator.truediv,
87 "^": operator.pow}
88 self.fn = {"sin": math.sin,
89 "cos": math.cos,
90 "tan": math.tan,
91 "abs": abs,
92 "trunc": lambda a: int(a),
93 "round": round,
94 "sgn": lambda a: abs(a) > epsilon and cmp(a, 0) or 0}
95
96 def evaluateStack(self, s):
97 op = s.pop()
98 if op == 'unary -':
99 return -self.evaluateStack(s)
100 if op in "+-*/^":
101 op2 = self.evaluateStack(s)
102 op1 = self.evaluateStack(s)
103 return self.opn[op](op1, op2)
104 elif op == "PI":
105 return math.pi # 3.1415926535
106 elif op == "E":
107 return math.e # 2.718281828
108 elif op in self.fn:
109 return self.fn[op](self.evaluateStack(s))
110 elif op[0].isalpha():
111 return 0
112 else:
113 return float(op)
114
115 def eval(self, num_string, parseAll=True):
116 self.exprStack = []
117 results = self.bnf.parseString(num_string, parseAll) # has side effect that makes next line work
118 val = self.evaluateStack(self.exprStack[:])
119 return val