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