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 get_candidate(cst, path, collapse_lvar=False): """Retrieve the candidate at a path in the cst, and convert it into a string. Arguments: 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 path -- array of 0's and 1's, indicating whether to go left or right at a disjunction collapse_lvar -- whether to rename all logic variables to "_" """ if cst[0] == 'conj': return get_candidate(cst[1], path, collapse_lvar) if cst[0] == 'disj': if path[0] == 0: return get_candidate(cst[1], path[1:], collapse_lvar) else: return get_candidate(cst[2], path[1:], collapse_lvar) assert len(path) == 0 and cst[0] == 'pause' state = get_candidate_from_pause(cst) return lisp.unparse(state, collapse_lvar)
def get_candidates(cst, current_path=None, candidate=None): """Retrieve all (path, candidate) pairs in a cst. Each path is encoded as an array of 0 / 1 indicating to go left / right at a disjunction. Each candidate is stringified. 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 current_path (optional) -- accumulator variable for recursion indicating path from the root to the current cst node candidate (optional) -- accumulator variable for recursion indicating current candidate """ if current_path is None: current_path = [] if type(cst) == list: label = cst[0] if label == 'pause': candidate = get_candidate_from_pause(cst) candidate = lisp.unparse(candidate, True) return get_candidates(cst[2], current_path, candidate) if label == 'state': raise ValueError("Should not have state") if label == 'conj': # only go left return get_candidates(cst[1], current_path, candidate) if label == 'disj': return get_candidates(cst[1], current_path + [0], candidate) + \ get_candidates(cst[2], current_path + [1], candidate) return [(current_path, candidate)]
def _send(self, datum): """Helper frunction to write to the scheme process. Arguments: datum -- the content of the message to be relayed, as a parsed lisp data structure. """ self.proc.stdin.write((lisp.unparse(datum) + '\n').encode('utf-8')) self.proc.stdin.flush()
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?")