Advertisement
Guest User

calculator.py

a guest
Nov 28th, 2012
121
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 7.02 KB | None | 0 0
  1. # # # calculator.py
  2.  
  3. import math
  4. import random
  5. import string
  6. import re
  7.  
  8. # # # enum
  9.  
  10. class EnumNotation:
  11.     Prefix = 1
  12.     Postfix = 2
  13.     Infix = 4
  14.     FunctionOnly = 8
  15.  
  16. # symbol, function name, precedence (higher the more precedence), fixity, what it does to its operators
  17.    
  18. binaryoperators = {
  19.     'log': ('log', 1, EnumNotation.FunctionOnly, lambda x, y: math.log(x, y)),
  20.     'atan2': ('atan2', 1, EnumNotation.FunctionOnly, lambda x, y: math.atan2(x, y)),
  21.     'gauss': ('gauss', 1, EnumNotation.FunctionOnly, lambda x, y: random.gauss(x, y)),
  22.     '+': ('add', 10, EnumNotation.Infix, lambda x, y: x + y),
  23.     '-': ('sub', 10, EnumNotation.Infix, lambda x, y: x - y),
  24.     '*': ('mul', 20, EnumNotation.Infix, lambda x, y: x * y),
  25.     '/': ('div', 20, EnumNotation.Infix, lambda x, y: x / y),
  26.     '%': ('mod', 20, EnumNotation.Infix, lambda x, y: x % y),
  27.     '^': ('pow', 30, EnumNotation.Infix, lambda x, y: x ** y),
  28.     'max': ('max', 40, EnumNotation.Infix, lambda x, y: max(x, y)),
  29.     'min': ('min', 40, EnumNotation.Infix, lambda x, y: min(x, y)),
  30.     'avg': ('avg', 40, EnumNotation.Infix, lambda x, y: (x + y)/ 2),
  31.     'd': ('roll', 50, EnumNotation.Infix, lambda x, y: reduce (lambda x, y: x + y, [random.randint(1, int(y)) for i in range(int(x))])),
  32. }
  33.  
  34. unaryoperators = {
  35.     'floor': ('floor', 1, EnumNotation.FunctionOnly, lambda x: math.floor(x)),
  36.     'ceil': ('ceil', 1, EnumNotation.FunctionOnly, lambda x: math.ceil(x)),
  37.     'round': ('round', 1, EnumNotation.FunctionOnly, lambda x: round(x)),
  38.     'V': ('sqrt', 10, EnumNotation.Prefix, lambda x: math.sqrt(x)),
  39.     'ln': ('ln', 15, EnumNotation.Prefix, lambda x: math.log(x)),
  40.     'abs': ('abs', 15, EnumNotation.Prefix, lambda x: abs(x)),
  41.     'sin': ('sin', 15, EnumNotation.Prefix, lambda x: math.sin(x)),
  42.     'cos': ('cos', 15, EnumNotation.Prefix, lambda x: math.cos(x)),
  43.     'tan': ('tan', 15, EnumNotation.Prefix, lambda x: math.tan(x)),
  44.     'asin': ('asin', 15, EnumNotation.Prefix, lambda x: math.asin(x)),
  45.     'acos': ('acos', 15, EnumNotation.Prefix, lambda x: math.acos(x)),
  46.     'atan': ('atan', 15, EnumNotation.Prefix, lambda x: math.atan(x)),
  47.     'sinh': ('sinh', 15, EnumNotation.Prefix, lambda x: math.sinh(x)),
  48.     'cosh': ('cosh', 15, EnumNotation.Prefix, lambda x: math.cosh(x)),
  49.     'tanh': ('tanh', 15, EnumNotation.Prefix, lambda x: math.tanh(x)),
  50.     'asinh': ('asinh', 15, EnumNotation.Prefix, lambda x: math.asinh(x)),
  51.     'acosh': ('acosh', 15, EnumNotation.Prefix, lambda x: math.acosh(x)),
  52.     'atanh': ('atanh', 15, EnumNotation.Prefix, lambda x: math.atanh(x)),
  53.     '!': ('factorial', 20, EnumNotation.Postfix, lambda x: math.gamma(x+1)),
  54.     'rand': ('rand', 25, EnumNotation.Prefix, lambda x: random.uniform(0, x)),
  55. }
  56.  
  57. variables = {
  58.     'pi': math.pi,
  59.     'e': math.e,
  60.     'ans': 0,
  61. }
  62.  
  63. def resetvariables():
  64.     variables = {
  65.         'pi': math.pi,
  66.         'e': math.e,
  67.         'ans': 0,
  68.     }
  69.  
  70. # regular expressions
  71.  
  72. regexdouble = r"(?P<num>-?\d*(\.\d+)?([eE][+\-]?\d+)?)" # scary huh?
  73. regexinteger = r"(\d+)"
  74. regexfunctionname = r"([\w\d]+)"
  75. regexnonfunction = r"([^\w\d])" # first non function name symbol
  76. regexstraybrackets = r"[()]"
  77.  
  78. regexbinaryinfix = r"(?P<num1>-?\d*(\.\d+)?([eE][+\-]?\d+)?)\s*{}\s*(?P<num2>-?\d*(\.\d+)?([eE][+\-]?\d+)?)" # notice {}
  79. regexunaryprefix = r"{}(?P<num>-?\d*(\.\d+)?([eE][+\-]?\d+)?)" # notice {}
  80. regexunarypostfix = r"(?P<num>-?\d*(\.\d+)?([eE][+\-]?\d+)?){}" # notice {}
  81. regexbrackets = r"(?P<funcname>[\w\d]*)\((?P<bracketed>[^(]*)\)" # brackets with an optional function name
  82.    
  83. # calculatethis should take a dictionary of variables -> values
  84.  
  85. def sanitize(s = ""):
  86.     # [\^$.|?*+()
  87.     return re.sub(r"([\[\\\^\$\.\|\?\*\+\(\)])", r"\\\1", s)
  88.  
  89. def calculatethis(evalstring = "", variablesdict = {}):
  90.     for (k, v) in variablesdict.iteritems():
  91.         variables[string.lower(k)] = v
  92.     ans = float(calculatethiscore(evalstring)) # try/catch exceptions?
  93.     variables['ans'] = ans
  94.     return ans
  95.  
  96. def calculatethiscore(evalstring = "", functionname = ""):
  97.     functionname = string.lower(functionname)
  98.     # step 1.5: split by ,s if we're in a function?
  99.     expectedcommanumber = 0
  100.     if len(functionname) > 0:
  101.         if functionname in [x[0] for x in binaryoperators.iteritems()]:
  102.             expectedcommanumber = 1
  103.             operatorentry = [x[0] for x in binaryoperators.iteritems() if x[0] == functionname]
  104.         elif functionname in [x[0] for x in unaryoperators.iteritems()]:
  105.             expectedcommanumber = 0
  106.             operatorentry = [x[0] for x in unaryoperators.iteritems() if x[0] == functionname]
  107.         else:
  108.             raise ArithmeticError("no definition for function name {}".format(functionname))
  109.    
  110.     if expectedcommanumber == 1:
  111.         splitstrings = evalstring.split(",")
  112.         return operatorentry[3](calculatethiscore(splitstrings[0]), calculatethiscore(splitstrings[1]))
  113.    
  114.     # step 2: recurse on brackets + do functions, substring and paste back into evalstring
  115.     while True:
  116.         mo = re.search(regexbrackets, evalstring)
  117.         if mo is None:
  118.             bracketsmo = re.search(regexstraybrackets, evalstring)
  119.             if bracketsmo is not None:
  120.                 raise ArithmeticError("Unbalanced brackets: Too many {}s", bracketsmo.group[0])
  121.             else:
  122.                 break
  123.         else:
  124.             result = calculatethiscore(mo.group('funcname'), string.lower(mo.group('bracketed')))
  125.             print mo.start(0)
  126.             print mo.end(0)
  127.             print len(evalstring)
  128.             evalstring = evalstring[0:mo.start(0)] + result + evalstring[mo.end(0)+1:len(evalstring)]
  129.    
  130.     # step 2.5: replace variables with values
  131.     for (k, v) in variables.iteritems():
  132.         evalstring.replace(k, str(v))
  133.    
  134.     # step 3: do all unary operators
  135.     # gotta handle precedence, fixity...
  136.     for i in range(100, -1, -1):
  137.         for (k, v) in filter(lambda x: x[1][1] == i, unaryoperators.iteritems()):
  138.             while True:
  139.                 if v[2] & EnumNotation.FunctionOnly != 0:
  140.                     break
  141.                 if v[2] & EnumNotation.Prefix != 0:
  142.                     mo = re.search(regexunaryprefix.format(sanitize(k)), evalstring, re.IGNORECASE)
  143.                     if mo is None:
  144.                         break
  145.                     else:
  146.                         result = v[3](float(mo.group('num')))
  147.                         evalstring = evalstring[0:mo.start(0)] + str(result) + evalstring[mo.end(0)+1:len(evalstring)]
  148.                 if v[2] & EnumNotation.Postfix != 0:
  149.                     mo = re.search(regexunarypostfix.format(sanitize(k)), evalstring, re.IGNORECASE)
  150.                     if mo is None:
  151.                         break
  152.                     else:
  153.                         result = v[3](float(mo.group('num')))
  154.                         evalstring = evalstring[0:mo.start(0)] + str(result) + evalstring[mo.end(0)+1:len(evalstring)]
  155.    
  156.     # step 4: do all binary operators
  157.     # gotta handle precedence, fixity...
  158.     for i in range(100, -1, -1):
  159.         for (k, v) in filter(lambda x: x[1][1] == i, binaryoperators.iteritems()):
  160.             while True:
  161.                 if v[2] & EnumNotation.FunctionOnly != 0:
  162.                     break
  163.                 if v[2] & EnumNotation.Infix != 0:
  164.                     mo = re.search(regexbinaryinfix.format(sanitize(k)), evalstring, re.IGNORECASE)
  165.                     if mo is None:
  166.                         break
  167.                     else:
  168.                         result = v[3](float(mo.group('num1')), float(mo.group('num2')))
  169.                         evalstring = evalstring[0:mo.start(0)] + str(result) + evalstring[mo.end(0)+1:len(evalstring)]
  170.    
  171.     # step 4.5: if I implement assignment operations, do 'em here
  172.    
  173.     # step 5: return result
  174.     return evalstring
  175.  
  176. # use by doing:
  177. # calculatethis("2d8")
  178. # calculatethis("1+1")
  179. # calculatethis("abs(-1)")
  180. # etc
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement