Commit 74866a38 by Peter Baratta

Move parseActions and statics out of evaluator()

parent babe46e6
...@@ -37,16 +37,33 @@ default_variables = {'j': numpy.complex(0, 1), ...@@ -37,16 +37,33 @@ default_variables = {'j': numpy.complex(0, 1),
'q': scipy.constants.e 'q': scipy.constants.e
} }
ops = {"^": operator.pow,
"*": operator.mul,
"/": operator.truediv,
"+": operator.add,
"-": operator.sub,
}
# We eliminated extreme ones, since they're rarely used, and potentially
# confusing. They may also conflict with variables if we ever allow e.g.
# 5R instead of 5*R
suffixes = {'%': 0.01, 'k': 1e3, 'M': 1e6, 'G': 1e9,
'T': 1e12, # 'P':1e15,'E':1e18,'Z':1e21,'Y':1e24,
'c': 1e-2, 'm': 1e-3, 'u': 1e-6,
'n': 1e-9, 'p': 1e-12} # ,'f':1e-15,'a':1e-18,'z':1e-21,'y':1e-24}
log = logging.getLogger("mitx.courseware.capa") log = logging.getLogger("mitx.courseware.capa")
class UndefinedVariable(Exception): class UndefinedVariable(Exception):
def raiseself(self): pass
''' Helper so we can use inside of a lambda ''' # unused for now
raise self # def raiseself(self):
# ''' Helper so we can use inside of a lambda '''
# raise self
general_whitespace = re.compile('[^\w]+') general_whitespace = re.compile('[^\\w]+')
def check_variables(string, variables): def check_variables(string, variables):
...@@ -65,85 +82,33 @@ def check_variables(string, variables): ...@@ -65,85 +82,33 @@ def check_variables(string, variables):
for v in possible_variables: for v in possible_variables:
if len(v) == 0: if len(v) == 0:
continue continue
if v[0] <= '9' and '0' <= 'v': # Skip things that begin with numbers if v[0] <= '9' and '0' <= v: # Skip things that begin with numbers
continue continue
if v not in variables: if v not in variables:
bad_variables.append(v) bad_variables.append(v)
if len(bad_variables) > 0: if len(bad_variables) > 0:
raise UndefinedVariable(' '.join(bad_variables)) raise UndefinedVariable(' '.join(bad_variables))
def lower_dict(d):
def evaluator(variables, functions, string, cs=False):
'''
Evaluate an expression. Variables are passed as a dictionary
from string to value. Unary functions are passed as a dictionary
from string to function. Variables must be floats.
cs: Case sensitive
TODO: Fix it so we can pass integers and complex numbers in variables dict
'''
# log.debug("variables: {0}".format(variables))
# log.debug("functions: {0}".format(functions))
# log.debug("string: {0}".format(string))
def lower_dict(d):
return dict([(k.lower(), d[k]) for k in d]) return dict([(k.lower(), d[k]) for k in d])
all_variables = copy.copy(default_variables) def super_float(text):
all_functions = copy.copy(default_functions)
if not cs:
all_variables = lower_dict(all_variables)
all_functions = lower_dict(all_functions)
all_variables.update(variables)
all_functions.update(functions)
if not cs:
string_cs = string.lower()
all_functions = lower_dict(all_functions)
all_variables = lower_dict(all_variables)
CasedLiteral = CaselessLiteral
else:
string_cs = string
CasedLiteral = Literal
check_variables(string_cs, set(all_variables.keys() + all_functions.keys()))
if string.strip() == "":
return float('nan')
ops = {"^": operator.pow,
"*": operator.mul,
"/": operator.truediv,
"+": operator.add,
"-": operator.sub,
}
# We eliminated extreme ones, since they're rarely used, and potentially
# confusing. They may also conflict with variables if we ever allow e.g.
# 5R instead of 5*R
suffixes = {'%': 0.01, 'k': 1e3, 'M': 1e6, 'G': 1e9,
'T': 1e12, # 'P':1e15,'E':1e18,'Z':1e21,'Y':1e24,
'c': 1e-2, 'm': 1e-3, 'u': 1e-6,
'n': 1e-9, 'p': 1e-12} # ,'f':1e-15,'a':1e-18,'z':1e-21,'y':1e-24}
def super_float(text):
''' Like float, but with si extensions. 1k goes to 1000''' ''' Like float, but with si extensions. 1k goes to 1000'''
if text[-1] in suffixes: if text[-1] in suffixes:
return float(text[:-1]) * suffixes[text[-1]] return float(text[:-1]) * suffixes[text[-1]]
else: else:
return float(text) return float(text)
def number_parse_action(x): # [ '7' ] -> [ 7 ] def number_parse_action(x): # [ '7' ] -> [ 7 ]
return [super_float("".join(x))] return [super_float("".join(x))]
def exp_parse_action(x): # [ 2 ^ 3 ^ 2 ] -> 512 def exp_parse_action(x): # [ 2 ^ 3 ^ 2 ] -> 512
x = [e for e in x if isinstance(e, numbers.Number)] # Ignore ^ x = [e for e in x if isinstance(e, numbers.Number)] # Ignore ^
x.reverse() x.reverse()
x = reduce(lambda a, b: b ** a, x) x = reduce(lambda a, b: b ** a, x)
return x return x
def parallel(x): # Parallel resistors [ 1 2 ] => 2/3 def parallel(x): # Parallel resistors [ 1 2 ] => 2/3
# convert from pyparsing.ParseResults, which doesn't support '0 in x' # convert from pyparsing.ParseResults, which doesn't support '0 in x'
x = list(x) x = list(x)
if len(x) == 1: if len(x) == 1:
...@@ -153,7 +118,7 @@ def evaluator(variables, functions, string, cs=False): ...@@ -153,7 +118,7 @@ def evaluator(variables, functions, string, cs=False):
x = [1. / e for e in x if isinstance(e, numbers.Number)] # Ignore || x = [1. / e for e in x if isinstance(e, numbers.Number)] # Ignore ||
return 1. / sum(x) return 1. / sum(x)
def sum_parse_action(x): # [ 1 + 2 - 3 ] -> 0 def sum_parse_action(x): # [ 1 + 2 - 3 ] -> 0
total = 0.0 total = 0.0
op = ops['+'] op = ops['+']
for e in x: for e in x:
...@@ -163,7 +128,7 @@ def evaluator(variables, functions, string, cs=False): ...@@ -163,7 +128,7 @@ def evaluator(variables, functions, string, cs=False):
total = op(total, e) total = op(total, e)
return total return total
def prod_parse_action(x): # [ 1 * 2 / 3 ] => 0.66 def prod_parse_action(x): # [ 1 * 2 / 3 ] => 0.66
prod = 1.0 prod = 1.0
op = ops['*'] op = ops['*']
for e in x: for e in x:
...@@ -173,9 +138,46 @@ def evaluator(variables, functions, string, cs=False): ...@@ -173,9 +138,46 @@ def evaluator(variables, functions, string, cs=False):
prod = op(prod, e) prod = op(prod, e)
return prod return prod
def evaluator(variables, functions, string, cs=False):
'''
Evaluate an expression. Variables are passed as a dictionary
from string to value. Unary functions are passed as a dictionary
from string to function. Variables must be floats.
cs: Case sensitive
TODO: Fix it so we can pass integers and complex numbers in variables dict
'''
# log.debug("variables: {0}".format(variables))
# log.debug("functions: {0}".format(functions))
# log.debug("string: {0}".format(string))
all_variables = copy.copy(default_variables)
all_functions = copy.copy(default_functions)
def func_parse_action(x): def func_parse_action(x):
return [all_functions[x[0]](x[1])] return [all_functions[x[0]](x[1])]
if not cs:
all_variables = lower_dict(all_variables)
all_functions = lower_dict(all_functions)
all_variables.update(variables)
all_functions.update(functions)
if not cs:
string_cs = string.lower()
all_functions = lower_dict(all_functions)
all_variables = lower_dict(all_variables)
CasedLiteral = CaselessLiteral
else:
string_cs = string
CasedLiteral = Literal
check_variables(string_cs, set(all_variables.keys() + all_functions.keys()))
if string.strip() == "":
return float('nan')
# SI suffixes and percent # SI suffixes and percent
number_suffix = reduce(lambda a, b: a | b, map(Literal, suffixes.keys()), NoMatch()) number_suffix = reduce(lambda a, b: a | b, map(Literal, suffixes.keys()), NoMatch())
(dot, minus, plus, times, div, lpar, rpar, exp) = map(Literal, ".-+*/()^") (dot, minus, plus, times, div, lpar, rpar, exp) = map(Literal, ".-+*/()^")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment