From Enso Wiki
from pyparsing import Literal,CaselessLiteral,Word,Combine,Group,Optional,\
ZeroOrMore,Forward,nums,alphas
from base import BaseCommand
import math
import operator
import sys
class Command(BaseCommand):
def __init__(self, *args):
BaseCommand.__init__(self, *args)
self.newCommand("scicalc", self._scicalc, "Scientific Calculator", "<p>Scientific calculator</p>")
self.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)>self.epsilon and cmp(a,0) or 0}
self._bnf = None
self.exprStack = []
#Command Methods
def _scicalc(self, postfix):
mathstring = self.getUnicodeSelection()
mathstring = mathstring.split('=')
self.exprStack = []
results = self._parser().parseString(mathstring[0])
val = self._evaluateStack(self.exprStack[:])
if len(mathstring) == 2:
self.setUnicodeSelection(unicode(mathstring[0]) + u"= " + unicode(val), "scicalc")
else:
self.setUnicodeSelection(unicode(val), "scicalc")
#Private
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 _parser(self):
if self._bnf is None:
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("-") + ( pi | e | fnumber | ident + lpar + expr + rpar ).setParseAction( self._pushFirst ) | ( lpar + expr.suppress() + rpar )).setParseAction(self._pushUMinus)
# by defining exponentiation as "atom [ ^ factor ]..." instead of "atom [ ^ atom ]...", we get right-to-left exponents, instead of left-to-righ
# 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 ) )
self._bnf = expr
return self._bnf
def _evaluateStack(self, s):
op = s.pop()
if op == 'unary -':
return -self._evaluateStack(s)
if op in self.opn.keys():
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)
[edit] Example
9 = 9.0
-9 = -9.0
--9 = 9.0
-E = -2.71828182846
9 + 3 + 6 = 18.0
9 + 3.0 / 11 = 9.27272727273
(9 + 3) = 12.0
(9+3.0) / 11 = 1.09090909091
9 - 12 - 6 = -9.0
9 - (12 - 6) = 3.0
2*3.14159 = 6.28318
3.1415926535*3.1415926535 / 10 = 0.986960440053
PI * PI / 10 = 0.986960440109
PI*pi/10 = 0.986960440109
PI^2 = 9.86960440109
round(PI^2) = 10.0
6.02E23 * 8.048 = 4.844896e+024
e / 3 = 0.90609394282
sin(PI/2) = 1.0
trunc(E) = 2
trunc(-E) = -2
round(E) = 3.0
round(-E) = -3.0
E^PI = 23.1406926328
2^3^2 = 512.0
2^3+2 = 10.0
2^3+5 = 13.0
2^9 = 512.0
sgn(-2) = -1
sgn(0) = 0
sgn(0.1) = 1