Source code

Revision control

Copy as Markdown

Other Tools

// 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>::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