1 // Copyright 2019 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "irregexp/imported/regexp-dotprinter.h"
6 
7 #include "irregexp/imported/regexp-compiler.h"
8 
9 namespace v8 {
10 namespace internal {
11 
12 // -------------------------------------------------------------------
13 // Dot/dotty output
14 
15 class DotPrinterImpl : public NodeVisitor {
16  public:
DotPrinterImpl(std::ostream & os)17   explicit DotPrinterImpl(std::ostream& os) : os_(os) {}
18   void PrintNode(const char* label, RegExpNode* node);
19   void Visit(RegExpNode* node);
20   void PrintAttributes(RegExpNode* from);
21   void PrintOnFailure(RegExpNode* from, RegExpNode* to);
22 #define DECLARE_VISIT(Type) virtual void Visit##Type(Type##Node* that);
23   FOR_EACH_NODE_TYPE(DECLARE_VISIT)
24 #undef DECLARE_VISIT
25  private:
26   std::ostream& os_;
27 };
28 
PrintNode(const char * label,RegExpNode * node)29 void DotPrinterImpl::PrintNode(const char* label, RegExpNode* node) {
30   os_ << "digraph G {\n  graph [label=\"";
31   for (int i = 0; label[i]; i++) {
32     switch (label[i]) {
33       case '\\':
34         os_ << "\\\\";
35         break;
36       case '"':
37         os_ << "\"";
38         break;
39       default:
40         os_ << label[i];
41         break;
42     }
43   }
44   os_ << "\"];\n";
45   Visit(node);
46   os_ << "}" << std::endl;
47 }
48 
Visit(RegExpNode * node)49 void DotPrinterImpl::Visit(RegExpNode* node) {
50   if (node->info()->visited) return;
51   node->info()->visited = true;
52   node->Accept(this);
53 }
54 
PrintOnFailure(RegExpNode * from,RegExpNode * on_failure)55 void DotPrinterImpl::PrintOnFailure(RegExpNode* from, RegExpNode* on_failure) {
56   os_ << "  n" << from << " -> n" << on_failure << " [style=dotted];\n";
57   Visit(on_failure);
58 }
59 
60 class AttributePrinter {
61  public:
AttributePrinter(std::ostream & os)62   explicit AttributePrinter(std::ostream& os) : os_(os), first_(true) {}
PrintSeparator()63   void PrintSeparator() {
64     if (first_) {
65       first_ = false;
66     } else {
67       os_ << "|";
68     }
69   }
PrintBit(const char * name,bool value)70   void PrintBit(const char* name, bool value) {
71     if (!value) return;
72     PrintSeparator();
73     os_ << "{" << name << "}";
74   }
PrintPositive(const char * name,int value)75   void PrintPositive(const char* name, int value) {
76     if (value < 0) return;
77     PrintSeparator();
78     os_ << "{" << name << "|" << value << "}";
79   }
80 
81  private:
82   std::ostream& os_;
83   bool first_;
84 };
85 
PrintAttributes(RegExpNode * that)86 void DotPrinterImpl::PrintAttributes(RegExpNode* that) {
87   os_ << "  a" << that << " [shape=Mrecord, color=grey, fontcolor=grey, "
88       << "margin=0.1, fontsize=10, label=\"{";
89   AttributePrinter printer(os_);
90   NodeInfo* info = that->info();
91   printer.PrintBit("NI", info->follows_newline_interest);
92   printer.PrintBit("WI", info->follows_word_interest);
93   printer.PrintBit("SI", info->follows_start_interest);
94   Label* label = that->label();
95   if (label->is_bound()) printer.PrintPositive("@", label->pos());
96   os_ << "}\"];\n"
97       << "  a" << that << " -> n" << that
98       << " [style=dashed, color=grey, arrowhead=none];\n";
99 }
100 
VisitChoice(ChoiceNode * that)101 void DotPrinterImpl::VisitChoice(ChoiceNode* that) {
102   os_ << "  n" << that << " [shape=Mrecord, label=\"?\"];\n";
103   for (int i = 0; i < that->alternatives()->length(); i++) {
104     GuardedAlternative alt = that->alternatives()->at(i);
105     os_ << "  n" << that << " -> n" << alt.node();
106   }
107   for (int i = 0; i < that->alternatives()->length(); i++) {
108     GuardedAlternative alt = that->alternatives()->at(i);
109     alt.node()->Accept(this);
110   }
111 }
112 
VisitLoopChoice(LoopChoiceNode * that)113 void DotPrinterImpl::VisitLoopChoice(LoopChoiceNode* that) {
114   VisitChoice(that);
115 }
116 
VisitNegativeLookaroundChoice(NegativeLookaroundChoiceNode * that)117 void DotPrinterImpl::VisitNegativeLookaroundChoice(
118     NegativeLookaroundChoiceNode* that) {
119   VisitChoice(that);
120 }
121 
VisitText(TextNode * that)122 void DotPrinterImpl::VisitText(TextNode* that) {
123   Zone* zone = that->zone();
124   os_ << "  n" << that << " [label=\"";
125   for (int i = 0; i < that->elements()->length(); i++) {
126     if (i > 0) os_ << " ";
127     TextElement elm = that->elements()->at(i);
128     switch (elm.text_type()) {
129       case TextElement::ATOM: {
130         Vector<const uc16> data = elm.atom()->data();
131         for (int i = 0; i < data.length(); i++) {
132           os_ << static_cast<char>(data[i]);
133         }
134         break;
135       }
136       case TextElement::CHAR_CLASS: {
137         RegExpCharacterClass* node = elm.char_class();
138         os_ << "[";
139         if (node->is_negated()) os_ << "^";
140         for (int j = 0; j < node->ranges(zone)->length(); j++) {
141           CharacterRange range = node->ranges(zone)->at(j);
142           os_ << AsUC32(range.from()) << "-" << AsUC32(range.to());
143         }
144         os_ << "]";
145         break;
146       }
147       default:
148         UNREACHABLE();
149     }
150   }
151   os_ << "\", shape=box, peripheries=2];\n";
152   PrintAttributes(that);
153   os_ << "  n" << that << " -> n" << that->on_success() << ";\n";
154   Visit(that->on_success());
155 }
156 
VisitBackReference(BackReferenceNode * that)157 void DotPrinterImpl::VisitBackReference(BackReferenceNode* that) {
158   os_ << "  n" << that << " [label=\"$" << that->start_register() << "..$"
159       << that->end_register() << "\", shape=doubleoctagon];\n";
160   PrintAttributes(that);
161   os_ << "  n" << that << " -> n" << that->on_success() << ";\n";
162   Visit(that->on_success());
163 }
164 
VisitEnd(EndNode * that)165 void DotPrinterImpl::VisitEnd(EndNode* that) {
166   os_ << "  n" << that << " [style=bold, shape=point];\n";
167   PrintAttributes(that);
168 }
169 
VisitAssertion(AssertionNode * that)170 void DotPrinterImpl::VisitAssertion(AssertionNode* that) {
171   os_ << "  n" << that << " [";
172   switch (that->assertion_type()) {
173     case AssertionNode::AT_END:
174       os_ << "label=\"$\", shape=septagon";
175       break;
176     case AssertionNode::AT_START:
177       os_ << "label=\"^\", shape=septagon";
178       break;
179     case AssertionNode::AT_BOUNDARY:
180       os_ << "label=\"\\b\", shape=septagon";
181       break;
182     case AssertionNode::AT_NON_BOUNDARY:
183       os_ << "label=\"\\B\", shape=septagon";
184       break;
185     case AssertionNode::AFTER_NEWLINE:
186       os_ << "label=\"(?<=\\n)\", shape=septagon";
187       break;
188   }
189   os_ << "];\n";
190   PrintAttributes(that);
191   RegExpNode* successor = that->on_success();
192   os_ << "  n" << that << " -> n" << successor << ";\n";
193   Visit(successor);
194 }
195 
VisitAction(ActionNode * that)196 void DotPrinterImpl::VisitAction(ActionNode* that) {
197   os_ << "  n" << that << " [";
198   switch (that->action_type_) {
199     case ActionNode::SET_REGISTER_FOR_LOOP:
200       os_ << "label=\"$" << that->data_.u_store_register.reg
201           << ":=" << that->data_.u_store_register.value << "\", shape=octagon";
202       break;
203     case ActionNode::INCREMENT_REGISTER:
204       os_ << "label=\"$" << that->data_.u_increment_register.reg
205           << "++\", shape=octagon";
206       break;
207     case ActionNode::STORE_POSITION:
208       os_ << "label=\"$" << that->data_.u_position_register.reg
209           << ":=$pos\", shape=octagon";
210       break;
211     case ActionNode::BEGIN_POSITIVE_SUBMATCH:
212       os_ << "label=\"$" << that->data_.u_submatch.current_position_register
213           << ":=$pos,begin-positive\", shape=septagon";
214       break;
215     case ActionNode::BEGIN_NEGATIVE_SUBMATCH:
216       os_ << "label=\"$" << that->data_.u_submatch.current_position_register
217           << ":=$pos,begin-negative\", shape=septagon";
218       break;
219     case ActionNode::POSITIVE_SUBMATCH_SUCCESS:
220       os_ << "label=\"escape\", shape=septagon";
221       break;
222     case ActionNode::EMPTY_MATCH_CHECK:
223       os_ << "label=\"$" << that->data_.u_empty_match_check.start_register
224           << "=$pos?,$" << that->data_.u_empty_match_check.repetition_register
225           << "<" << that->data_.u_empty_match_check.repetition_limit
226           << "?\", shape=septagon";
227       break;
228     case ActionNode::CLEAR_CAPTURES: {
229       os_ << "label=\"clear $" << that->data_.u_clear_captures.range_from
230           << " to $" << that->data_.u_clear_captures.range_to
231           << "\", shape=septagon";
232       break;
233     }
234   }
235   os_ << "];\n";
236   PrintAttributes(that);
237   RegExpNode* successor = that->on_success();
238   os_ << "  n" << that << " -> n" << successor << ";\n";
239   Visit(successor);
240 }
241 
DotPrint(const char * label,RegExpNode * node)242 void DotPrinter::DotPrint(const char* label, RegExpNode* node) {
243   StdoutStream os;
244   DotPrinterImpl printer(os);
245   printer.PrintNode(label, node);
246 }
247 
248 }  // namespace internal
249 }  // namespace v8
250