def _parse_tree(cst, candidate, path): # get the label of the top-most element of the CST # this should be either 'pause', 'conj', 'disj', or a relation assert type(cst) == list label = cst[0] assert label != 'state' # root node in cst is a PAUSE if label == 'pause': # obtain the candidate partial program from the "pause" node candidate = lisp.unparse(helper.get_candidate_from_pause(cst), collapse_lvar=True) obj = _parse_tree(cst[2], candidate, path) choices.append(obj) return obj # root node in cst is a CONJ if label == 'conj': # obtain all children of conj nodes leafs = [ _parse_tree(leaf, candidate, path) for p, leaf in helper.flatten_branches(cst, path) ] return FlatConj(constraints=leafs, candidate=candidate, path=path, unparsed="(" + " && ".join(l.unparsed for l in leafs) + ")") if label == 'disj': # obtain all children of disj nodes leafs = [ _parse_tree(leaf, candidate, p) for p, leaf in helper.flatten_branches(cst, path) ] return FlatDisj(constraints=leafs, candidate=candidate, path=path, unparsed="(" + " || ".join(l.unparsed for l in leafs) + ")") # root node is a RELATION; leaf node leafstr = lisp.unparse(cst, True) leaf_tokens = lisp.tokenize(leafstr) leaf_type = leaf_tokens[1] tok_seq = get_token_ids(leaf_tokens) return Constraint(unparsed=leafstr, candidate=candidate, type=leaf_type, seq=tok_seq, len=len(tok_seq), path=path[:])
def parse_split_trees(cst): """Split the CST into multiple trees, one per candidate. This function returns a list of tree objects, subclasses of gnn_nodes.Node cst -- a constraint tree, consisting of conj / disj / pause internal nodes and constraint leafs. the tree representing the "state" of the PBE problem and comes from Interaction.state """ shared_memo = {} parsed = [] for path, subtree in helper.flatten_branches(cst, []): ast, acc = parse_tree(subtree, {"memo": shared_memo}, path=path) parsed.append((ast, acc)) return parsed
def parse_tree(cst, acc=None, prev=None, candidate=None, path=None): """Recursive function that converts a constraint tree into a parsed form consisting of subclasses of gnn_nodes.Node cst -- a constraint tree, consisting of conj / disj / pause internal nodes and constraint leafs. the tree representing the "state" of the PBE problem and comes from Interaction.state acc -- an accumulator that is passed by value prev -- a previous instance of parsed_tree() accumulator from the previous step of the same problem candidate -- candidate partial program related to this part of the subtree path -- array of 0's and 1's, indicating whether to go left or right at a disjunction """ # set up initial values that are objects path = path or [] acc = acc or {"constraints": [], "memo": {}, "lvar": {}} if "constraints" not in acc: acc["constraints"] = [] # repository of constraints if "memo" not in acc: acc["memo"] = {} # memoized parsed subtree if "lvar" not in acc: acc["lvar"] = {} # all logic variables prev = prev or {} node_type = get_cst_node_type(cst) # get the pause & state out of the way if node_type == 'pause': candidate = lisp.unparse(helper.get_candidate_from_pause(cst), True) return parse_tree(cst[2], acc, prev, candidate, path) if node_type == 'state': raise ValueError("Should never be here!") # logical constructs if node_type == 'conj': children = [] for subpath, subcst in helper.flatten_branches(cst, path): # for conj, subpath is ignored! child, acc = parse_tree(subcst, acc, prev, candidate, path) children.append(child) return NAryConj(children, path), acc #left, acc = parse_tree(cst[1], acc, prev, candidate, path) #right, acc = parse_tree(cst[2], acc, prev, candidate, path) #return Conj(left, right, path), acc if node_type == 'disj': children = [] for subpath, subcst in helper.flatten_branches(cst, path): child, acc = parse_tree(subcst, acc, prev, candidate, subpath) children.append(child) return NAryDisj(children, path), acc #left, acc = parse_tree(cst[1], acc, prev, candidate, path+[0]) #right, acc = parse_tree(cst[2], acc, prev, candidate, path+[1]) #return Disj(left, right, path), acc # if using MEMOIZE, check if this (sub-)tree has already been parsed, and # its parsed version stored unparsed = lisp.unparse(cst) if MEMOIZE and unparsed in acc["memo"]: return acc["memo"][unparsed], acc # logic variables: add to repository of logic variables if not exist if node_type == 'lvar': if cst not in acc['lvar']: new_node = prev.get('lvar', {}).get(cst) new_node = new_node or LVar(cst) acc['lvar'][cst] = new_node return acc["lvar"][cst], acc # relations if node_type in ('evalo', 'lookupo', '==', 'eval-listo', 'not-falseo', 'not-nullo'): # a relations can have multiple children in its tree child_asts = [] for child_cst in cst[1:]: child_ast, acc = parse_tree(child_cst, acc, prev, candidate, path) child_asts.append(child_ast) NodeClass = GNN_NODE_CLASS[node_type] new_node_args = child_asts + [path, unparsed, candidate] new_node = NodeClass(*new_node_args) acc["constraints"].append(new_node) return new_node, acc # pairs if node_type == 'pair': if len(cst) == 3 and cst[1] == '.': # first left, acc = parse_tree(cst[0], acc, prev, candidate, path) # second right, acc = parse_tree(cst[2], acc, prev, candidate, path) else: # first left, acc = parse_tree(cst[0], acc, prev, candidate, path) # rest right, acc = parse_tree(cst[1:], acc, prev, candidate, path) new_node = Pair(left, right) if MEMOIZE and not has_lvar(unparsed): acc["memo"][unparsed] = new_node return new_node, acc # language constructs (inside the relations leafs) if node_type == 'const': new_node = Constant(unparsed) if MEMOIZE and not has_lvar(unparsed): acc["memo"][unparsed] = new_node return new_node, acc raise ValueError("Should not be here?")