Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  regexp-graph-printer.cc   Sprache: C

 

// Copyright 2026 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef V8_ENABLE_REGEXP_DIAGNOSTICS

#include "irregexp/imported/regexp-graph-printer.h"

#include <iomanip>
#include <queue>
#include <unordered_set>

#include "irregexp/imported/regexp-ast-printer.h"
#include "irregexp/imported/regexp-compiler.h"
#include "irregexp/imported/regexp-node-printer.h"

namespace v8 {
namespace internal {
namespace regexp {

class GraphPrinter::Scheduler final : public NodeVisitor {
 public:
  auto begin() { return schedule_.begin(); }
  auto end() { return schedule_.end(); }
  size_t size() { return schedule_.size(); }
  auto operator[](size_t index) { return schedule_[index]; }
  Node* at(size_t index) { return schedule_[index]; }
  const std::vector<Node*>& successors(Node* node) { return successors_[node]; }
  // Returns true iff |n1| is scheduled before |n2|.
  bool ScheduledBefore(const Node* n1, const Node* n2) {
    auto it1 = std::find(begin(), end(), n1);
    if (it1 == end()) return false;
    auto it2 = std::find(begin(), end(), n2);
    if (it2 == end()) return false;
    return std::distance(it1, it2) > 0;
  }

#define DECLARE_VISIT(Type) void Visit##Type(Type##Node* that) override;
  FOR_EACH_NODE_TYPE(DECLARE_VISIT)
#undef DECLARE_VISIT
  void Visit(Node* node);
  void CreateSchedule(Node* root) {
    // TODO(pthier): Improve schedule to reduce number of edges in printer (I.e.
    // prefer to schedule success nodes directly after nodes).
    Visit(root);
    std::set<Node*> visited;
    std::queue<Node*> queue;
    for (auto n : nodes_) {
      auto it = depends_on_.find(n);
      if (it == depends_on_.end() || it->second.size() == 0) {
        visited.insert(n);
        queue.push(n);
      }
    }
    while (!queue.empty()) {
      Node* node = queue.front();
      queue.pop();
      schedule_.push_back(node);
      for (auto successor : successors_[node]) {
        depends_on_[successor].erase(node);
        if (depends_on_[successor].size() == 0) {
          if (!visited.contains(successor)) {
            visited.insert(successor);
            queue.push(successor);
          }
        }
      }
    }
    DCHECK_EQ(nodes_.size(), visited.size());
  }

 private:
  void VisitSeqNode(SeqNode* node);
  void VisitOther(Node* node);
  void AddEdge(Node* from, Node* to) {
    if (IsReachable(to, from)) return;
    successors_[from].push_back(to);
    depends_on_[to].insert(from);
  }

  bool IsReachable(Node* start, Node* end) {
    if (start == end) return true;

    std::queue<Node*> queue;
    std::unordered_set<Node*> visited;

    queue.push(start);
    visited.insert(start);

    while (!queue.empty()) {
      Node* current = queue.front();
      queue.pop();

      if (current == end) return true;

      auto it = successors_.find(current);
      if (it != successors_.end()) {
        for (Node* successor : it->second) {
          if (visited.find(successor) == visited.end()) {
            visited.insert(successor);
            queue.push(successor);
          }
        }
      }
    }
    return false;
  }
  std::unordered_set<Node*> visited_;
  std::vector<Node*> nodes_;
  std::unordered_map<Node*, std::set<Node*>> depends_on_;
  // This is not strictly necessary. We could also use the NodeVisitor again to
  // iterate successors, but it's easier if we just have them independent of the
  // node type in one place.
  std::unordered_map<Node*, std::vector<Node*>> successors_;
  std::vector<Node*> schedule_;
};

void GraphPrinter::Scheduler::Visit(Node* node) {
  if (visited_.contains(node)) return;
  visited_.insert(node);
  nodes_.push_back(node);
  node->Accept(this);
}

void GraphPrinter::Scheduler::VisitSeqNode(SeqNode* node) {
  AddEdge(node, node->on_success());
  Visit(node->on_success());
}

void GraphPrinter::Scheduler::VisitOther(Node* node) {
  // Nothing to do.
}

void GraphPrinter::Scheduler::VisitEnd(EndNode* node) { VisitOther(node); }

void GraphPrinter::Scheduler::VisitAction(ActionNode* node) {
  VisitSeqNode(node->AsSeqNode());
}

void GraphPrinter::Scheduler::VisitUnanchoredAdvance(
    UnanchoredAdvanceNode* node) {
  VisitSeqNode(node->AsSeqNode());
}

void GraphPrinter::Scheduler::VisitChoice(ChoiceNode* node) {
  ZoneList<GuardedAlternative>* alternatives = node->alternatives();
  for (int i = 0; i < alternatives->length(); i++) {
    const GuardedAlternative& guard = alternatives->at(i);
    AddEdge(node, guard.node());
    Visit(guard.node());
  }
}

void GraphPrinter::Scheduler::VisitLoopChoice(LoopChoiceNode* node) {
  AddEdge(node, node->loop_node());
  Visit(node->loop_node());
  AddEdge(node, node->continue_node());
  Visit(node->continue_node());
}

void GraphPrinter::Scheduler::VisitNegativeLookaroundChoice(
    NegativeLookaroundChoiceNode* node) {
  VisitChoice(node);
}

void GraphPrinter::Scheduler::VisitBackReference(BackReferenceNode* node) {
  VisitSeqNode(node->AsSeqNode());
}

void GraphPrinter::Scheduler::VisitAssertion(AssertionNode* node) {
  VisitSeqNode(node->AsSeqNode());
}

void GraphPrinter::Scheduler::VisitText(TextNode* node) {
  VisitSeqNode(node->AsSeqNode());
}

namespace {

// TODO(pthier): Some of the helpers are equal or at least very similar to
// helpers in the Maglev graph printer. Factor out a common header.

// Add a target to the target list in the first non-null position from the end.
// This might have to extend the target list if there is no free spot.
size_t AddTarget(std::vector<Node*>& targets, Node* target) {
  if (targets.size() == 0 || (targets.back() != nullptr)) {
    targets.push_back(target);
    return targets.size() - 1;
  }

  size_t i = targets.size();
  while (i > 0) {
    if (targets[i - 1] != nullptr) break;
    i--;
  }
  targets[i] = target;
  return i;
}

// If the target is not a fallthrough, add i to the target list in the first
// non-null position from the end. This might have to extend the target list if
// there is no free spot. Returns true if it was added, false if it was a
// fallthrough.
bool AddTargetIfNotNext(std::vector<Node*>& targets, Node* target, Node* next,
                        std::set<size_t>* arrows_starting_here = nullptr) {
  if (next == target) return false;
  size_t index = AddTarget(targets, target);
  if (arrows_starting_here != nullptr) arrows_starting_here->insert(index);
  return true;
}

enum ConnectionLocation {
  kTop = 1 << 0,
  kLeft = 1 << 1,
  kRight = 1 << 2,
  kBottom = 1 << 3,
  kHorizontal = kLeft | kRight,
  kVertical = kTop | kBottom
};

struct Connection {
  void Connect(ConnectionLocation loc) { connected |= loc; }
  bool IsConnected(ConnectionLocation loc) { return (connected & loc) == loc; }

  void AddHorizontal() { Connect(kHorizontal); }

  void AddVertical() { Connect(kVertical); }

  const char* ToString() const {
    switch (connected) {
      case 0:
        return " ";
      case kTop:
        return "╵";
      case kLeft:
        return "╴";
      case kRight:
        return "╶";
      case kBottom:
        return "╷";
      case kTop | kLeft:
        return "╯";
      case kTop | kRight:
        return "╰";
      case kBottom | kLeft:
        return "╮";
      case kBottom | kRight:
        return "╭";
      case kTop | kBottom:
        return "│";
      case kLeft | kRight:
        return "─";
      case kTop | kBottom | kLeft:
        return "┤";
      case kTop | kBottom | kRight:
        return "├";
      case kLeft | kRight | kTop:
        return "┴";
      case kLeft | kRight | kBottom:
        return "┬";
      case kTop | kLeft | kRight | kBottom:
        return "┼";
    }
    UNREACHABLE();
  }

  uint8_t connected = 0;
};

std::ostream& operator<<(std::ostream& os, const Connection& c) {
  return os << c.ToString();
}

int IntWidth(int val) {
  if (val == -1) return 2;
  return std::ceil(std::log10(val + 1));
}

int MaxIdWidth(int max_node_id, int padding_adjustment = 0) {
  int max_width = IntWidth(max_node_id) + 2;
  return max_width + padding_adjustment;
}

void PrintPaddedId(std::ostream& os, GraphLabeller<Node>* labeller,
                   int max_node_id, Node* node, std::string padding = " ",
                   int padding_adjustment = 0) {
  int id_width = 0;
  if (node) {
    int id = labeller->NodeId(node);
    id_width = IntWidth(id);
  }
  int max_width = MaxIdWidth(max_node_id, padding_adjustment);
  int padding_width = std::max(0, max_width - id_width);
  for (int i = 0; i < padding_width; ++i) {
    os << padding;
  }
}

void PrintPadding(std::ostream& os, int max_node_id, std::string padding = " ",
                  int padding_adjustment = 0) {
  int id_width = 0;
  int max_width = MaxIdWidth(max_node_id, padding_adjustment);
  int padding_width = std::max(0, max_width - id_width);
  for (int i = 0; i < padding_width; ++i) {
    os << padding;
  }
}

}  // namespace

GraphPrinter::GraphPrinter(std::unique_ptr<NodePrinter<Node>> printer)
    : printer_(std::move(printer)) {}

GraphPrinter::~GraphPrinter() = default;

size_t GraphPrinter::AddEdge(Node* from, Node* to) {
  if (edges_.size() == 0 || (edges_.back().IsValid())) {
    edges_.push_back({from, to});
    return edges_.size() - 1;
  }

  size_t i = edges_.size();
  while (i > 0) {
    if (edges_[i - 1].IsValid()) break;
    i--;
  }
  edges_[i] = {from, to};
  return i;
}

bool GraphPrinter::AddEdgeIfNotNext(Node* from, Node* to, Node* next,
                                    std::set<size_t>* arrows_starting_here) {
  if (next == to) return false;
  size_t index = AddEdge(from, to);
  if (arrows_starting_here != nullptr) arrows_starting_here->insert(index);
  return true;
}

void GraphPrinter::PreSizeTargets() {
  std::fill(targets_.begin(), targets_.end(), nullptr);
  for (size_t i = 0; i < schedule_->size(); i++) {
    Node* node = schedule_->at(i);
    Node* next = i + 1 < schedule_->size() ? schedule_->at(i + 1) : nullptr;
    max_node_id_ = std::max(max_node_id_, labeller()->NodeId(node));
    std::replace(targets_.begin(), targets_.end(), node,
                 static_cast<Node*>(nullptr));
    std::for_each(edges_.begin(), edges_.end(), [&](Edge& e) {
      if (EdgeEndsAt(e, node)) {
        e.Invalidate();
      }
    });
    for (auto successor : schedule_->successors(node)) {
      bool is_loop = false;
      if (LoopChoiceNode* loop = successor->AsLoopChoiceNode();
          loop != nullptr) {
        if (loop->loop_node() == node) {
          is_loop = true;
          std::replace(targets_.begin(), targets_.end(),
                       static_cast<Node*>(loop), static_cast<Node*>(nullptr));
        }
      }
      if (!is_loop) {
        AddTargetIfNotNext(targets_, successor, next);
        AddEdgeIfNotNext(node, successor, next);
      }
    }
    if (LoopChoiceNode* loop_header = node->AsLoopChoiceNode();
        loop_header != nullptr) {
      AddTarget(targets_, loop_header);
      AddEdge(loop_header->loop_node(), node);
    }
  }
  DCHECK(std::all_of(edges_.begin(), edges_.end(),
                     [](Edge e) { return !e.IsValid(); }));
}

bool GraphPrinter::IsBackEdge(const Edge& edge) const {
  return schedule_->ScheduledBefore(edge.to(), edge.from());
}

bool GraphPrinter::EdgeStartsAt(const Edge& edge, const Node* node) const {
  if (!edge.IsValid() || node == nullptr) return false;
  const Node* start = IsBackEdge(edge) ? edge.to() : edge.from();
  return start == node;
}

bool GraphPrinter::EdgeEndsAt(const Edge& edge, const Node* node) const {
  if (!edge.IsValid() || node == nullptr) return false;
  const Node* end = IsBackEdge(edge) ? edge.from() : edge.to();
  return end == node;
}

bool GraphPrinter::IsTarget(const Node* node) const {
  return node != nullptr &&
         std::find_if(edges_.begin(), edges_.end(), [node](Edge e) {
           return e.to() == node;
         }) != edges_.end();
}
bool GraphPrinter::IsStart(const Node* node) const {
  return node != nullptr &&
         std::find_if(edges_.begin(), edges_.end(), [node](Edge e) {
           return e.from() == node;
         }) != edges_.end();
}

void GraphPrinter::PrintVerticalArrows(Node* current) {
  bool saw_start = false;
  bool saw_target = false;
  bool is_loop = false;
  int line_color = -1;
  int current_color = -1;
  for (size_t i = 0; i < edges_.size(); ++i) {
    Edge& e = edges_[i];
    int desired_color = line_color;
    Connection c;
    bool will_print_below = false;
    if (saw_start || saw_target) {
      c.AddHorizontal();
    }
    if (current != nullptr && e.from() == current) {
      if (IsTarget(current)) {
        will_print_below = true;
      } else {
        desired_color = (i % 6) + 1;
        line_color = desired_color;
        c.Connect(kRight);
        c.Connect(IsBackEdge(e) ? kTop : kBottom);
        saw_start = true;
        if (IsBackEdge(e)) {
          is_loop = true;
        }
      }
    }
    if (current != nullptr && e.to() == current) {
      desired_color = (i % 6) + 1;
      line_color = desired_color;
      c.Connect(kRight);
      c.Connect(IsBackEdge(e) ? kBottom : kTop);
      saw_target = true;
    }

    // Only add the vertical connection if there was no other connection.
    if (c.connected == 0 && edges_[i].IsValid() && !will_print_below) {
      desired_color = (i % 6) + 1;
      c.AddVertical();
    }
    if (v8_flags.log_colour && desired_color != current_color &&
        desired_color != -1) {
      os() << "\033[0;3" << desired_color << "m";
      current_color = desired_color;
    }
    os() << c;
  }
  if (saw_start && is_loop) {
    os() << "◄─";
    PrintPaddedId(os(), labeller(), max_node_id_, current,
                  !saw_target ? " " : "─", -2);
  } else {
    PrintPaddedId(os(), labeller(), max_node_id_, current,
                  !saw_target && !saw_start ? " " : "─");
  }
  os() << (saw_target ? "► " : (!IsTarget(current) ? "  " : "─ "));
}

void GraphPrinter::PrintVerticalArrowsBelow(Node* current,
                                            bool has_fallthrough) {
  if (!IsTarget(current) || !IsStart(current)) return;
  bool saw_start = false;
  bool saw_target = false;
  int line_color = -1;
  int current_color = -1;
  for (size_t i = 0; i < edges_.size(); ++i) {
    Edge& e = edges_[i];
    int desired_color = line_color;
    Connection c;
    if (saw_start || saw_target) {
      c.AddHorizontal();
    }
    if (e.from() == current) {
      if (IsTarget(current)) {
        desired_color = (i % 6) + 1;
        line_color = desired_color;
        c.Connect(kRight);
        c.Connect(kBottom);
        saw_start = true;
      }
    }

    // Only add the vertical connection if there was no other connection.
    if (c.connected == 0 && e.IsValid() && !EdgeEndsAt(e, current)) {
      desired_color = (i % 6) + 1;
      c.AddVertical();
    }
    if (v8_flags.log_colour && desired_color != current_color &&
        desired_color != -1) {
      os() << "\033[0;3" << desired_color << "m";
      current_color = desired_color;
    }
    os() << c;
  }
  if (saw_start) {
    PrintPadding(os(), max_node_id_, "─", 2);
    Connection c;
    c.Connect(kTop);
    c.Connect(kLeft);
    if (has_fallthrough) {
      c.Connect(kBottom);
    }
    os() << c;
  } else {
    if (v8_flags.log_colour) {
      os() << "\033[0m";
    }
  }
  os() << std::endl;
}

void GraphPrinter::PrintGraph(Node* root) {
  schedule_ = std::make_unique<Scheduler>();
  schedule_->CreateSchedule(root);
  PreSizeTargets();

  // Print Graph.
  for (size_t i = 0; i < schedule_->size(); i++) {
    Node* node = schedule_->at(i);
    max_node_id_ = std::max(max_node_id_, labeller()->NodeId(node));
    Node* next = i + 1 < schedule_->size() ? schedule_->at(i + 1) : nullptr;
    bool has_fallthrough = false;
    for (auto successor : schedule_->successors(node)) {
      if (LoopChoiceNode* loop = successor->AsLoopChoiceNode();
          loop != nullptr) {
        if (loop->loop_node() == node) {
          // The loop header is already a target, so we can get the target id
          // and skip adding it as a target below.
          continue;
        }
      }
      has_fallthrough |= !AddEdgeIfNotNext(node, successor, next);
    }
    if (LoopChoiceNode* loop_header = node->AsLoopChoiceNode();
        loop_header != nullptr) {
      AddEdge(loop_header->loop_node(), node);
    }
    PrintVerticalArrows(node);
    PrintNode(node);
    PrintVerticalArrowsBelow(node, has_fallthrough);
    std::for_each(edges_.begin(), edges_.end(), [&](Edge& e) {
      if (EdgeEndsAt(e, node)) e.Invalidate();
    });
    if (has_fallthrough) {
      PrintVerticalArrows();
      os() << std::setfill(' ') << std::setw(MaxIdWidth(max_node_id_, -4))
           << "";
      if (v8_flags.log_colour) {
        os() << "\033[0m";
      }
      if (IsStart(node) && IsTarget(node)) {
        if (v8_flags.log_colour) {
          int color = 0;
          for (size_t e = edges_.size() - 1; e >= 0; --e) {
            if (edges_[e].from() == node) {
              color = (e % 6) + 1;
              break;
            }
          }
          os() << "\033[0;3" << color << "m";
        }
        os() << "▼";
        if (v8_flags.log_colour) {
          os() << "\033[0m";
        }
      } else {
        os() << "↓";
      }
      os() << std::endl;
    }
  }
}

void GraphPrinter::PrintNode(Node* node) {
  PrintNodeNoNewline(node);
  os() << std::endl;
}

void GraphPrinter::PrintNodeNoNewline(Node* node) {
  node->Accept(printer());
  set_color(PrinterBase::Color::kDefault);
}

void GraphPrinter::PrintTrace(Trace* trace) {
  if (trace->is_trivial()) return;

  os() << "Trace: ";
  bool first = true;
  for (const Trace* trace_part = trace; trace_part != nullptr;
       trace_part = trace_part->next()) {
    if (trace_part->is_trivial()) continue;
    if (!first) {
      os() << " -> ";
    }
    first = false;
    os() << "{";
    os() << "cp:" << trace_part->cp_offset();
    if (trace_part->at_start() != Trace::UNKNOWN) {
      os() << ", at_start:"
           << (trace_part->at_start() == Trace::TRUE_VALUE ? "T" : "F");
    }
    if (trace_part->characters_preloaded() > 0) {
      os() << ", preloaded:" << trace_part->characters_preloaded();
    }
    if (trace_part->backtrack() != nullptr) {
      os() << ", backtrack:label[" << std::hex << std::setw(8)
           << std::setfill('0')
           << static_cast<int>(
                  reinterpret_cast<intptr_t>(trace_part->backtrack()))
           << std::dec << "]";
    }
    if (trace_part->action() != nullptr) {
      os() << ", action:";
      Node* action_node = trace_part->action();
      PrintNodeLabel(action_node);
    }
    if (trace_part->special_loop_state() != nullptr) {
      os() << ", special_loop";
    }
    os() << "}";
  }
}

void GraphPrinter::PrintBoyerMoorePositionInfo(
    const BoyerMoorePositionInfo* pi) {
  os() << "[";
  bool first = true;
  for (int i = 0; i < BoyerMoorePositionInfo::kMapSize; ++i) {
    if (pi->at(i)) {
      if (!first) os() << ",";
      os() << AsUC16(i);
      first = false;
    }
  }
  os() << "]";
}

void GraphPrinter::PrintBoyerMooreLookahead(const BoyerMooreLookahead* bm) {
  os() << "BM Info:" << std::endl;
  for (int i = 0; i < bm->length(); ++i) {
    os() << "     " << i << ": ";
    PrintBoyerMoorePositionInfo(bm->at(i));
    os() << std::endl;
  }
}

}  // namespace regexp
}  // namespace internal
}  // namespace v8

#endif  // V8_ENABLE_REGEXP_DIAGNOSTICS

Messung V0.5 in Prozent
C=96 H=88 G=91

¤ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet am  2026-09-30) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1126438
#Domains=1897691