def check_bad_tree(self, tree, label): try: parser.sequence2st(tree) except parser.ParserError: pass else: self.fail("did not detect invalid tree for %r" % label)
def test_illegal_encoding(self): tree = 339, (257, (0, '')) self.check_bad_tree(tree, 'missed encoding') tree = 339, (257, (0, '')), b'iso-8859-1' self.check_bad_tree(tree, 'non-string encoding') tree = 339, (257, (0, '')), '\udcff' with self.assertRaises(UnicodeEncodeError): parser.sequence2st(tree)
def roundtrip(self, f, s): st1 = f(s) t = st1.totuple() try: st2 = parser.sequence2st(t) except parser.ParserError, why: self.fail("could not roundtrip %r: %s" % (s, why))
def parse_formula(unit_name): for name in CONVERSION_FACTORS: exec(name + '=' + CONVERSION_FACTORS[name]) # force any attempt to call a function to fail. unit_name = unit_name.replace('()', '') # these we have to hard code because the syntax tree can't be generated from them unit_name = unit_name.replace('\'', 'arcminute') unit_name = unit_name.replace('"', 'arcsecond') unit_name = unit_name.replace('°', 'degree') # turn the formula into a syntax tree and replace symbols with full names # can't be a simple string replace because 'h' could override 'ha' expr = parser.expr(unit_name) syntax_tree = expr.tolist() updated_tree, updated_unit_name = replace_tree_variable(syntax_tree) updated_expr = parser.sequence2st(updated_tree) code = updated_expr.compile() # execute our code. should be safe security wise because we removed the possibility of # function calls multiplication_factor = eval(code) multiplication_factor = str.format('{0:.14f}', multiplication_factor) return { 'unit_name': updated_unit_name, 'multiplication_factor': multiplication_factor, }
def roundtrip(self, f, s): st1 = f(s) t = st1.totuple() try: st2 = parser.sequence2st(t) except parser.ParserError as why: self.fail('could not roundtrip %r: %s' % (s, why)) self.assertEqual(t, st2.totuple(), 'could not re-generate syntax tree')
def test_illegal_encoding(self): # Illegal encoding declaration tree = \ (339, (257, (0, ''))) self.check_bad_tree(tree, "missed encoding") tree = \ (339, (257, (0, '')), b'iso-8859-1') self.check_bad_tree(tree, "non-string encoding") tree = \ (339, (257, (0, '')), '\udcff') with self.assertRaises(UnicodeEncodeError): parser.sequence2st(tree)
def roundtrip(self, f, s): st1 = f(s) t = st1.totuple() try: st2 = parser.sequence2st(t) except parser.ParserError as why: self.fail("could not roundtrip %r: %s" % (s, why)) self.assertEqual(t, st2.totuple(), "could not re-generate syntax tree")
def roundtrip(self, f, s): st1 = f(s) t = st1.totuple() try: st2 = parser.sequence2st(t) except parser.ParserError: self.fail("could not roundtrip %r" % s) self.assertEquals(t, st2.totuple(), "could not re-generate syntax tree")
def tweak_field(moose_wildcard, field, assignment_string): """Tweak a specified field of all objects that match the moose_wildcard using assignment string. All identifiers in assignment string must be fields of the target object. Example: tweak_field('/mycell/##[Class=Compartment]', 'Rm', '1.5 / (3.1416 * diameter * length') will assign Rm to every compartment in mycell such that the specific membrane resistance is 1.5 Ohm-m2. """ if not isinstance(moose_wildcard, str): raise TypeError('moose_wildcard must be a string.') id_list = moose.getWildcardList(moose_wildcard, True) expression = parser.expr(assignment_string) expr_list = expression.tolist() # This is a hack: I just tried out some possible syntax trees and # hand coded the replacement such that any identifier is replaced # by moose_obj.identifier def replace_fields_with_value(x): if len(x) > 1: if x[0] == symbol.power and x[1][0] == symbol.atom and x[1][1][ 0] == token.NAME: field = x[1][1][1] x[1] = [symbol.atom, [token.NAME, 'moose_obj']] x.append( [symbol.trailer, [token.DOT, '.'], [token.NAME, field]]) for item in x: if isinstance(item, list): replace_fields_with_value(item) return x tmp = replace_fields_with_value(expr_list) new_expr = parser.sequence2st(tmp) code = new_expr.compile() for moose_id in id_list: moose_obj = eval('moose.%s(moose_id)' % (moose.Neutral(moose_id).className)) value = eval(code) moose.setField(moose_id, field, str(value))
def _esoteric_source(src): if str_check(src): return parser.suite(src).compile() if is_ast_node(src): if isinstance(src, ast.ClassDef): node = ast.Module([src]) elif (isinstance(src, ast.Module) and isinstance(src.body[0], ast.ClassDef)): node = src else: raise TypeError(f'build_class() using an ast node as the ' f'class body it must be a ast.Class node ' f'or an ast.Module node whose .body[0] is ' f'a Class') return compile(node, '<ast.Class>', 'exec') if isinstance(src, (tuple, list)): src = parser.sequence2st(src) if isinstance(src, parser.STType): if not parser.issuite(src): raise TypeError(f'build_class() the parser syntax tree objects ' f'must be a "suite"') return parser.compilest(src)
def tweak_field(moose_wildcard, field, assignment_string): """Tweak a specified field of all objects that match the moose_wildcard using assignment string. All identifiers in assignment string must be fields of the target object. Example: tweak_field('/mycell/##[Class=Compartment]', 'Rm', '1.5 / (3.1416 * diameter * length') will assign Rm to every compartment in mycell such that the specific membrane resistance is 1.5 Ohm-m2. """ if not isinstance(moose_wildcard, str): raise TypeError("moose_wildcard must be a string.") id_list = _moose.getWildcardList(moose_wildcard, True) expression = parser.expr(assignment_string) expr_list = expression.tolist() # This is a hack: I just tried out some possible syntax trees and # hand coded the replacement such that any identifier is replaced # by moose_obj.identifier def replace_fields_with_value(x): if len(x) > 1: if x[0] == symbol.power and x[1][0] == symbol.atom and x[1][1][0] == token.NAME: field = x[1][1][1] x[1] = [symbol.atom, [token.NAME, "moose_obj"]] x.append([symbol.trailer, [token.DOT, "."], [token.NAME, field]]) for item in x: if isinstance(item, list): replace_fields_with_value(item) return x tmp = replace_fields_with_value(expr_list) new_expr = parser.sequence2st(tmp) code = new_expr.compile() for moose_id in id_list: moose_obj = eval("_moose.%s(moose_id)" % (_moose.Neutral(moose_id).className)) value = eval(code) _moose.setField(moose_id, field, str(value))
def dump_and_modify(node): name = symbol.sym_name.get(node[0]) if name is None: name = token.tok_name.get(node[0]) print(name, '', end='') for i in range(1, len(node)): item = node[i] if type(item) is type([]): dump_and_modify(item) else: print(repr(item)) if name == 'NUMBER': node[i] = repr(int(item) + 1) ast = parser.expr('1 + 3') list = ast.tolist() dump_and_modify(list) ast = parser.sequence2st(list) print(eval(parser.compilest(ast))) ''' eval_input testlist test or_test and_test not_test comparison expr xor_expr and_expr shift_expr arith_expr term factor power atom NUMBER '1' PLUS '+' term factor power atom NUMBER '3' NEWLINE '' ENDMARKER '' 6 [Finished in 0.2s] '''
def ParseAndCompileUserFunctionString(self, inString): # shift user functions into numpy namespace at run time, not import time numpySafeTokenList = [] for key in list(self.functionDictionary.keys()): numpySafeTokenList += self.functionDictionary[key] for key in list(self.constantsDictionary.keys()): numpySafeTokenList += self.constantsDictionary[key] # no blank lines of text, StringIO() allows using file methods on text stringToConvert = '' rawData = io.StringIO(inString).readlines() for line in rawData: stripped = line.strip() if len(stripped) > 0: # no empty strings if stripped[0] != '#': # no comment-only lines stringToConvert += stripped + '\n' # convert brackets to parentheses stringToConvert = stringToConvert.replace('[', '(').replace(']', ')') if stringToConvert == '': raise Exception( 'You must enter some function text for the software to use.') if -1 != stringToConvert.find('='): raise Exception( 'Please do not use an equals sign "=" in your text.') st = parser.expr(stringToConvert) tup = st.totuple() tokens = self.GetTokensFromTupleParsingHelper(tup) if '^' in tokens: raise Exception( 'The caret symbol "^" is not recognized by the parser, please substitute double asterisks "**" for "^".' ) if 'ln' in tokens: raise Exception( "The parser uses log() for the natural log function, not ln(). Please use log() in your text." ) if 'abs' in tokens: raise Exception( "The parser uses fabs() for the absolute value, not abs(). Please use fabs() in your text." ) if 'EXP' in tokens: raise Exception( "The parser uses lower case exp(), not upper case EXP(). Please use lower case exp() in your text." ) if 'LOG' in tokens: raise Exception( "The parser uses lower case log(), not upper case LOG(). Please use lower case log() in your text." ) # test for required reserved tokens tokenNames = list(set(tokens) - set(numpySafeTokenList)) if 'X' not in tokenNames: raise Exception( 'You must use a separate upper case "X" in your function to enter a valid function of X.' ) if 'Y' not in tokenNames: raise Exception( 'You must use a separate upper case "Y" in your function to enter a valid function of Y.' ) self._coefficientDesignators = sorted( list(set(tokenNames) - set(['X', 'Y']))) if len(self._coefficientDesignators) == 0: raise Exception( 'I could not find any equation parameter or coefficient names, please check the function text' ) # now compile code object using safe tokens with integer conversion self.safe_dict = locals() for f in numpySafeTokenList: self.safe_dict[f] = eval('numpy.' + f) # convert integer use such as (3/2) into floats such as (3.0/2.0) st = parser.expr(stringToConvert) stList = parser.st2list(st) stList = self.RecursivelyConvertIntStringsToFloatStrings(stList) st = parser.sequence2st(stList) # later evals re-use this compiled code for improved performance in EvaluateCachedData() methods self.userFunctionCodeObject = parser.compilest(st)
def test_ParserError_message(self): try: parser.sequence2st((257, (269, (257, (0, ''))))) except parser.ParserError as why: self.assertIn("simple_stmt", str(why)) # Expected self.assertIn("file_input", str(why)) # Got
def ParseAndCompileUserFunctionString(self, inString): # shift user functions into numpy namespace at run time, do not import time numpySafeTokenList = [] for key in list(self.functionDictionary.keys()): numpySafeTokenList += self.functionDictionary[key] for key in list(self.constantsDictionary.keys()): numpySafeTokenList += self.constantsDictionary[key] # to shift user functions such as "power" into the numpy namespace "numpy.power" for evaluation for token in numpySafeTokenList: exec(token + " = numpy." + token) # no blank lines of text, StringIO() allows using file methods on text stringToConvert = "" rawData = StringIO.StringIO(inString).readlines() for line in rawData: stripped = line.strip() if len(stripped) > 0: # no empty strings if stripped[0] != "#": # no comment-only lines stringToConvert += stripped + "\n" # convert brackets to parentheses stringToConvert = stringToConvert.replace("[", "(").replace("]", ")") if stringToConvert == "": raise Exception("You must enter some function text for the software to use.") if -1 != stringToConvert.find("="): raise Exception('Please do not use an equals sign "=" in your text.') st = parser.expr(stringToConvert) tup = st.totuple() tokens = self.GetTokensFromTupleParsingHelper(tup) if "^" in tokens: raise Exception( 'The caret symbol "^" is not recognized by the parser, please substitute double asterisks "**" for "^".' ) if "ln" in tokens: raise Exception( "The parser uses log() for the natural log function, not ln(). Please use log() in your text." ) if "abs" in tokens: raise Exception("The parser uses fabs() for the absolute value, not abs(). Please use fabs() in your text.") if "EXP" in tokens: raise Exception( "The parser uses lower case exp(), not upper case EXP(). Please use lower case exp() in your text." ) if "LOG" in tokens: raise Exception( "The parser uses lower case log(), not upper case LOG(). Please use lower case log() in your text." ) # test for required reserved tokens tokenNames = list(set(tokens) - set(numpySafeTokenList)) if "X" not in tokenNames: raise Exception('You must use a separate upper case "X" in your function to enter a valid function of X.') if "Y" not in tokenNames: raise Exception('You must use a separate upper case "Y" in your function to enter a valid function of Y.') self._coefficientDesignators = sorted(list(set(tokenNames) - set(["X", "Y"]))) if len(self._coefficientDesignators) == 0: raise Exception( "I could not find any equation parameter or coefficient names, please check the function text" ) # now compile code object using safe tokens self.safe_dict = dict([(k, locals().get(k, None)) for k in numpySafeTokenList]) # now compile code object using safe tokens with integer conversion self.safe_dict = dict([(k, locals().get(k, None)) for k in numpySafeTokenList]) # convert integer use such as (3/2) into floats such as (3.0/2.0) st = parser.expr(stringToConvert) stList = parser.st2list(st) stList = self.RecursivelyConvertIntStringsToFloatStrings(stList) st = parser.sequence2st(stList) # later evals re-use this compiled code for improved performance in EvaluateCachedData() methods self.userFunctionCodeObject = parser.compilest(st)
name = token.tok_name.get(node[0]) print(name, '', end = '') for i in range(1, len(node)): item = node[i] if type(item) is type([]): dump_and_modify(item) else: print(repr(item)) if name == 'NUMBER': node[i] = repr(int(item) + 1) ast = parser.expr('1 + 3') list = ast.tolist() dump_and_modify(list) ast = parser.sequence2st(list) print(eval(parser.compilest(ast))) ''' eval_input testlist test or_test and_test not_test comparison expr xor_expr and_expr shift_expr arith_expr term factor power atom NUMBER '1' PLUS '+' term factor power atom NUMBER '3' NEWLINE '' ENDMARKER '' 6 [Finished in 0.2s] '''
import parser
def get_st(self): return sequence2st(self.tree)