static void setReportId(ReportManager &rm, NGWrapper &g, NFAVertex v, s32 adj) { // Don't try and set the report ID of a special vertex. assert(!is_special(v, g)); // There should be no reports set already. assert(g[v].reports.empty()); Report r = rm.getBasicInternalReport(g, adj); g[v].reports.insert(rm.getInternalId(r)); DEBUG_PRINTF("set report id for vertex %u, adj %d\n", g[v].index, adj); }
/** \brief Replace the graph's reports with new reports that specify bounds. */ static void updateReportBounds(ReportManager &rm, NGWrapper &g, NFAVertex accept, set<NFAVertex> &done) { for (auto v : inv_adjacent_vertices_range(accept, g)) { // Don't operate on g.accept itself. if (v == g.accept) { assert(accept == g.acceptEod); continue; } // Don't operate on a vertex we've already done. if (contains(done, v)) { continue; } done.insert(v); flat_set<ReportID> new_reports; auto &reports = g[v].reports; for (auto id : reports) { Report ir = rm.getReport(id); // make a copy assert(!ir.hasBounds()); // Note that we need to cope with offset adjustment here. ir.minOffset = g.min_offset - ir.offsetAdjust; if (g.max_offset == MAX_OFFSET) { ir.maxOffset = MAX_OFFSET; } else { ir.maxOffset = g.max_offset - ir.offsetAdjust; } assert(ir.maxOffset >= ir.minOffset); ir.minLength = g.min_length; if (g.min_length && !g.som) { ir.quashSom = true; } DEBUG_PRINTF("id %u -> min_offset=%llu, max_offset=%llu, " "min_length=%llu\n", id, ir.minOffset, ir.maxOffset, ir.minLength); new_reports.insert(rm.getInternalId(ir)); } DEBUG_PRINTF("swapping reports on vertex %u\n", g[v].index); reports.swap(new_reports); } }
static void getHighlanderReporters(const NGHolder &g, const NFAVertex accept, const ReportManager &rm, set<NFAVertex> &verts) { for (auto v : inv_adjacent_vertices_range(accept, g)) { if (v == g.accept) { continue; } const auto &reports = g[v].reports; if (reports.empty()) { assert(0); continue; } // Must be _all_ highlander callback reports. for (auto report : reports) { const Report &ir = rm.getReport(report); if (ir.ekey == INVALID_EKEY || ir.type != EXTERNAL_CALLBACK) { goto next_vertex; } // If there's any bounds, these are handled outside the NFA and // probably shouldn't be pre-empted. if (ir.hasBounds()) { goto next_vertex; } } verts.insert(v); next_vertex: continue; } }
SourceManager::SourceManager(StringPool &strings, ReportManager &reports) : strings_(strings), rr_(reports), next_source_id_(1), last_lookup_(0) { reports.setSourceManager(this); }
static bool hasOffsetAdjustments(const ReportManager &rm, const NGHolder &g) { for (auto report : all_reports(g)) { const Report &ir = rm.getReport(report); if (ir.offsetAdjust) { return true; } } return false; }
int main(int argc, char **argv) { args::Parser parser("Documentation generator."); args::StringOption filename(parser, "filename", "SourcePawn file to scan for documentation."); StringPool strings; ReportManager reports; SourceManager source(strings, reports); if (!parser.parse(argc, argv)) { parser.usage(stderr, argc, argv); return 1; } PoolAllocator pool; { PoolScope scope(pool); CompileContext cc(pool, strings, reports, source); cc.SkipResolution(); const char* file = filename.value().chars(); JsonObject *obj = Run(cc, file); if (!obj) { reports.PrintMessages(); return 1; } JsonRenderer renderer(stdout); renderer.Render(obj); if (reports.HasMessages()) reports.PrintMessages(); } return 0; }
static bool hasOffsetAdjust(const ReportManager &rm, NGWrapper &g, int *adjust) { const auto &reports = all_reports(g); if (reports.empty()) { assert(0); return false; } int offsetAdjust = rm.getReport(*reports.begin()).offsetAdjust; for (auto report : reports) { const Report &ir = rm.getReport(report); if (ir.offsetAdjust != offsetAdjust) { DEBUG_PRINTF("different adjusts!\n"); return false; } } *adjust = offsetAdjust; return true; }
void dumpReportManager(const ReportManager &rm, const Grey &grey) { if (!grey.dumpFlags) { return; } stringstream ss; ss << grey.dumpPath << "internal_reports.txt"; FILE *f = fopen(ss.str().c_str(), "w"); const vector<Report> &reports = rm.reports(); for (u32 i = 0; i < reports.size(); i++) { const Report &ir = reports[i]; fprintf(f, "int %u: %s onmatch: %u", i, irTypeToString(ir.type), ir.onmatch); u32 dkey = rm.getDkey(ir); if (dkey != MO_INVALID_IDX) { fprintf(f, " dkey %u", dkey); } if (ir.ekey != MO_INVALID_IDX) { fprintf(f, " ekey %u", ir.ekey); } if (ir.hasBounds()) { fprintf(f, " hasBounds (minOffset=%llu, maxOffset=%llu, " "minLength=%llu)", ir.minOffset, ir.maxOffset, ir.minLength); } if (ir.offsetAdjust != 0) { fprintf(f, " offsetAdjust: %d", ir.offsetAdjust); } if (isReverseNfaReport(ir)) { fprintf(f, " reverse nfa: %u", ir.revNfaIndex); } if (isSomRelSetReport(ir)) { fprintf(f, " set, adjust: %lld", ir.somDistance); } fprintf(f, "\n"); } fclose(f); }
/** \brief Find the (min, max) offset adjustment for the reports on a given * vertex. */ static pair<s32,s32> getMinMaxOffsetAdjust(const ReportManager &rm, const NGHolder &g, NFAVertex v) { s32 minAdj = 0, maxAdj = 0; const auto &reports = g[v].reports; for (auto ri = reports.begin(), re = reports.end(); ri != re; ++ri) { const Report &ir = rm.getReport(*ri); if (ri == reports.begin()) { minAdj = ir.offsetAdjust; maxAdj = ir.offsetAdjust; } else { minAdj = min(minAdj, ir.offsetAdjust); maxAdj = max(maxAdj, ir.offsetAdjust); } } return make_pair(minAdj, maxAdj); }
set<u32> reportsToEkeys(const set<ReportID> &reports, const ReportManager &rm) { assert(!reports.empty()); set<u32> ekeys; for (auto it = reports.begin(), ite = reports.end(); it != ite; ++it) { u32 e = rm.getReport(*it).ekey; if (it == reports.begin()) { if (e != INVALID_EKEY) { ekeys.insert(e); } } else { ekeysUnion(&ekeys, e); } } return ekeys; }
/** * True if the vertex has (a) a self-loop, (b) only out-edges to accept and * itself and (c) only simple exhaustible reports. */ static bool hasOnlySelfLoopAndExhaustibleAccepts(const NGHolder &g, const ReportManager &rm, NFAVertex v) { if (!edge(v, v, g).second) { return false; } for (auto w : adjacent_vertices_range(v, g)) { if (w != v && w != g.accept) { return false; } } for (const auto &report_id : g[v].reports) { if (!isSimpleExhaustible(rm.getReport(report_id))) { return false; } } return true; }
/** Remove any edges from vertices that generate accepts (for Highlander * graphs). */ void pruneHighlanderAccepts(NGHolder &g, const ReportManager &rm) { // Safety check: all reports must be simple exhaustible reports, or this is // not safe. This optimisation should be called early enough that no // internal reports have been added. for (auto report_id : all_reports(g)) { const Report &ir = rm.getReport(report_id); if (ir.ekey == INVALID_EKEY || ir.hasBounds() || !isExternalReport(ir)) { DEBUG_PRINTF("report %u is not external highlander with " "no bounds\n", report_id); return; } } vector<NFAEdge> dead; for (auto u : inv_adjacent_vertices_range(g.accept, g)) { if (is_special(u, g)) { continue; } // We can prune any out-edges that aren't accepts for (const auto &e : out_edges_range(u, g)) { if (!is_any_accept(target(e, g), g)) { dead.push_back(e); } } } if (dead.empty()) { return; } DEBUG_PRINTF("found %zu removable edges due to single match\n", dead.size()); remove_edges(dead, g); pruneUseless(g); }
void pruneHighlanderDominated(NGHolder &g, const ReportManager &rm) { vector<NFAVertex> reporters; for (auto v : inv_adjacent_vertices_range(g.accept, g)) { for (const auto &report_id : g[v].reports) { const Report &r = rm.getReport(report_id); if (isSimpleExhaustible(r)) { reporters.push_back(v); break; } } } for (auto v : inv_adjacent_vertices_range(g.acceptEod, g)) { for (const auto &report_id : g[v].reports) { const Report &r = rm.getReport(report_id); if (isSimpleExhaustible(r)) { reporters.push_back(v); break; } } } if (reporters.empty()) { return; } sort(begin(reporters), end(reporters), make_index_ordering(g)); reporters.erase(unique(begin(reporters), end(reporters)), end(reporters)); DEBUG_PRINTF("%zu vertices have simple exhaustible reports\n", reporters.size()); const auto &dom = findDominators(g); bool modified = false; // If a reporter vertex is dominated by another with the same report, we // can remove that report; if all reports are removed, we can remove the // vertex entirely. for (const auto v : reporters) { const auto reports = g[v].reports; // copy, as we're going to mutate for (const auto &report_id : reports) { if (!isSimpleExhaustible(rm.getReport(report_id))) { continue; } if (isDominatedByReporter(g, dom, v, report_id)) { DEBUG_PRINTF("removed dominated report %u from vertex %u\n", report_id, g[v].index); g[v].reports.erase(report_id); } } if (g[v].reports.empty()) { DEBUG_PRINTF("removed edges to accepts from %u, no reports left\n", g[v].index); remove_edge(v, g.accept, g); remove_edge(v, g.acceptEod, g); modified = true; } } // If a reporter vertex has a self-loop, but otherwise only leads to accept // (note: NOT acceptEod) and has simple exhaustible reports, we can delete // the self-loop. for (const auto v : reporters) { if (hasOnlySelfLoopAndExhaustibleAccepts(g, rm, v)) { remove_edge(v, v, g); modified = true; DEBUG_PRINTF("removed self-loop on %u\n", g[v].index); } } if (!modified) { return; } pruneUseless(g); // We may have only removed self-loops, in which case pruneUseless wouldn't // renumber, so we do edge renumbering explicitly here. g.renumberEdges(); }