// 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-node-printer.h"
#include "irregexp/imported/regexp-ast-printer.h"
#include "irregexp/imported/regexp-compiler.h"
namespace v8 {
namespace internal {
namespace regexp {
void NodePrinter<Node>::PrintNodeLabel(const Node* node, const char * name) {
set_color(PrinterBase::Color::kBlue);
PrintNodeLabel(node);
os() << ": " << name << " " ;
}
void NodePrinter<Node>::PrintSuccess(const SeqNode* node) {
set_color(PrinterBase::Color::kGreen);
os() << " ⇝ " ;
PrintNodeLabel(node->on_success());
set_color(PrinterBase::Color::kDefault);
}
void NodePrinter<Node>::VisitEnd(EndNode* node) {
PrintNodeLabel(node, "End" );
switch (node->action()) {
case EndNode::ACCEPT:
os() << "Accept" ;
break ;
case EndNode::BACKTRACK:
os() << "Backtrack" ;
break ;
case EndNode::NEGATIVE_SUBMATCH_SUCCESS: {
NegativeSubmatchSuccess* nss_node = node->AsNegativeSubmatchSuccess();
os() << "[stack: " << nss_node->stack_pointer_register_
<< "; pos: " << nss_node->current_position_register_;
if (nss_node->clear_capture_count_ > 0 ) {
const int clear_capture_end =
nss_node->clear_capture_start_ + nss_node->clear_capture_count_ - 1 ;
os() << "; clear captures " << nss_node->clear_capture_start_ << " -- "
<< clear_capture_end;
}
os() << "]" ;
}
}
}
void NodePrinter<Node>::VisitAction(ActionNode* node) {
PrintNodeLabel(node, "Action" );
switch (node->action_type()) {
case ActionNode::SET_REGISTER_FOR_LOOP:
os() << "set register for loop " << node->register_from()
<< " := " << node->value();
break ;
case ActionNode::INCREMENT_REGISTER:
os() << "increment register " << node->register_from();
break ;
case ActionNode::STORE_POSITION:
os() << "store position " << node->register_from();
break ;
case ActionNode::RESTORE_POSITION:
os() << "restore position " << node->register_from();
break ;
case ActionNode::BEGIN_POSITIVE_SUBMATCH:
os() << "begin positive submatch [stack: "
<< node->data_.u_submatch.stack_pointer_register
<< "; pos: " << node->data_.u_submatch.current_position_register
<< "; ⇝ " ;
PrintNodeLabel(node->success_node());
os() << "]" ;
break ;
case ActionNode::BEGIN_NEGATIVE_SUBMATCH:
os() << "begin negative submatch [stack: "
<< node->data_.u_submatch.stack_pointer_register
<< "; pos: " << node->data_.u_submatch.current_position_register
<< "]" ;
break ;
case ActionNode::POSITIVE_SUBMATCH_SUCCESS:
os() << "positive submatch success [stack: "
<< node->data_.u_submatch.stack_pointer_register
<< "; pos: " << node->data_.u_submatch.current_position_register;
if (node->data_.u_submatch.clear_register_count != 0 ) {
const int clear_to = node->data_.u_submatch.clear_register_from +
node->data_.u_submatch.clear_register_count - 1 ;
os() << "; clear reg " << node->data_.u_submatch.clear_register_from
<< " -- " << clear_to;
}
os() << "]" ;
break ;
case ActionNode::EMPTY_MATCH_CHECK:
os() << "empty match check [start reg: "
<< node->data_.u_empty_match_check.start_register
<< "; repetition: reg "
<< node->data_.u_empty_match_check.start_register << " / limit "
<< node->data_.u_empty_match_check.repetition_limit << "]" ;
break ;
case ActionNode::CLEAR_CAPTURES:
os() << "clear captures " << node->register_from() << " -- "
<< node->register_to();
break ;
case ActionNode::MODIFY_FLAGS:
os() << "modify flags " << Flags(node->data_.u_modify_flags.flags);
break ;
case ActionNode::EATS_AT_LEAST:
os() << "eats at least " << node->data_.u_eats_at_least.characters;
break ;
}
PrintSuccess(node);
}
void NodePrinter<Node>::VisitUnanchoredAdvance(UnanchoredAdvanceNode* node) {
PrintNodeLabel(node, "UnanchoredAdvance" );
os() << "unanchored advance" ;
PrintSuccess(node);
}
void NodePrinter<Node>::PrintGuard(const Guard* guard) {
os() << guard->reg() << " " ;
switch (guard->op()) {
case Guard::LT:
os() << "<" ;
break ;
case Guard::GEQ:
os() << ">=" ;
break ;
}
os() << " " << guard->value();
}
void NodePrinter<Node>::VisitChoice(ChoiceNode* node) {
PrintNodeLabel(node, "Choice" );
if (node->not_at_start()) {
os() << "!^ " ;
}
for (int i = 0 ; i < node->alternatives()->length(); i++) {
const GuardedAlternative& alt = node->alternatives()->at(i);
if (i != 0 ) os() << " " ;
PrintNodeLabel(alt.node());
if (alt.guards() != nullptr && alt.guards()->length() != 0 ) {
for (int g = 0 ; g < alt.guards()->length(); g++) {
if (g != 0 ) os() << " && " ;
PrintGuard(alt.guards()->at(i));
}
}
}
}
void NodePrinter<Node>::VisitLoopChoice(LoopChoiceNode* node) {
PrintNodeLabel(node, "LoopChoice" );
if (node->read_backward()) {
os() << "↩ " ;
}
os() << "{ " ;
PrintNodeLabel(node->loop_node());
os() << " } [" ;
os() << "greedy: "
<< (node->alternatives()->at(0 ).node() == node->loop_node());
os() << "; body can be empty: " << node->body_can_be_zero_length();
os() << "]" ;
set_color(PrinterBase::Color::kGreen);
os() << " ⇝ " ;
PrintNodeLabel(node->continue_node());
set_color(PrinterBase::Color::kDefault);
}
void NodePrinter<Node>::VisitNegativeLookaroundChoice(
NegativeLookaroundChoiceNode* node) {
PrintNodeLabel(node, "NegativeLookaroundChoice" );
if (node->not_at_start()) {
os() << "!^ " ;
}
for (int i = 0 ; i < node->alternatives()->length(); i++) {
const GuardedAlternative& alt = node->alternatives()->at(i);
if (i != 0 ) os() << " " ;
PrintNodeLabel(alt.node());
if (alt.guards() != nullptr && alt.guards()->length() != 0 ) {
for (int g = 0 ; g < alt.guards()->length(); g++) {
if (g != 0 ) os() << " && " ;
PrintGuard(alt.guards()->at(i));
}
}
}
}
void NodePrinter<Node>::VisitBackReference(BackReferenceNode* node) {
PrintNodeLabel(node, "BackReference" );
if (node->read_backward()) {
os() << "↩ " ;
}
os() << node->start_register() << " -- " << node->end_register();
PrintSuccess(node);
}
void NodePrinter<Node>::VisitAssertion(AssertionNode* node) {
PrintNodeLabel(node, "Assertion" );
switch (node->assertion_type()) {
case AssertionNode::AT_END:
os() << "at end" ;
break ;
case AssertionNode::AT_START:
os() << "at start" ;
break ;
case AssertionNode::AT_BOUNDARY:
os() << "at boundary" ;
break ;
case AssertionNode::AT_NON_BOUNDARY:
os() << "at non-boundary" ;
break ;
case AssertionNode::AFTER_NEWLINE:
os() << "after newline" ;
break ;
}
PrintSuccess(node);
}
void NodePrinter<Node>::VisitText(TextNode* node) {
PrintNodeLabel(node, "Text" );
if (node->read_backward()) {
os() << "↩ " ;
}
for (int i = 0 ; i < node->elements()->length(); i++) {
const TextElement& elm = node->elements()->at(i);
if (i != 0 ) os() << " " ;
AstNodePrinter tree_printer(*this , nullptr);
tree_printer.Print(elm.tree());
}
os() << "; Eats: " << node->EatsAtLeast(false ) << "/"
<< node->EatsAtLeast(true );
PrintSuccess(node);
}
} // namespace regexp
} // namespace internal
} // namespace v8
#endif // V8_ENABLE_REGEXP_DIAGNOSTICS
Messung V0.5 in Prozent C=98 H=90 G=94
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-09-30)
¤
*© Formatika GbR, Deutschland