Scicalc

From Enso Wiki

Jump to: navigation, search
  • NOTE: This command is created for Command Server not as a standalone command
  • NOTE: This command required pyparsing

[edit] Code

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