253 lines
7.5 KiB
C++
253 lines
7.5 KiB
C++
// 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 "regexp/regexp-dotprinter.h"
|
|
|
|
#include "regexp/regexp-compiler.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_ << AsUC16(range.from()) << "-" << AsUC16(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
|