blob: 7cf1e82c4d0d2d4ec9d506ca5f407e7b0eef1e6a [file] [log] [blame]
// Copyright 2019 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.
#include "src/regexp/regexp-dotprinter.h"
#include "src/regexp/regexp-compiler.h"
#include "src/utils/ostreams.h"
namespace v8 {
namespace internal {
// -------------------------------------------------------------------
// Dot/dotty output
#ifdef DEBUG
class DotPrinterImpl : public NodeVisitor {
public:
explicit DotPrinterImpl(std::ostream& os) : os_(os) {}
void PrintNode(const char* label, RegExpNode* node);
void Visit(RegExpNode* node);
void PrintAttributes(RegExpNode* from);
void PrintOnFailure(RegExpNode* from, RegExpNode* to);
#define DECLARE_VISIT(Type) virtual void Visit##Type(Type##Node* that);
FOR_EACH_NODE_TYPE(DECLARE_VISIT)
#undef DECLARE_VISIT
private:
std::ostream& os_;
};
void DotPrinterImpl::PrintNode(const char* label, RegExpNode* node) {
os_ << "digraph G {\n graph [label=\"";
for (int i = 0; label[i]; i++) {
switch (label[i]) {
case '\\':
os_ << "\\\\";
break;
case '"':
os_ << "\"";
break;
default:
os_ << label[i];
break;
}
}
os_ << "\"];\n";
Visit(node);
os_ << "}" << std::endl;
}
void DotPrinterImpl::Visit(RegExpNode* node) {
if (node->info()->visited) return;
node->info()->visited = true;
node->Accept(this);
}
void DotPrinterImpl::PrintOnFailure(RegExpNode* from, RegExpNode* on_failure) {
os_ << " n" << from << " -> n" << on_failure << " [style=dotted];\n";
Visit(on_failure);
}
class AttributePrinter {
public:
explicit AttributePrinter(std::ostream& os) // NOLINT
: os_(os), first_(true) {}
void PrintSeparator() {
if (first_) {
first_ = false;
} else {
os_ << "|";
}
}
void PrintBit(const char* name, bool value) {
if (!value) return;
PrintSeparator();
os_ << "{" << name << "}";
}
void PrintPositive(const char* name, int value) {
if (value < 0) return;
PrintSeparator();
os_ << "{" << name << "|" << value << "}";
}
private:
std::ostream& os_;
bool first_;
};
void DotPrinterImpl::PrintAttributes(RegExpNode* that) {
os_ << " a" << that << " [shape=Mrecord, color=grey, fontcolor=grey, "
<< "margin=0.1, fontsize=10, label=\"{";
AttributePrinter printer(os_);
NodeInfo* info = that->info();
printer.PrintBit("NI", info->follows_newline_interest);
printer.PrintBit("WI", info->follows_word_interest);
printer.PrintBit("SI", info->follows_start_interest);
Label* label = that->label();
if (label->is_bound()) printer.PrintPositive("@", label->pos());
os_ << "}\"];\n"
<< " a" << that << " -> n" << that
<< " [style=dashed, color=grey, arrowhead=none];\n";
}
void DotPrinterImpl::VisitChoice(ChoiceNode* that) {
os_ << " n" << that << " [shape=Mrecord, label=\"?\"];\n";
for (int i = 0; i < that->alternatives()->length(); i++) {
GuardedAlternative alt = that->alternatives()->at(i);
os_ << " n" << that << " -> n" << alt.node();
}
for (int i = 0; i < that->alternatives()->length(); i++) {
GuardedAlternative alt = that->alternatives()->at(i);
alt.node()->Accept(this);
}
}
void DotPrinterImpl::VisitLoopChoice(LoopChoiceNode* that) {
VisitChoice(that);
}
void DotPrinterImpl::VisitNegativeLookaroundChoice(
NegativeLookaroundChoiceNode* that) {
VisitChoice(that);
}
void DotPrinterImpl::VisitText(TextNode* that) {
Zone* zone = that->zone();
os_ << " n" << that << " [label=\"";
for (int i = 0; i < that->elements()->length(); i++) {
if (i > 0) os_ << " ";
TextElement elm = that->elements()->at(i);
switch (elm.text_type()) {
case TextElement::ATOM: {
Vector<const uc16> data = elm.atom()->data();
for (int i = 0; i < data.length(); i++) {
os_ << static_cast<char>(data[i]);
}
break;
}
case TextElement::CHAR_CLASS: {
RegExpCharacterClass* node = elm.char_class();
os_ << "[";
if (node->is_negated()) os_ << "^";
for (int j = 0; j < node->ranges(zone)->length(); j++) {
CharacterRange range = node->ranges(zone)->at(j);
os_ << AsUC32(range.from()) << "-" << AsUC32(range.to());
}
os_ << "]";
break;
}
default:
UNREACHABLE();
}
}
os_ << "\", shape=box, peripheries=2];\n";
PrintAttributes(that);
os_ << " n" << that << " -> n" << that->on_success() << ";\n";
Visit(that->on_success());
}
void DotPrinterImpl::VisitBackReference(BackReferenceNode* that) {
os_ << " n" << that << " [label=\"$" << that->start_register() << "..$"
<< that->end_register() << "\", shape=doubleoctagon];\n";
PrintAttributes(that);
os_ << " n" << that << " -> n" << that->on_success() << ";\n";
Visit(that->on_success());
}
void DotPrinterImpl::VisitEnd(EndNode* that) {
os_ << " n" << that << " [style=bold, shape=point];\n";
PrintAttributes(that);
}
void DotPrinterImpl::VisitAssertion(AssertionNode* that) {
os_ << " n" << that << " [";
switch (that->assertion_type()) {
case AssertionNode::AT_END:
os_ << "label=\"$\", shape=septagon";
break;
case AssertionNode::AT_START:
os_ << "label=\"^\", shape=septagon";
break;
case AssertionNode::AT_BOUNDARY:
os_ << "label=\"\\b\", shape=septagon";
break;
case AssertionNode::AT_NON_BOUNDARY:
os_ << "label=\"\\B\", shape=septagon";
break;
case AssertionNode::AFTER_NEWLINE:
os_ << "label=\"(?<=\\n)\", shape=septagon";
break;
}
os_ << "];\n";
PrintAttributes(that);
RegExpNode* successor = that->on_success();
os_ << " n" << that << " -> n" << successor << ";\n";
Visit(successor);
}
void DotPrinterImpl::VisitAction(ActionNode* that) {
os_ << " n" << that << " [";
switch (that->action_type_) {
case ActionNode::SET_REGISTER_FOR_LOOP:
os_ << "label=\"$" << that->data_.u_store_register.reg
<< ":=" << that->data_.u_store_register.value << "\", shape=octagon";
break;
case ActionNode::INCREMENT_REGISTER:
os_ << "label=\"$" << that->data_.u_increment_register.reg
<< "++\", shape=octagon";
break;
case ActionNode::STORE_POSITION:
os_ << "label=\"$" << that->data_.u_position_register.reg
<< ":=$pos\", shape=octagon";
break;
case ActionNode::BEGIN_SUBMATCH:
os_ << "label=\"$" << that->data_.u_submatch.current_position_register
<< ":=$pos,begin\", shape=septagon";
break;
case ActionNode::POSITIVE_SUBMATCH_SUCCESS:
os_ << "label=\"escape\", shape=septagon";
break;
case ActionNode::EMPTY_MATCH_CHECK:
os_ << "label=\"$" << that->data_.u_empty_match_check.start_register
<< "=$pos?,$" << that->data_.u_empty_match_check.repetition_register
<< "<" << that->data_.u_empty_match_check.repetition_limit
<< "?\", shape=septagon";
break;
case ActionNode::CLEAR_CAPTURES: {
os_ << "label=\"clear $" << that->data_.u_clear_captures.range_from
<< " to $" << that->data_.u_clear_captures.range_to
<< "\", shape=septagon";
break;
}
}
os_ << "];\n";
PrintAttributes(that);
RegExpNode* successor = that->on_success();
os_ << " n" << that << " -> n" << successor << ";\n";
Visit(successor);
}
#endif // DEBUG
void DotPrinter::DotPrint(const char* label, RegExpNode* node) {
#ifdef DEBUG
StdoutStream os;
DotPrinterImpl printer(os);
printer.PrintNode(label, node);
#endif // DEBUG
}
} // namespace internal
} // namespace v8