コード例 #1
0
static void generateNFAForSubtree(NFA& nfa, unsigned nfaRootId, PrefixTreeVertex& root, size_t maxNFASize)
{
    // This recurses the subtree of the prefix tree.
    // For each edge that has fixed length (no quantifiers like ?, *, or +) it generates the nfa graph,
    // recurses into children, and deletes any processed leaf nodes.
    struct ActiveSubtree {
        ActiveSubtree(PrefixTreeVertex& vertex, unsigned nfaNodeId, unsigned edgeIndex)
            : vertex(vertex)
            , nfaNodeId(nfaNodeId)
            , edgeIndex(edgeIndex)
        {
        }
        PrefixTreeVertex& vertex;
        unsigned nfaNodeId;
        unsigned edgeIndex;
    };
    Vector<ActiveSubtree> stack;
    if (!root.edges.isEmpty())
        stack.append(ActiveSubtree(root, nfaRootId, 0));
    bool nfaTooBig = false;
    
    // Generate graphs for each subtree that does not contain any quantifiers.
    while (!stack.isEmpty()) {
        PrefixTreeVertex& vertex = stack.last().vertex;
        const unsigned edgeIndex = stack.last().edgeIndex;
        
        // Only stop generating an NFA at a leaf to ensure we have a correct NFA. We could go slightly over the maxNFASize.
        if (vertex.edges.isEmpty() && nfa.graphSize() > maxNFASize)
            nfaTooBig = true;
        
        if (edgeIndex < vertex.edges.size()) {
            auto& edge = vertex.edges[edgeIndex];
            
            // Clean up any processed leaves and return early if we are past the maxNFASize.
            if (nfaTooBig) {
                stack.last().edgeIndex = stack.last().vertex.edges.size();
                continue;
            }
            
            // Quantified edges in the subtree will be a part of another NFA.
            if (!edge.term.hasFixedLength()) {
                stack.last().edgeIndex++;
                continue;
            }
            
            unsigned subtreeRootId = edge.term.generateGraph(nfa, stack.last().nfaNodeId, edge.child->finalActions);
            ASSERT(edge.child.get());
            stack.append(ActiveSubtree(*edge.child.get(), subtreeRootId, 0));
        } else {
            ASSERT(edgeIndex == vertex.edges.size());
            vertex.edges.removeAllMatching([](PrefixTreeEdge& edge)
            {
                return edge.term.isDeletedValue();
            });
            stack.removeLast();
            if (!stack.isEmpty()) {
                auto& activeSubtree = stack.last();
                auto& edge = activeSubtree.vertex.edges[stack.last().edgeIndex];
                if (edge.child->edges.isEmpty())
                    edge.term = Term(Term::DeletedValue); // Mark this leaf for deleting.
                activeSubtree.edgeIndex++;
            }
        }
    }
}