def flooding(S, E, T, graph): queue = list() queue.append({'stack': S.copy(), 'trace': T.copy()}) seen_edges = set() while True: path = queue.pop(0) cur_stack = path['stack'] cur_trace = path['trace'] cur_node = cur_stack[-1] # check for bad test case if len(cur_trace) > 2 * len(graph.edges): continue # check if all edges covered if len(E - seen_edges) == 0: queue.append(path) return queue if cur_node.label == 'acc': queue.append(path.copy()) continue # check if there is a reduce rule that may exist for this state reduce_edges = [] if cur_node.reduce_rule is not None: for rule in cur_node.reduce_rule: reduce_edges.append( get_reduce_edge(rule[0], rule[1], cur_node, cur_stack)) # check if the previous move was reduce so we should shift a non-terminal if len(cur_trace) > 0 and cur_trace[-1].is_pop: shift_edge: Edge for edge in cur_node.edges: if edge.label == cur_trace[-1].label and not edge.is_pop: shift_edge = edge break seen_edges.add(shift_edge) push(cur_stack, cur_trace, queue, shift_edge) else: # if the previous step wasn't a reduce then we should shift on terminals and check # for valid reductions on all edges for edge in cur_node.edges: # avoid shifting on an self-loop if edge.label in graph.terminal and ( edge.next_node != cur_node or edge not in seen_edges): seen_edges.add(edge) push(cur_stack, cur_trace, queue, edge) # check that reduction edge is correct edge elif edge.label in graph.nonterminal and edge in reduce_edges: for rule_index in range(len(reduce_edges)): if edge == reduce_edges[rule_index]: # pop nodes of stack pop(cur_stack, cur_trace, queue, edge, cur_node.reduce_rule[rule_index][1]) seen_edges.add(edge)
def path_completion(S, T, goal_label, graph): queue = list() queue.append({'stack': S.copy(), 'trace': T.copy()}) while len(queue) > 0: path = queue.pop(0) cur_stack = path['stack'] cur_trace = path['trace'] cur_node = cur_stack[-1] # check if the destination has been reached if str(cur_node.label) == goal_label: return path # check for bad test case if len(cur_trace) > 2 * len(graph.edges): continue # check if there is a reduce rule that may exist for this state reduce_edges = [] if cur_node.reduce_rule is not None: for rule in cur_node.reduce_rule: reduce_edges.append( get_reduce_edge(rule[0], rule[1], cur_node, cur_stack)) # check if the previous move was pop so we should shift a non-terminal if len(cur_trace) > 0 and cur_trace[-1].is_pop: shift_edge: Edge for edge in cur_node.edges: if edge.label == cur_trace[-1].label and not edge.is_pop: shift_edge = edge break push(cur_stack, cur_trace, queue, shift_edge) else: # if the previous step wasn't a pop then we should shift on terminals and check # for valid reductions on all edges for edge in cur_node.edges: if edge.label in graph.terminal and edge.next_node != cur_node: push(cur_stack, cur_trace, queue, edge) # check that reduction edge is correct edge elif edge.label in graph.nonterminal and edge in reduce_edges: # pop nodes of stack for rule_index in range(len(reduce_edges)): if edge == reduce_edges[rule_index]: # pop nodes of stack pop(cur_stack, cur_trace, queue, edge, cur_node.reduce_rule[rule_index][1])
def lookback_for_pop(candidate, graph, unseen_edges): target_stack_depth = candidate.pop_count src = candidate.next_node target = candidate.source possible_solutions = [] max_len = len(graph.edges) queue = list() queue.append({'stack': [src], 'trace': []}) while len(queue) > 0: path = queue.pop(0) cur_stack = path['stack'] cur_trace = path['trace'] cur_node = cur_stack[-1] # check for bad test case if len(cur_trace) > max_len: continue if cur_node == target and len(cur_stack) - 1 == target_stack_depth: possible_solutions.append(path.copy()) max_len = len(cur_trace) continue # check if there is a reduce rule that may exist for this state reduce_edges = [] if cur_node.reduce_rule is not None: for rule in cur_node.reduce_rule: pop_amount = rule[1] # don't take excessively large pops if len(cur_stack) >= pop_amount + 1: reduce_edges.append( get_reduce_edge(rule[0], rule[1], cur_node, cur_stack)) # check if the previous move was reduce so we should shift a non-terminal if len(cur_trace) > 0 and cur_trace[-1].is_pop: shift_edge: Edge for edge in cur_node.edges: if edge.label == cur_trace[-1].label and not edge.is_pop: shift_edge = edge break push(cur_stack, cur_trace, queue, shift_edge) else: # if the previous step wasn't a reduce then we should shift on terminals and check # for valid reductions on all edges for edge in cur_node.edges: if edge.label in graph.terminal: push(cur_stack, cur_trace, queue, edge) # check that reduction edge is correct edge elif edge.label in graph.nonterminal and edge in reduce_edges: for rule_index in range(len(reduce_edges)): if edge == reduce_edges[rule_index]: # pop nodes of stack pop(cur_stack, cur_trace, queue, edge, pop_amount) # find the possible solution that covers as many new edges as possible best_path = possible_solutions[0] new_count = 0 for e in best_path: if e in unseen_edges: new_count += 1 for path in possible_solutions[1:]: count = 0 for e in path: if e in unseen_edges: count += 1 if count > new_count: new_count = count best_path = path return best_path
def path_completion_improved(S, T, goal_label, graph): """Complete a path that was generated during the flooding phase""" high_priority_queue = list() low_priority_queue = list() stack_depth_dict = precalc_node_stack_depth(T) high_priority_queue.append({ 'stack': S.copy(), 'trace': T.copy(), 'stack_history': stack_depth_dict }) while len(high_priority_queue) + len(low_priority_queue) > 0: path = None if len(high_priority_queue) > 0: path = high_priority_queue.pop(0) else: path = low_priority_queue.pop(0) cur_stack = path['stack'] cur_trace = path['trace'] cur_node = cur_stack[-1] cur_stack_history = path['stack_history'] # check if the destination has been reached if str(cur_node.label) == goal_label: return path # check for bad test case if len(cur_trace) > 2 * len(graph.edges): continue # check if there is a reduce rule that may exist for this state reduce_edges = [] if cur_node.reduce_rule is not None: for rule in cur_node.reduce_rule: reduce_edges.append( get_reduce_edge(rule[0], rule[1], cur_node, cur_stack)) # check if the previous move was pop so we should shift a non-terminal if len(cur_trace) > 0 and cur_trace[-1].is_pop: shift_edge: Edge for edge in cur_node.edges: if edge.label == cur_trace[-1].label and not edge.is_pop: shift_edge = edge break # always push even if this is a loop push(cur_stack, cur_trace, high_priority_queue, shift_edge, cur_stack_history=cur_stack_history) else: # if the previous step wasn't a pop then we should shift on terminals and check # for valid reductions on all edges stuck_flag = True for edge in cur_node.edges: if edge.label in graph.terminal: # if this is a loop we only take it if the stack does not increase in size if edge.next_node.label not in cur_stack_history or ( edge.next_node.label in cur_stack_history and cur_stack_history[edge.next_node.label] >= len(cur_stack) + 1): push(cur_stack, cur_trace, high_priority_queue, edge, cur_stack_history=cur_stack_history) else: push(cur_stack, cur_trace, low_priority_queue, edge, cur_stack_history=cur_stack_history) # check that reduction edge is correct edge elif edge.label in graph.nonterminal and edge in reduce_edges: # pop nodes of stack for rule_index in range(len(reduce_edges)): if edge == reduce_edges[rule_index]: # if this is a loop we only take it if the stack does not increase in size if edge.next_node.label not in cur_stack_history or ( edge.next_node.label in cur_stack_history and cur_stack_history[edge.next_node.label] >= len(cur_stack) - edge.pop_count): # pop nodes of stack pop(cur_stack, cur_trace, high_priority_queue, edge, cur_node.reduce_rule[rule_index][1], cur_stack_history=cur_stack_history) else: pop(cur_stack, cur_trace, low_priority_queue, edge, cur_node.reduce_rule[rule_index][1], cur_stack_history=cur_stack_history)
def flooding_improved(S, E, T, graph): fresh_queue = list( ) # traversals that have just covered a previously unseen edge stale_queue = list( ) # traversal that has covered a previously unseen edge but now has no more unseen edges to cover brute_force_queue = list( ) # traversal that covers an already seen edge as its last edge. Used when no fresh_queue fresh_queue.append({'stack': S.copy(), 'trace': T.copy()}) seen_edges = set() while True: new_edge_seen = False path_from_brute = False path = None if len(fresh_queue) > 0: path = fresh_queue.pop(0) else: unblock(S, E, T, graph, seen_edges, fresh_queue, stale_queue) continue cur_stack = path['stack'] cur_trace = path['trace'] cur_node = cur_stack[-1] # check for bad test case if len(cur_trace) > 2 * len(graph.edges): continue # check if all edges covered if len(E - seen_edges) == 0: stale_queue.append(path) return stale_queue + fresh_queue if cur_node.label == 'acc': stale_queue.append(path.copy()) continue # check if there is a reduce rule that may exist for this state reduce_edges = [] if cur_node.reduce_rule is not None: for rule in cur_node.reduce_rule: reduce_edges.append( get_reduce_edge(rule[0], rule[1], cur_node, cur_stack)) # check if the previous move was reduce so we should shift a non-terminal if len(cur_trace) > 0 and cur_trace[-1].is_pop: shift_edge: Edge for edge in cur_node.edges: if edge.label == cur_trace[-1].label and not edge.is_pop: shift_edge = edge break if shift_edge not in seen_edges: push(cur_stack, cur_trace, fresh_queue, shift_edge) new_edge_seen = True else: push(cur_stack, cur_trace, brute_force_queue, shift_edge) seen_edges.add(shift_edge) else: # if the previous step wasn't a reduce then we should shift on terminals and check # for valid reductions on all edges for edge in cur_node.edges: # avoid shifting on an self-loop if edge.label in graph.terminal: if edge not in seen_edges: new_edge_seen = True push(cur_stack, cur_trace, fresh_queue, edge) else: push(cur_stack, cur_trace, brute_force_queue, edge) seen_edges.add(edge) # check that reduction edge is correct edge elif edge.label in graph.nonterminal and edge in reduce_edges: for rule_index in range(len(reduce_edges)): if edge == reduce_edges[rule_index]: # pop nodes of stack if edge not in seen_edges: new_edge_seen = True pop(cur_stack, cur_trace, fresh_queue, edge, cur_node.reduce_rule[rule_index][1]) else: pop(cur_stack, cur_trace, brute_force_queue, edge, cur_node.reduce_rule[rule_index][1]) seen_edges.add(edge) if not new_edge_seen and not path_from_brute: stale_queue.append(path)