diff --git a/src/rose/rose_build_bytecode.cpp b/src/rose/rose_build_bytecode.cpp index 8b397c52..6d485b0c 100644 --- a/src/rose/rose_build_bytecode.cpp +++ b/src/rose/rose_build_bytecode.cpp @@ -1160,7 +1160,11 @@ bool buildLeftfixes(RoseBuildImpl &tbi, build_context &bc, u32 lag = g[v].left.lag; bool is_transient = contains(tbi.transient, leftfix); - if (is_transient && tbi.cc.grey.roseLookaroundMasks) { + // Transient leftfixes can sometimes be implemented solely with + // lookarounds, in which case we don't need to build an engine. + // TODO: Handle SOM-tracking cases as well. + if (cc.grey.roseLookaroundMasks && is_transient && + !g[v].left.tracksSom()) { vector lookaround; if (makeLeftfixLookaround(tbi, v, lookaround)) { DEBUG_PRINTF("implementing as lookaround!\n"); diff --git a/src/rose/rose_build_lookaround.cpp b/src/rose/rose_build_lookaround.cpp index 54c01e08..a06bacef 100644 --- a/src/rose/rose_build_lookaround.cpp +++ b/src/rose/rose_build_lookaround.cpp @@ -538,6 +538,36 @@ void findLookaroundMasks(const RoseBuildImpl &tbi, const RoseVertex v, } } +static +bool hasSingleFloatingStart(const NGHolder &g) { + NFAVertex initial = NGHolder::null_vertex(); + for (auto v : adjacent_vertices_range(g.startDs, g)) { + if (v == g.startDs) { + continue; + } + if (initial != NGHolder::null_vertex()) { + DEBUG_PRINTF("more than one start\n"); + return false; + } + initial = v; + } + + if (initial == NGHolder::null_vertex()) { + DEBUG_PRINTF("no floating starts\n"); + return false; + } + + // Anchored start must have no successors other than startDs and initial. + for (auto v : adjacent_vertices_range(g.start, g)) { + if (v != initial && v != g.startDs) { + DEBUG_PRINTF("anchored start\n"); + return false; + } + } + + return true; +} + static bool getTransientPrefixReach(const NGHolder &g, u32 lag, map &look) { @@ -546,15 +576,9 @@ bool getTransientPrefixReach(const NGHolder &g, u32 lag, return false; } - // Currently we don't handle anchored prefixes, as we would need to be able - // to represent the bounds from the anchor as well. - if (out_degree(g.start, g) != 1) { - DEBUG_PRINTF("anchored\n"); - return false; - } - - if (out_degree(g.startDs, g) != 2) { - DEBUG_PRINTF("more than one start\n"); + // Must be a floating chain wired to startDs. + if (!hasSingleFloatingStart(g)) { + DEBUG_PRINTF("not a single floating start\n"); return false; } @@ -569,12 +593,28 @@ bool getTransientPrefixReach(const NGHolder &g, u32 lag, look[0 - i] = g[v].char_reach; - if (in_degree(v, g) != 1) { + NFAVertex next = NGHolder::null_vertex(); + for (auto u : inv_adjacent_vertices_range(v, g)) { + if (u == g.start) { + continue; // Benign, checked by hasSingleFloatingStart + } + if (next == NGHolder::null_vertex()) { + next = u; + continue; + } DEBUG_PRINTF("branch\n"); return false; } - v = *(inv_adjacent_vertices(v, g).first); + if (next == NGHolder::null_vertex() || next == v) { + DEBUG_PRINTF("no predecessor or only self-loop\n"); + // This graph is malformed -- all vertices in a graph that makes it + // to this analysis should have predecessors. + assert(0); + return false; + } + + v = next; i++; }