Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
louisfisch committed Sep 23, 2015
0 parents commit fd24138
Showing 1 changed file with 226 additions and 0 deletions.
226 changes: 226 additions & 0 deletions math_exp_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
# A really simple expression evaluator supporting the
# four basic math functions, parentheses, and variables.
from math import *

class Parser:
def __init__(self, string, vars={}):
self.string = string
self.index = 0
self.vars = {
'pi' : 3.141592653589793,
'e' : 2.718281828459045
}

for var in vars.keys():
if self.vars.get(var) != None:
raise Exception(
"Cannot redefine the value of " + var
)
self.vars[var] = vars[var]

def getValue(self):
value = self.parseExpression()
self.skipWhitespace()

if self.hasNext():
raise Exception(
"Unexpected character found: '" + self.peek() + "' at index " + str(self.index)
)
return value

def peek(self):
return self.string[self.index:self.index + 1]

def hasNext(self):
return self.index < len(self.string)

def skipWhitespace(self):
while self.hasNext():
if self.peek() in ' \t\n\r':
self.index += 1
else:
return

def parseExpression(self):
return self.parseAddition()

def parseAddition(self):
values = [self.parseMultiplication()]

while True:
self.skipWhitespace()
char = self.peek()

if char == '+':
self.index += 1
values.append(self.parseMultiplication())
elif char == '-':
self.index += 1
values.append(-1 * self.parseMultiplication())
else:
break

return sum(values)

def parseMultiplication(self):
values = [self.parseParenthesis()]

while True:
self.skipWhitespace()
char = self.peek()

if char == '*':
self.index += 1
values.append(self.parseParenthesis())
elif char == '/':
div_index = self.index
self.index += 1
denominator = self.parseParenthesis()

if denominator == 0:
raise Exception(
"Division by 0 kills baby whales (occured at index " + str(div_index) + ")"
)
values.append(1.0 / denominator)
else:
break

value = 1.0

for factor in values:
value *= factor
return value

def parseParenthesis(self):
self.skipWhitespace()
char = self.peek()

if char == '(':
self.index += 1
value = self.parseExpression()
self.skipWhitespace()

if self.peek() != ')':
raise Exception(
"No closing parenthesis found at character " + str(self.index)
)
self.index += 1
return value
else:
return self.parseNegative()

def parseNegative(self):
self.skipWhitespace()
char = self.peek()

if char == '-':
self.index += 1
return -1 * self.parseParenthesis()
else:
return self.parseValue()

def parseValue(self):
self.skipWhitespace()
char = self.peek()

if char in '0123456789.':
return self.parseNumber()
else:
return self.parseVariable()

def parseVariable(self):
self.skipWhitespace()
var = ''
functions = ['math','abs','acos' 'asin','atan','atan2','ceil','cos','cosh','degrees','exp','fabs','floor','fmod','frexp','hypot','ldexp','log','log10','modf','pow','radians','sin','sinh','sqrt','tan','tanh']

while self.hasNext():
char = self.peek()

if char.lower() in '_abcdefghijklmnopqrstuvwxyz0123456789':
var += char
self.index += 1
else:
break

if len(var) < 3:
value = self.vars.get(var, None)

if value == None:
raise Exception(
"Unrecognized variable: '" + var + "'"
)
return float(value)
else:
if var in functions:
value = self.parseParenthesis()
value = eval(var+"("+str(value)+")")
else:
raise Exception(
"Unrecognized function: '" + var + "'"
)

return float(value)

def parseNumber(self):
self.skipWhitespace()
strValue = ''
decimal_found = False
char = ''

while self.hasNext():
char = self.peek()

if char == '.':
if decimal_found:
raise Exception(
"Found an extra period in a number at character " + str(self.index) + ". Are you European?"
)
decimal_found = True
strValue += '.'
elif char in '0123456789':
strValue += char
else:
break
self.index += 1

if len(strValue) == 0:
if char == '':
raise Exception("Unexpected end found")
else:
raise Exception(
"I was expecting to find a number at character " + str(self.index) + " but instead I found a '" + char + "'. What's up with that?")

return float(strValue)

def evaluate(expression, vars={}):
try:
p = Parser(expression, vars)
value = p.getValue()
except Exception as (ex):
msg = ex.message
raise Exception(msg)

# Return an integer type if the answer is an integer
if int(value) == value:
return int(value)

# If Python made some silly precision error
# like x.99999999999996, just return x+1 as an integer
epsilon = 0.0000000001
if int(value + epsilon) != int(value):
return int(value + epsilon)
elif int(value - epsilon) != int(value):
return int(value)

return value


print evaluate("cos(x+4*3) + 2 * 3", { 'x': 5 })
print evaluate("exp(0)")
print evaluate("-(1 + 2) * 3")
print evaluate("(1-2)/3.0 + 0.0000")
print evaluate("abs(-2) + pi / 4")
print evaluate("(x + e * 10) / 10", { 'x' : 3 })
print evaluate("1.0 / 3 * 6")
print evaluate("(1 - 1 + -1) * pi")
print evaluate("cos(pi) * 1")

0 comments on commit fd24138

Please sign in to comment.