Line data Source code
1 : // Copyright 2016 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 : #ifndef V8_REGEXP_REGEXP_AST_H_
6 : #define V8_REGEXP_REGEXP_AST_H_
7 :
8 : #include "src/objects.h"
9 : #include "src/objects/string.h"
10 : #include "src/utils.h"
11 : #include "src/zone/zone-containers.h"
12 : #include "src/zone/zone.h"
13 :
14 : namespace v8 {
15 : namespace internal {
16 :
17 : #define FOR_EACH_REG_EXP_TREE_TYPE(VISIT) \
18 : VISIT(Disjunction) \
19 : VISIT(Alternative) \
20 : VISIT(Assertion) \
21 : VISIT(CharacterClass) \
22 : VISIT(Atom) \
23 : VISIT(Quantifier) \
24 : VISIT(Capture) \
25 : VISIT(Group) \
26 : VISIT(Lookaround) \
27 : VISIT(BackReference) \
28 : VISIT(Empty) \
29 : VISIT(Text)
30 :
31 : #define FORWARD_DECLARE(Name) class RegExp##Name;
32 : FOR_EACH_REG_EXP_TREE_TYPE(FORWARD_DECLARE)
33 : #undef FORWARD_DECLARE
34 :
35 : class RegExpCompiler;
36 : class RegExpNode;
37 : class RegExpTree;
38 :
39 :
40 1812 : class RegExpVisitor BASE_EMBEDDED {
41 : public:
42 0 : virtual ~RegExpVisitor() {}
43 : #define MAKE_CASE(Name) \
44 : virtual void* Visit##Name(RegExp##Name*, void* data) = 0;
45 : FOR_EACH_REG_EXP_TREE_TYPE(MAKE_CASE)
46 : #undef MAKE_CASE
47 : };
48 :
49 :
50 : // A simple closed interval.
51 : class Interval {
52 : public:
53 : Interval() : from_(kNone), to_(kNone) {}
54 266543 : Interval(int from, int to) : from_(from), to_(to) {}
55 : Interval Union(Interval that) {
56 43780 : if (that.from_ == kNone)
57 : return *this;
58 5168 : else if (from_ == kNone)
59 : return that;
60 : else
61 : return Interval(Min(from_, that.from_), Max(to_, that.to_));
62 : }
63 68448 : bool Contains(int value) { return (from_ <= value) && (value <= to_); }
64 : bool is_empty() { return from_ == kNone; }
65 : int from() const { return from_; }
66 : int to() const { return to_; }
67 : static Interval Empty() { return Interval(); }
68 : static const int kNone = -1;
69 :
70 : private:
71 : int from_;
72 : int to_;
73 : };
74 :
75 :
76 : // Represents code units in the range from from_ to to_, both ends are
77 : // inclusive.
78 : class CharacterRange {
79 : public:
80 66 : CharacterRange() : from_(0), to_(0) {}
81 : // For compatibility with the CHECK_OK macro
82 : CharacterRange(void* null) { DCHECK_NULL(null); } // NOLINT
83 : static void AddClassEscape(char type, ZoneList<CharacterRange>* ranges,
84 : Zone* zone);
85 : // Add class escapes. Add case equivalent closure for \w and \W if necessary.
86 : static void AddClassEscape(char type, ZoneList<CharacterRange>* ranges,
87 : bool add_unicode_case_equivalents, Zone* zone);
88 : static Vector<const int> GetWordBounds();
89 : static inline CharacterRange Singleton(uc32 value) {
90 : return CharacterRange(value, value);
91 : }
92 : static inline CharacterRange Range(uc32 from, uc32 to) {
93 : DCHECK(0 <= from && to <= String::kMaxCodePoint);
94 : DCHECK(static_cast<uint32_t>(from) <= static_cast<uint32_t>(to));
95 : return CharacterRange(from, to);
96 : }
97 : static inline CharacterRange Everything() {
98 : return CharacterRange(0, String::kMaxCodePoint);
99 : }
100 50638 : static inline ZoneList<CharacterRange>* List(Zone* zone,
101 : CharacterRange range) {
102 : ZoneList<CharacterRange>* list =
103 50638 : new (zone) ZoneList<CharacterRange>(1, zone);
104 50638 : list->Add(range, zone);
105 50638 : return list;
106 : }
107 840318 : bool Contains(uc32 i) { return from_ <= i && i <= to_; }
108 : uc32 from() const { return from_; }
109 : void set_from(uc32 value) { from_ = value; }
110 : uc32 to() const { return to_; }
111 : void set_to(uc32 value) { to_ = value; }
112 : bool is_valid() { return from_ <= to_; }
113 292880 : bool IsEverything(uc32 max) { return from_ == 0 && to_ >= max; }
114 : bool IsSingleton() { return (from_ == to_); }
115 : static void AddCaseEquivalents(Isolate* isolate, Zone* zone,
116 : ZoneList<CharacterRange>* ranges,
117 : bool is_one_byte);
118 : // Whether a range list is in canonical form: Ranges ordered by from value,
119 : // and ranges non-overlapping and non-adjacent.
120 : static bool IsCanonical(ZoneList<CharacterRange>* ranges);
121 : // Convert range list to canonical form. The characters covered by the ranges
122 : // will still be the same, but no character is in more than one range, and
123 : // adjacent ranges are merged. The resulting list may be shorter than the
124 : // original, but cannot be longer.
125 : static void Canonicalize(ZoneList<CharacterRange>* ranges);
126 : // Negate the contents of a character range in canonical form.
127 : static void Negate(ZoneList<CharacterRange>* src,
128 : ZoneList<CharacterRange>* dst, Zone* zone);
129 : static const int kStartMarker = (1 << 24);
130 : static const int kPayloadMask = (1 << 24) - 1;
131 :
132 : private:
133 : CharacterRange(uc32 from, uc32 to) : from_(from), to_(to) {}
134 :
135 : uc32 from_;
136 : uc32 to_;
137 : };
138 :
139 :
140 : class CharacterSet final BASE_EMBEDDED {
141 : public:
142 : explicit CharacterSet(uc16 standard_set_type)
143 90063 : : ranges_(nullptr), standard_set_type_(standard_set_type) {}
144 : explicit CharacterSet(ZoneList<CharacterRange>* ranges)
145 269462 : : ranges_(ranges), standard_set_type_(0) {}
146 : ZoneList<CharacterRange>* ranges(Zone* zone);
147 : uc16 standard_set_type() { return standard_set_type_; }
148 : void set_standard_set_type(uc16 special_set_type) {
149 14291 : standard_set_type_ = special_set_type;
150 : }
151 : bool is_standard() { return standard_set_type_ != 0; }
152 : void Canonicalize();
153 :
154 : private:
155 : ZoneList<CharacterRange>* ranges_;
156 : // If non-zero, the value represents a standard set (e.g., all whitespace
157 : // characters) without having to expand the ranges.
158 : uc16 standard_set_type_;
159 : };
160 :
161 :
162 : class TextElement final BASE_EMBEDDED {
163 : public:
164 : enum TextType { ATOM, CHAR_CLASS };
165 :
166 : static TextElement Atom(RegExpAtom* atom);
167 : static TextElement CharClass(RegExpCharacterClass* char_class);
168 :
169 : int cp_offset() const { return cp_offset_; }
170 365955 : void set_cp_offset(int cp_offset) { cp_offset_ = cp_offset; }
171 : int length() const;
172 :
173 : TextType text_type() const { return text_type_; }
174 :
175 : RegExpTree* tree() const { return tree_; }
176 :
177 7378597 : RegExpAtom* atom() const {
178 : DCHECK(text_type() == ATOM);
179 : return reinterpret_cast<RegExpAtom*>(tree());
180 : }
181 :
182 : RegExpCharacterClass* char_class() const {
183 : DCHECK(text_type() == CHAR_CLASS);
184 : return reinterpret_cast<RegExpCharacterClass*>(tree());
185 : }
186 :
187 : private:
188 : TextElement(TextType text_type, RegExpTree* tree)
189 : : cp_offset_(-1), text_type_(text_type), tree_(tree) {}
190 :
191 : int cp_offset_;
192 : TextType text_type_;
193 : RegExpTree* tree_;
194 : };
195 :
196 :
197 5936920 : class RegExpTree : public ZoneObject {
198 : public:
199 : static const int kInfinity = kMaxInt;
200 0 : virtual ~RegExpTree() {}
201 : virtual void* Accept(RegExpVisitor* visitor, void* data) = 0;
202 : virtual RegExpNode* ToNode(RegExpCompiler* compiler,
203 : RegExpNode* on_success) = 0;
204 1306343 : virtual bool IsTextElement() { return false; }
205 90372 : virtual bool IsAnchoredAtStart() { return false; }
206 91108 : virtual bool IsAnchoredAtEnd() { return false; }
207 : virtual int min_match() = 0;
208 : virtual int max_match() = 0;
209 : // Returns the interval of registers used for captures within this
210 : // expression.
211 1153288 : virtual Interval CaptureRegisters() { return Interval::Empty(); }
212 : virtual void AppendToText(RegExpText* text, Zone* zone);
213 : std::ostream& Print(std::ostream& os, Zone* zone); // NOLINT
214 : #define MAKE_ASTYPE(Name) \
215 : virtual RegExp##Name* As##Name(); \
216 : virtual bool Is##Name();
217 : FOR_EACH_REG_EXP_TREE_TYPE(MAKE_ASTYPE)
218 : #undef MAKE_ASTYPE
219 : };
220 :
221 :
222 0 : class RegExpDisjunction final : public RegExpTree {
223 : public:
224 : explicit RegExpDisjunction(ZoneList<RegExpTree*>* alternatives);
225 : void* Accept(RegExpVisitor* visitor, void* data) override;
226 : RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
227 : RegExpDisjunction* AsDisjunction() override;
228 : Interval CaptureRegisters() override;
229 : bool IsDisjunction() override;
230 : bool IsAnchoredAtStart() override;
231 : bool IsAnchoredAtEnd() override;
232 27860 : int min_match() override { return min_match_; }
233 30955 : int max_match() override { return max_match_; }
234 : ZoneList<RegExpTree*>* alternatives() { return alternatives_; }
235 :
236 : private:
237 : bool SortConsecutiveAtoms(RegExpCompiler* compiler);
238 : void RationalizeConsecutiveAtoms(RegExpCompiler* compiler);
239 : void FixSingleCharacterDisjunctions(RegExpCompiler* compiler);
240 : ZoneList<RegExpTree*>* alternatives_;
241 : int min_match_;
242 : int max_match_;
243 : };
244 :
245 :
246 0 : class RegExpAlternative final : public RegExpTree {
247 : public:
248 : explicit RegExpAlternative(ZoneList<RegExpTree*>* nodes);
249 : void* Accept(RegExpVisitor* visitor, void* data) override;
250 : RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
251 : RegExpAlternative* AsAlternative() override;
252 : Interval CaptureRegisters() override;
253 : bool IsAlternative() override;
254 : bool IsAnchoredAtStart() override;
255 : bool IsAnchoredAtEnd() override;
256 41108 : int min_match() override { return min_match_; }
257 66805 : int max_match() override { return max_match_; }
258 : ZoneList<RegExpTree*>* nodes() { return nodes_; }
259 :
260 : private:
261 : ZoneList<RegExpTree*>* nodes_;
262 : int min_match_;
263 : int max_match_;
264 : };
265 :
266 :
267 0 : class RegExpAssertion final : public RegExpTree {
268 : public:
269 : enum AssertionType {
270 : START_OF_LINE,
271 : START_OF_INPUT,
272 : END_OF_LINE,
273 : END_OF_INPUT,
274 : BOUNDARY,
275 : NON_BOUNDARY
276 : };
277 22039 : explicit RegExpAssertion(AssertionType type) : assertion_type_(type) {}
278 : void* Accept(RegExpVisitor* visitor, void* data) override;
279 : RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
280 : RegExpAssertion* AsAssertion() override;
281 : bool IsAssertion() override;
282 : bool IsAnchoredAtStart() override;
283 : bool IsAnchoredAtEnd() override;
284 21711 : int min_match() override { return 0; }
285 22719 : int max_match() override { return 0; }
286 : AssertionType assertion_type() { return assertion_type_; }
287 :
288 : private:
289 : AssertionType assertion_type_;
290 : };
291 :
292 :
293 0 : class RegExpCharacterClass final : public RegExpTree {
294 : public:
295 : // NEGATED: The character class is negated and should match everything but
296 : // the specified ranges.
297 : // CONTAINS_SPLIT_SURROGATE: The character class contains part of a split
298 : // surrogate and should not be unicode-desugared (crbug.com/641091).
299 : enum Flag {
300 : NEGATED = 1 << 0,
301 : CONTAINS_SPLIT_SURROGATE = 1 << 1,
302 : };
303 : typedef base::Flags<Flag> Flags;
304 :
305 : explicit RegExpCharacterClass(ZoneList<CharacterRange>* ranges,
306 : Flags flags = Flags())
307 538912 : : set_(ranges), flags_(flags) {}
308 90063 : explicit RegExpCharacterClass(uc16 type) : set_(type), flags_(0) {}
309 : void* Accept(RegExpVisitor* visitor, void* data) override;
310 : RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
311 : RegExpCharacterClass* AsCharacterClass() override;
312 : bool IsCharacterClass() override;
313 208843 : bool IsTextElement() override { return true; }
314 273560 : int min_match() override { return 1; }
315 : // The character class may match two code units for unicode regexps.
316 : // TODO(yangguo): we should split this class for usage in TextElement, and
317 : // make max_match() dependent on the character class content.
318 143042 : int max_match() override { return 2; }
319 : void AppendToText(RegExpText* text, Zone* zone) override;
320 : CharacterSet character_set() { return set_; }
321 : // TODO(lrn): Remove need for complex version if is_standard that
322 : // recognizes a mangled standard set and just do { return set_.is_special(); }
323 : bool is_standard(Zone* zone);
324 : // Returns a value representing the standard character set if is_standard()
325 : // returns true.
326 : // Currently used values are:
327 : // s : unicode whitespace
328 : // S : unicode non-whitespace
329 : // w : ASCII word character (digit, letter, underscore)
330 : // W : non-ASCII word character
331 : // d : ASCII digit
332 : // D : non-ASCII digit
333 : // . : non-newline
334 : // * : All characters, for advancing unanchored regexp
335 20621 : uc16 standard_type() { return set_.standard_set_type(); }
336 918264 : ZoneList<CharacterRange>* ranges(Zone* zone) { return set_.ranges(zone); }
337 : bool is_negated() const { return (flags_ & NEGATED) != 0; }
338 : bool contains_split_surrogate() const {
339 : return (flags_ & CONTAINS_SPLIT_SURROGATE) != 0;
340 : }
341 :
342 : private:
343 : CharacterSet set_;
344 : const Flags flags_;
345 : };
346 :
347 :
348 0 : class RegExpAtom final : public RegExpTree {
349 : public:
350 2067146 : explicit RegExpAtom(Vector<const uc16> data) : data_(data) {}
351 : void* Accept(RegExpVisitor* visitor, void* data) override;
352 : RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
353 : RegExpAtom* AsAtom() override;
354 : bool IsAtom() override;
355 501 : bool IsTextElement() override { return true; }
356 7752216 : int min_match() override { return data_.length(); }
357 7006624 : int max_match() override { return data_.length(); }
358 : void AppendToText(RegExpText* text, Zone* zone) override;
359 : Vector<const uc16> data() { return data_; }
360 7773955 : int length() { return data_.length(); }
361 :
362 : private:
363 : Vector<const uc16> data_;
364 : };
365 :
366 :
367 0 : class RegExpText final : public RegExpTree {
368 : public:
369 5244 : explicit RegExpText(Zone* zone) : elements_(2, zone), length_(0) {}
370 : void* Accept(RegExpVisitor* visitor, void* data) override;
371 : RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
372 : RegExpText* AsText() override;
373 : bool IsText() override;
374 0 : bool IsTextElement() override { return true; }
375 3390 : int min_match() override { return length_; }
376 5178 : int max_match() override { return length_; }
377 : void AppendToText(RegExpText* text, Zone* zone) override;
378 16524 : void AddElement(TextElement elm, Zone* zone) {
379 16524 : elements_.Add(elm, zone);
380 16524 : length_ += elm.length();
381 16524 : }
382 : ZoneList<TextElement>* elements() { return &elements_; }
383 :
384 : private:
385 : ZoneList<TextElement> elements_;
386 : int length_;
387 : };
388 :
389 :
390 0 : class RegExpQuantifier final : public RegExpTree {
391 : public:
392 : enum QuantifierType { GREEDY, NON_GREEDY, POSSESSIVE };
393 1687531 : RegExpQuantifier(int min, int max, QuantifierType type, RegExpTree* body)
394 : : body_(body),
395 : min_(min),
396 : max_(max),
397 1687531 : min_match_(min * body->min_match()),
398 3375062 : quantifier_type_(type) {
399 1687531 : if (max > 0 && body->max_match() > kInfinity / max) {
400 39779 : max_match_ = kInfinity;
401 : } else {
402 1647752 : max_match_ = max * body->max_match();
403 : }
404 1687531 : }
405 : void* Accept(RegExpVisitor* visitor, void* data) override;
406 : RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
407 : static RegExpNode* ToNode(int min, int max, bool is_greedy, RegExpTree* body,
408 : RegExpCompiler* compiler, RegExpNode* on_success,
409 : bool not_at_start = false);
410 : RegExpQuantifier* AsQuantifier() override;
411 : Interval CaptureRegisters() override;
412 : bool IsQuantifier() override;
413 1683779 : int min_match() override { return min_match_; }
414 1689984 : int max_match() override { return max_match_; }
415 : int min() { return min_; }
416 : int max() { return max_; }
417 : bool is_possessive() { return quantifier_type_ == POSSESSIVE; }
418 : bool is_non_greedy() { return quantifier_type_ == NON_GREEDY; }
419 1036455 : bool is_greedy() { return quantifier_type_ == GREEDY; }
420 : RegExpTree* body() { return body_; }
421 :
422 : private:
423 : RegExpTree* body_;
424 : int min_;
425 : int max_;
426 : int min_match_;
427 : int max_match_;
428 : QuantifierType quantifier_type_;
429 : };
430 :
431 :
432 0 : class RegExpCapture final : public RegExpTree {
433 : public:
434 : explicit RegExpCapture(int index)
435 1268796 : : body_(nullptr), index_(index), name_(nullptr) {}
436 : void* Accept(RegExpVisitor* visitor, void* data) override;
437 : RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
438 : static RegExpNode* ToNode(RegExpTree* body, int index,
439 : RegExpCompiler* compiler, RegExpNode* on_success);
440 : RegExpCapture* AsCapture() override;
441 : bool IsAnchoredAtStart() override;
442 : bool IsAnchoredAtEnd() override;
443 : Interval CaptureRegisters() override;
444 : bool IsCapture() override;
445 78652 : int min_match() override { return body_->min_match(); }
446 97389 : int max_match() override { return body_->max_match(); }
447 : RegExpTree* body() { return body_; }
448 1268796 : void set_body(RegExpTree* body) { body_ = body; }
449 : int index() { return index_; }
450 : const ZoneVector<uc16>* name() const { return name_; }
451 1317 : void set_name(const ZoneVector<uc16>* name) { name_ = name; }
452 134238 : static int StartRegister(int index) { return index * 2; }
453 141160 : static int EndRegister(int index) { return index * 2 + 1; }
454 :
455 : private:
456 : RegExpTree* body_;
457 : int index_;
458 : const ZoneVector<uc16>* name_;
459 : };
460 :
461 0 : class RegExpGroup final : public RegExpTree {
462 : public:
463 28226 : explicit RegExpGroup(RegExpTree* body) : body_(body) {}
464 : void* Accept(RegExpVisitor* visitor, void* data) override;
465 11145 : RegExpNode* ToNode(RegExpCompiler* compiler,
466 : RegExpNode* on_success) override {
467 11145 : return body_->ToNode(compiler, on_success);
468 : }
469 : RegExpGroup* AsGroup() override;
470 265 : bool IsAnchoredAtStart() override { return body_->IsAnchoredAtStart(); }
471 255 : bool IsAnchoredAtEnd() override { return body_->IsAnchoredAtEnd(); }
472 : bool IsGroup() override;
473 32184 : int min_match() override { return body_->min_match(); }
474 39843 : int max_match() override { return body_->max_match(); }
475 14458 : Interval CaptureRegisters() override { return body_->CaptureRegisters(); }
476 : RegExpTree* body() { return body_; }
477 :
478 : private:
479 : RegExpTree* body_;
480 : };
481 :
482 0 : class RegExpLookaround final : public RegExpTree {
483 : public:
484 : enum Type { LOOKAHEAD, LOOKBEHIND };
485 :
486 : RegExpLookaround(RegExpTree* body, bool is_positive, int capture_count,
487 : int capture_from, Type type)
488 : : body_(body),
489 : is_positive_(is_positive),
490 : capture_count_(capture_count),
491 : capture_from_(capture_from),
492 3768 : type_(type) {}
493 :
494 : void* Accept(RegExpVisitor* visitor, void* data) override;
495 : RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
496 : RegExpLookaround* AsLookaround() override;
497 : Interval CaptureRegisters() override;
498 : bool IsLookaround() override;
499 : bool IsAnchoredAtStart() override;
500 3375 : int min_match() override { return 0; }
501 4793 : int max_match() override { return 0; }
502 : RegExpTree* body() { return body_; }
503 : bool is_positive() { return is_positive_; }
504 : int capture_count() { return capture_count_; }
505 : int capture_from() { return capture_from_; }
506 : Type type() { return type_; }
507 :
508 : class Builder {
509 : public:
510 : Builder(bool is_positive, RegExpNode* on_success,
511 : int stack_pointer_register, int position_register,
512 : int capture_register_count = 0, int capture_register_start = 0);
513 : RegExpNode* on_match_success() { return on_match_success_; }
514 : RegExpNode* ForMatch(RegExpNode* match);
515 :
516 : private:
517 : bool is_positive_;
518 : RegExpNode* on_match_success_;
519 : RegExpNode* on_success_;
520 : int stack_pointer_register_;
521 : int position_register_;
522 : };
523 :
524 : private:
525 : RegExpTree* body_;
526 : bool is_positive_;
527 : int capture_count_;
528 : int capture_from_;
529 : Type type_;
530 : };
531 :
532 :
533 0 : class RegExpBackReference final : public RegExpTree {
534 : public:
535 336 : RegExpBackReference() : capture_(nullptr), name_(nullptr) {}
536 : explicit RegExpBackReference(RegExpCapture* capture)
537 5217 : : capture_(capture), name_(nullptr) {}
538 : void* Accept(RegExpVisitor* visitor, void* data) override;
539 : RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
540 : RegExpBackReference* AsBackReference() override;
541 : bool IsBackReference() override;
542 5502 : int min_match() override { return 0; }
543 : // The back reference may be recursive, e.g. /(\2)(\1)/. To avoid infinite
544 : // recursion, we give up. Ignorance is bliss.
545 7660 : int max_match() override { return kInfinity; }
546 3029 : int index() { return capture_->index(); }
547 : RegExpCapture* capture() { return capture_; }
548 234 : void set_capture(RegExpCapture* capture) { capture_ = capture; }
549 : const ZoneVector<uc16>* name() const { return name_; }
550 336 : void set_name(const ZoneVector<uc16>* name) { name_ = name; }
551 :
552 : private:
553 : RegExpCapture* capture_;
554 : const ZoneVector<uc16>* name_;
555 : };
556 :
557 :
558 0 : class RegExpEmpty final : public RegExpTree {
559 : public:
560 395831 : RegExpEmpty() {}
561 : void* Accept(RegExpVisitor* visitor, void* data) override;
562 : RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
563 : RegExpEmpty* AsEmpty() override;
564 : bool IsEmpty() override;
565 1937 : int min_match() override { return 0; }
566 2214 : int max_match() override { return 0; }
567 : };
568 :
569 : } // namespace internal
570 : } // namespace v8
571 :
572 : #endif // V8_REGEXP_REGEXP_AST_H_
|