diff options
Diffstat (limited to 'weave/ast_tools.py')
-rw-r--r-- | weave/ast_tools.py | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/weave/ast_tools.py b/weave/ast_tools.py new file mode 100644 index 000000000..785ff61b9 --- /dev/null +++ b/weave/ast_tools.py @@ -0,0 +1,221 @@ +import token +import symbol +import parser + +from types import ListType, TupleType, StringType, IntType + +def int_to_symbol(i): + """ Convert numeric symbol or token to a desriptive name. + """ + try: + return symbol.sym_name[i] + except KeyError: + return token.tok_name[i] + +def translate_symbols(ast_tuple): + """ Translate numeric grammar symbols in an ast_tuple descriptive names. + + This simply traverses the tree converting any integer value to values + found in symbol.sym_name or token.tok_name. + """ + new_list = [] + for item in ast_tuple: + if type(item) == IntType: + new_list.append(int_to_symbol(item)) + elif type(item) in [TupleType,ListType]: + new_list.append(translate_symbols(item)) + else: + new_list.append(item) + if type(ast_tuple) == TupleType: + return tuple(new_list) + else: + return new_list + +def ast_to_string(ast_seq): + """* Traverse an ast tree sequence, printing out all leaf nodes. + + This effectively rebuilds the expression the tree was built + from. I guess its probably missing whitespace. How bout + indent stuff and new lines? Haven't checked this since we're + currently only dealing with simple expressions. + *""" + output = '' + for item in ast_seq: + if type(item) is StringType: + output = output + item + elif type(item) in [ListType,TupleType]: + output = output + ast_to_string(item) + return output + +def build_atom(expr_string): + """ Build an ast for an atom from the given expr string. + + If expr_string is not a string, it is converted to a string + before parsing to an ast_tuple. + """ + # the [1][1] indexing below starts atoms at the third level + # deep in the resulting parse tree. parser.expr will return + # a tree rooted with eval_input -> test_list -> test ... + # I'm considering test to be the root of atom symbols. + # It might be a better idea to move down a little in the + # parse tree. Any benefits? Right now, this works fine. + if type(expr_string) == StringType: + ast = parser.expr(expr_string).totuple()[1][1] + else: + ast = parser.expr(`expr_string`).totuple()[1][1] + return ast + +def atom_tuple(expr_string): + return build_atom(expr_string) + +def atom_list(expr_string): + return tuples_to_lists(build_atom(expr_string)) + +def find_first_pattern(ast_tuple,pattern_list): + """* Find the first occurence of a pattern one of a list of patterns + in ast_tuple. + + Used for testing at the moment. + + ast_tuple -- tuple or list created by ast.totuple() or ast.tolist(). + pattern_list -- A single pattern or list of patterns to search + for in the ast_tuple. If a single pattern is + used, it MUST BE A IN A TUPLE format. + Returns: + found -- true/false indicating whether pattern was found + data -- dictionary of data from first matching pattern in tree. + (see match function by Jeremy Hylton). + *""" + found,data = 0,{} + + # convert to a list if input wasn't a list + if type(pattern_list) != ListType: + pattern_list = [pattern_list] + + # look for any of the patterns in a list of patterns + for pattern in pattern_list: + found,data = match(pattern,ast_tuple) + if found: + break + + # if we didn't find the pattern, search sub-trees of the parse tree + if not found: + for item in ast_tuple: + if type(item) in [TupleType,ListType]: + # only search sub items if they are a list or tuple. + found, data = find_first_pattern(item,pattern_list) + if found: + break + return found,data + +name_pattern = (token.NAME, ['var']) + +def remove_duplicates(lst): + output = [] + for item in lst: + if item not in output: + output.append(item) + return output + +reserved_names = ['sin'] + +def remove_reserved_names(lst): + """ These are functions names -- don't create variables for them + There is a more reobust approach, but this ought to work pretty + well. + """ + output = [] + for item in lst: + if item not in reserved_names: + output.append(item) + return output + +def harvest_variables(ast_list): + """ Retreive all the variables that need to be defined. + """ + variables = [] + if type(ast_list) in (ListType,TupleType): + found,data = match(name_pattern,ast_list) + if found: + variables.append(data['var']) + for item in ast_list: + if type(item) in (ListType,TupleType): + variables.extend(harvest_variables(item)) + variables = remove_duplicates(variables) + variables = remove_reserved_names(variables) + return variables + +def match(pattern, data, vars=None): + """Match `data' to `pattern', with variable extraction. + + pattern + Pattern to match against, possibly containing variables. + + data + Data to be checked and against which variables are extracted. + + vars + Dictionary of variables which have already been found. If not + provided, an empty dictionary is created. + + The `pattern' value may contain variables of the form ['varname'] which + are allowed to match anything. The value that is matched is returned as + part of a dictionary which maps 'varname' to the matched value. 'varname' + is not required to be a string object, but using strings makes patterns + and the code which uses them more readable. + + This function returns two values: a boolean indicating whether a match + was found and a dictionary mapping variable names to their associated + values. + + From the Demo/Parser/example.py file + """ + if vars is None: + vars = {} + if type(pattern) is ListType: # 'variables' are ['varname'] + vars[pattern[0]] = data + return 1, vars + if type(pattern) is not TupleType: + return (pattern == data), vars + if len(data) != len(pattern): + return 0, vars + for pattern, data in map(None, pattern, data): + same, vars = match(pattern, data, vars) + if not same: + break + return same, vars + + +def tuples_to_lists(ast_tuple): + """ Convert an ast object tree in tuple form to list form. + """ + if type(ast_tuple) not in [ListType,TupleType]: + return ast_tuple + + new_list = [] + for item in ast_tuple: + new_list.append(tuples_to_lists(item)) + return new_list + +def test(): + from scipy_test import module_test + module_test(__name__,__file__) + +def test_suite(): + from scipy_test import module_test_suite + return module_test_suite(__name__,__file__) + +""" +A little tree I built to help me understand the parse trees. + -----------303------------------------------ + | | + 304 -------------------------307------------------------- + | | | | | | + 1 'result' 9 '[' 308 12 ',' 308 10 ']' + | | + ---------309-------- -----309-------- + | | | | + 291|304 291|304 291|304 | + | | | | + 1 'a1' 11 ':' 1 'a2' 2 '10' 11 ':' +""" |