PageRenderTime 32ms CodeModel.GetById 2ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 1ms

/src/lib/regular_expression/regular_expression.e

http://github.com/tybor/Liberty
Specman e | 799 lines | 581 code | 52 blank | 166 comment | 23 complexity | 4609aef74b10375870996f3b6412dc02 MD5 | raw file
  1-- This file is part of a Liberty Eiffel library.
  2-- See the full copyright at the end.
  3--
  4deferred class REGULAR_EXPRESSION
  5   --
  6   -- Regular expression matching and substitution capabilities.
  7   -- Use REGULAR_EXPRESSION_BUILDER to create REGULAR_EXPRESSION objects.
  8   --
  9   -- See tutorial/regular_expression for usage.
 10   --
 11
 12feature {ANY} -- matching capabilities
 13   match (text: ABSTRACT_STRING): BOOLEAN
 14         -- Returns True if `Current' regular_expression can match the `text'.
 15         --
 16         -- See also `match_next', `match_from', `last_match_succeeded', `last_match_first_index'.
 17      require
 18         text /= Void
 19         save_matching_text(text)
 20      do
 21         Result := match_from(text, text.lower)
 22      ensure
 23         Result = last_match_succeeded
 24         Result implies valid_substrings(text)
 25         Result implies last_match_first_index.in_range(text.lower, text.upper + 1)
 26         Result implies last_match_first_index <= last_match_last_index + 1
 27      end
 28
 29   match_from (text: ABSTRACT_STRING; first_index: INTEGER): BOOLEAN
 30         -- Returns True if `Current' regular_expression can match the `text' starting from `first_index'.
 31         --
 32         -- See also `match', `last_match_succeeded', `last_match_first_index'.
 33      require
 34         text /= Void
 35         first_index.in_range(text.lower, text.upper + 1)
 36         save_matching_text(text)
 37      deferred
 38      ensure
 39         Result = last_match_succeeded
 40         Result implies valid_substrings(text)
 41         Result implies last_match_first_index >= first_index
 42         Result implies last_match_first_index.in_range(text.lower, text.upper + 1)
 43         Result implies last_match_first_index <= last_match_last_index + 1
 44      end
 45
 46   match_next (text: ABSTRACT_STRING): BOOLEAN
 47         -- Returns True if `Current' regular_expression can match the same `text' one more time.
 48         -- Must be called after a successful `match' or `math_from' or `match_next' using the same `text'.
 49         --
 50         -- See also `match', `match_from', `last_match_succeeded'.
 51      require
 52         text /= Void
 53         last_match_succeeded
 54         text.has_prefix(last_match_text)
 55         save_matching_text(text)
 56      do
 57         Result := match_from(text, last_match_last_index + 1)
 58      ensure
 59         Result = last_match_succeeded
 60         Result implies valid_substrings(text)
 61         Result implies last_match_first_index.in_range(text.lower, text.upper + 1)
 62         Result implies last_match_first_index <= last_match_last_index + 1
 63      end
 64
 65   last_match_succeeded: BOOLEAN
 66         -- Did last match succeed?
 67         --
 68         -- See also `match', `match_from'.
 69      do
 70         Result := substrings_first_indexes.item(0) > 0
 71      end
 72
 73   last_match_first_index: INTEGER
 74         -- The starting position in the text where starts the sub-string who is matching the whole pattern.
 75         --
 76         -- See also `match', `match_from'.
 77      require
 78         last_match_succeeded
 79      do
 80         Result := substrings_first_indexes.item(0)
 81      ensure
 82         Result > 0
 83      end
 84
 85   last_match_last_index: INTEGER
 86         -- The last position in the text where starts the sub-string who is matching the whole pattern.
 87         --
 88         -- See also `match', `match_from'.
 89      require
 90         last_match_succeeded
 91      do
 92         Result := substrings_last_indexes.item(0)
 93      ensure
 94         Result + 1 >= last_match_first_index
 95      end
 96
 97   last_match_count: INTEGER
 98         -- Length of the string matching the whole pattern.
 99         --
100         -- See also `last_match_first_index', `last_match_last_index', `match', `match_from'.
101      require
102         last_match_succeeded
103      do
104         Result := last_match_last_index - last_match_first_index + 1
105      ensure
106         Result >= 0
107         definition: Result = last_match_last_index - last_match_first_index + 1
108      end
109
110   group_count: INTEGER
111         -- Number of groups in `Current' regular expression.
112         --
113         -- See also `ith_group_matched', `ith_group_first_index'.
114      do
115         Result := substrings_first_indexes.upper
116      end
117
118   group_names: TRAVERSABLE[FIXED_STRING]
119         -- The names of the matched named group.
120         --
121         -- See also `named_group_matched', `named_group_first_index'.
122      do
123         set_group_names_memory
124         Result := group_names_memory
125      ensure
126         Result /= Void
127         Result.for_all(agent (s: FIXED_STRING): BOOLEAN do Result := s /= Void end (?))
128         Result.count = substrings_names.count
129         Result.for_all(agent (s: FIXED_STRING): BOOLEAN do Result := substrings_names.fast_has(s) end (?))
130         Result.for_all(agent (s: FIXED_STRING): BOOLEAN do Result := has_group_name(s) end (?))
131      end
132
133   has_group_name (name: ABSTRACT_STRING): BOOLEAN
134         -- Is there a group names `name'?
135      require
136         name /= Void
137      do
138         set_group_names_memory
139         Result := group_names_memory.fast_has(name.intern)
140      end
141
142   ith_group_matched (i: INTEGER): BOOLEAN
143         -- Did the `i'th group match during last match?
144         --
145         -- See also `group_count', `ith_group_first_index'.
146      require
147         i.in_range(0, group_count)
148         last_match_succeeded
149      do
150         Result := substrings_first_indexes.item(i) > 0
151      end
152
153   named_group_matched (name: ABSTRACT_STRING): BOOLEAN
154         -- Did the group named `name' match during the last match?
155         --
156         -- See also `group_names', `named_group_first_index'.
157      require
158         name /= Void
159         has_group_name(name)
160      do
161         Result := ith_group_matched(substrings_names.fast_at(name.intern))
162      end
163
164   ith_group_first_index (i: INTEGER): INTEGER
165         -- First index in the last matching text of the `i'th group.
166         --
167         -- See also `group_count'.
168      require
169         i.in_range(0, group_count)
170         last_match_succeeded
171         ith_group_matched(i)
172      do
173         Result := substrings_first_indexes.item(i)
174      ensure
175         Result.in_range(0, last_match_text.upper + 1)
176      end
177
178   named_group_first_index (name: ABSTRACT_STRING): INTEGER
179         -- First index in the last matching text of the group named `name'.
180         --
181         -- See also `group_names'.
182      require
183         name /= Void
184         has_group_name(name)
185         last_match_succeeded
186         named_group_matched(name)
187      do
188         Result := ith_group_first_index(substrings_names.fast_at(name.intern))
189      end
190
191   ith_group_last_index (i: INTEGER): INTEGER
192         -- Last index in the last matching text of the `i'th group.
193         --
194         -- See also `ith_group_first_index', `group_count'.
195      require
196         i.in_range(0, group_count)
197         last_match_succeeded
198         ith_group_matched(i)
199      do
200         Result := substrings_last_indexes.item(i)
201      ensure
202         Result.in_range(ith_group_first_index(i) - 1, last_match_text.upper)
203      end
204
205   named_group_last_index (name: ABSTRACT_STRING): INTEGER
206         -- Last index in the last matching text of the group named `name'.
207         --
208         -- See also `named_group_first_index', `group_names'.
209      require
210         name /= Void
211         has_group_name(name)
212         last_match_succeeded
213         named_group_matched(name)
214      do
215         Result := ith_group_last_index(substrings_names.fast_at(name.intern))
216      end
217
218   ith_group_count (i: INTEGER): INTEGER
219         -- Length of the `i'th group of `Current' in the last matching.
220         --
221         -- See also `ith_group_first_index', `append_ith_group', `group_count'.
222      require
223         i.in_range(0, group_count)
224         last_match_succeeded
225         ith_group_matched(i)
226      do
227         Result := substrings_last_indexes.item(i) - substrings_first_indexes.item(i) + 1
228      ensure
229         Result >= 0
230         Result = ith_group_last_index(i) - ith_group_first_index(i) + 1
231      end
232
233   named_group_count (name: ABSTRACT_STRING): INTEGER
234         -- Length of the group named `name' in the last matching.
235         --
236         -- See also `named_group_first_index', `append_named_group', `group_names'.
237      require
238         name /= Void
239         has_group_name(name)
240         last_match_succeeded
241         named_group_matched(name)
242      local
243         i: INTEGER
244      do
245         i := substrings_names.fast_at(name.intern)
246         Result := substrings_last_indexes.item(i) - substrings_first_indexes.item(i) + 1
247      ensure
248         Result >= 0
249         Result = named_group_last_index(name) - named_group_first_index(name) + 1
250      end
251
252   for_all_matched_named_groups (text: ABSTRACT_STRING; action: PROCEDURE[TUPLE[FIXED_STRING, STRING]])
253         -- Call the `action' for each group that matched during the last match.
254         -- The first action argument is the name of the group; the second is its content.
255         -- The order of the action calls is the ascending order of the group definitions in the pattern.
256         --
257         -- Note: the same STRING objects may be reused, so be sure to copy them if you want to keep them.
258      require
259         text /= Void
260         action /= Void
261         last_match_succeeded
262         text.has_prefix(last_match_text)
263      local
264         i: INTEGER; group_name: FIXED_STRING; group_data: STRING
265      do
266         from
267            group_data := once ""
268            i := 1
269         until
270            i > group_count
271         loop
272            if ith_group_matched(i) and then substrings_names.fast_has_value(i) then
273               group_name := substrings_names.fast_key_at(i)
274               group_data.clear_count
275               append_ith_group(text, group_data, i)
276               action.call([group_name, group_data])
277            end
278            i := i + 1
279         end
280      end
281
282   append_heading_text (text: ABSTRACT_STRING; buffer: STRING)
283         -- Append in `buffer' the text before the matching area.
284         -- `text' is the same as used in last matching.
285         --
286         -- See also `append_pattern_text', `append_tailing_text', `append_ith_group'.
287      require
288         text /= Void
289         buffer /= Void
290         last_match_succeeded
291         text.has_prefix(last_match_text)
292      do
293         buffer.append_substring(text, 1, substrings_first_indexes.item(0))
294      ensure
295         buffer.count = old buffer.count + last_match_first_index - 1
296      end
297
298   append_pattern_text (text: ABSTRACT_STRING; buffer: STRING)
299         -- Append in `buffer' the text matching the pattern.
300         -- `text' is the same as used in last matching.
301         --
302         -- See also `append_heading_text', `append_tailing_text', `append_ith_group'.
303      require
304         text /= Void
305         buffer /= Void
306         last_match_succeeded
307         text.has_prefix(last_match_text)
308      do
309         buffer.append_substring(text, substrings_first_indexes.item(0), substrings_last_indexes.item(0))
310      ensure
311         buffer.count = old buffer.count + last_match_count
312      end
313
314   append_tailing_text (text: ABSTRACT_STRING; buffer: STRING)
315         -- Append in `buffer' the text after the matching area.
316         -- `text' is the same as used in last matching.
317         --
318         -- See also `append_heading_text', `append_pattern_text', `append_ith_group'.
319      require
320         text /= Void
321         buffer /= Void
322         last_match_succeeded
323         text.is_equal(last_match_text)
324      do
325         buffer.append_substring(text, substrings_last_indexes.item(0) + 1, text.count)
326      ensure
327         buffer.count = old buffer.count + text.count - last_match_last_index
328      end
329
330   append_ith_group (text: ABSTRACT_STRING; buffer: STRING; i: INTEGER)
331         -- Append in `buffer' the text of the `i'th group.
332         -- `text' is the same as used in last matching.
333         --
334         -- See also `append_pattern_text', `group_count'.
335      require
336         text /= Void
337         buffer /= Void
338         last_match_succeeded
339         text.is_equal(last_match_text)
340         i.in_range(0, group_count)
341         ith_group_matched(i)
342      do
343         buffer.append_substring(text, substrings_first_indexes.item(i), substrings_last_indexes.item(i))
344      ensure
345         buffer.count = old buffer.count + ith_group_count(i)
346      end
347
348   append_named_group (text: ABSTRACT_STRING; buffer: STRING; name: ABSTRACT_STRING)
349         -- Append in `buffer' the text of the group named `name'.
350         -- `text' is the same as used in last matching.
351         --
352         -- See also `append_pattern_text', `group_name'.
353      require
354         text /= Void
355         buffer /= Void
356         last_match_succeeded
357         text.is_equal(last_match_text)
358         name /= Void
359         has_group_name(name)
360         named_group_matched(name)
361      local
362         i: INTEGER
363      do
364         i := substrings_names.fast_at(name.intern)
365         buffer.append_substring(text, substrings_first_indexes.item(i), substrings_last_indexes.item(i))
366      ensure
367         buffer.count = old buffer.count + named_group_count(name)
368      end
369
370   named_group_value (text, name: ABSTRACT_STRING): STRING
371         -- Returns the text of the group named `name' (always the same STRING!)
372         -- `text' is the same as used in last matching.
373         --
374         -- See also `append_named_group', `group_name'.
375      require
376         text /= Void
377         last_match_succeeded
378         text.is_equal(last_match_text)
379         name /= Void
380         has_group_name(name)
381         named_group_matched(name)
382      do
383         Result := once ""
384         Result.clear_count
385         append_named_group(text, Result, name)
386      end
387
388feature {ANY} -- substitution capabilities
389   prepare_substitution (p: ABSTRACT_STRING)
390         -- Set pattern `p' for substitution. If pattern `p' is not compatible with the `Current' regular
391         -- expression, the `pattern_error_message' is updated as well as `pattern_error_position'.
392         --
393         -- See also `substitute_in', `substitute_for', `substitute_all_in', `substitute_all_for'.
394      require
395         p /= Void
396      local
397         in_verbatim_text: BOOLEAN; i: INTEGER
398      do
399         from
400            if compiled_substitution_pattern = Void then
401               create compiled_substitution_pattern.with_capacity(4)
402            else
403               compiled_substitution_pattern.clear_count
404            end
405            substitution_pattern_ready := True
406            substitution_pattern.make_from_string(p)
407            substrings_first_indexes.resize(0, substrings_first_indexes.upper)
408            substrings_last_indexes.resize(0, substrings_last_indexes.upper)
409            i := 1
410         until
411            i > substitution_pattern.count
412         loop
413            if substitution_pattern.item(i) = '\' and then i < substitution_pattern.count and then substitution_pattern.item(i + 1).is_digit then
414               if in_verbatim_text then
415                  substrings_last_indexes.add_first(i - 1)
416                  substrings_last_indexes.reindex(substrings_last_indexes.lower - 1)
417                  in_verbatim_text := False
418               end
419               i := i + 1
420               if substitution_pattern.item(i).value > substrings_first_indexes.upper then
421                  pattern_error_position := i
422                  pattern_error_message := once "Invalid reference for current pattern."
423                  substitution_pattern_ready := False
424                  i := substitution_pattern.count
425               end
426               compiled_substitution_pattern.add_last(substitution_pattern.item(i).value)
427            else
428               if substitution_pattern.item(i) = '\' and then i < substitution_pattern.count then
429                  substitution_pattern.remove(i)
430               end
431               if not in_verbatim_text then
432                  substrings_first_indexes.add_first(i)
433                  substrings_first_indexes.reindex(substrings_first_indexes.lower - 1)
434                  compiled_substitution_pattern.add_last(substrings_first_indexes.lower)
435                  in_verbatim_text := True
436               end
437            end
438            i := i + 1
439         end
440         if in_verbatim_text then
441            check
442               i - 1 = substitution_pattern.count
443            end
444            substrings_last_indexes.add_first(i - 1)
445            substrings_last_indexes.reindex(substrings_last_indexes.lower - 1)
446         end
447      ensure
448         substitution_pattern_ready implies valid_substitution
449         substitution_pattern_ready xor pattern_error_message /= Void
450      end
451
452   last_substitution: STRING
453         -- You need to copy this STRING if you want to keep it.
454      do
455         Result := last_substitution_memory
456         if Result = Void then
457            create Result.make(128)
458            last_substitution_memory := Result
459         end
460      end
461
462   substitute_for (text: ABSTRACT_STRING)
463         -- This call has to be preceded by a successful matching on the same text.
464         -- Then the substitution is made on the matching part. The result is in `last_substitution'.
465         --
466         -- See also `prepare_substitution', `last_substitution', `substitute_in'.
467      require
468         can_substitute
469         text /= Void
470         text.is_equal(last_match_text)
471      local
472         i, first: INTEGER; src: ABSTRACT_STRING; index: INTEGER
473      do
474         from
475            last_substitution.copy_substring(text, 1, last_match_first_index - 1)
476            i := compiled_substitution_pattern.lower
477         until
478            i > compiled_substitution_pattern.upper
479         loop
480            index := compiled_substitution_pattern.item(i)
481            if index < 0 then
482               src := substitution_pattern
483            else
484               src := text
485            end
486            first := substrings_first_indexes.item(index)
487            if first > 0 then
488               last_substitution.append_substring(src, first, substrings_last_indexes.item(index))
489            end
490            i := i + 1
491         end
492         last_substitution.append_substring(text, last_match_last_index + 1, text.count)
493         invalidate_last_match
494      ensure
495         last_substitution /= Void
496         substitution_pattern_ready
497         only_one_substitution_per_match: not can_substitute
498      end
499
500   substitute_in (text: STRING)
501         -- This call has to be preceded by a successful matching on the same text.
502         -- Then the substitution is made in `text' on the matching
503         -- part (`text' is modified).
504         --
505         -- See also `prepare_substitution', `substitute_for'.
506      require
507         can_substitute
508         text /= Void
509         text.is_equal(last_match_text)
510      do
511         substitute_for(text)
512         text.copy(last_substitution)
513      ensure
514         substitution_pattern_ready
515         only_one_substitution_per_match: not can_substitute
516      end
517
518   substitute_all_for (text: ABSTRACT_STRING)
519         -- Every matching part is substituted. No preliminary matching is required.
520         -- The result is in `last_substitution'.
521         --
522         -- See also `prepare_substitution', `last_substitution', `substitute_all_in'.
523      require
524         substitution_pattern_ready
525         text /= Void
526      local
527         text_pos: INTEGER
528      do
529         text_pos := substitute_all_without_tail(text)
530         if text_pos = 1 then
531            last_substitution.make_from_string(text)
532         else
533            last_substitution.append_substring(text, text_pos, text.count)
534         end
535      ensure
536         last_substitution /= Void
537         substitution_pattern_ready
538      end
539
540   substitute_all_in (text: STRING)
541         -- Every matching part is substituted. No preliminary matching is required.
542         -- `text' is modified according to the substitutions is any.
543         --
544         -- See also `prepare_substitution', `last_substitution', `substitute_all_for'.
545      require
546         substitution_pattern_ready
547         text /= Void
548      local
549         text_pos: INTEGER
550      do
551         text_pos := substitute_all_without_tail(text)
552         if text_pos /= 1 then
553            text.replace_substring(last_substitution, 1, text_pos - 1)
554         end
555      ensure
556         substitution_pattern_ready
557      end
558
559   can_substitute: BOOLEAN
560         -- Substitution is only allowed when some valid substitution
561         -- pattern has been registered and after a successful pattern matching.
562         --
563         -- See also `substitute_in', `substitute_for'.
564      do
565         Result := substitution_pattern_ready and last_match_succeeded
566      ensure
567         definition: Result = (substitution_pattern_ready and last_match_succeeded)
568      end
569
570   substitution_pattern_ready: BOOLEAN -- True if some valid substitution pattern has been registered.
571
572feature {ANY} -- Error informations
573   pattern_error_message: STRING
574         -- Error message for the substitution pattern.
575         --
576         -- See also `prepare_substitution'.
577
578   pattern_error_position: INTEGER
579         -- Error position in the substitution pattern.
580         --
581         -- See also `prepare_substitution'.
582
583feature {}
584   save_matching_text (text: ABSTRACT_STRING): BOOLEAN
585         -- Used in assertion only. Side-effect: save the text
586      do
587         last_match_text.make_from_string(text)
588         Result := True
589      ensure
590         Result -- Assertion only feature
591      end
592
593   invalidate_last_match
594         -- Used to prevent 2 substitutions without intermediate matching.
595      require
596         last_match_succeeded
597      do
598         substrings_first_indexes.put(0, 0)
599      ensure
600         not last_match_succeeded
601         not can_substitute
602      end
603
604   valid_substrings (text: ABSTRACT_STRING): BOOLEAN
605         -- Used in assertion only.
606      require
607         last_match_succeeded
608      local
609         i, first, last: INTEGER
610      do
611         from
612            i := 0
613            first := substrings_first_indexes.item(0)
614            last := substrings_last_indexes.item(0)
615            Result := text.valid_index(first)
616            if not Result then
617               Result := text.upper + 1 = first and then first = last + 1
618            elseif last < first then
619               Result := first = last + 1
620            else
621               Result := text.valid_index(last) and then first <= last
622            end
623         until
624            not Result or i >= substrings_first_indexes.upper
625         loop
626            i := i + 1
627            first := substrings_first_indexes.item(i)
628            last := substrings_last_indexes.item(i)
629            Result := first = 0
630            if not Result then
631               Result := first.in_range(last_match_text.lower, last_match_text.upper + 1)
632               Result := Result and then last.in_range(first - 1, last_match_text.upper)
633            end
634         end
635      ensure
636         Result -- Method for assertion only (error position is element item `i')
637      end
638
639   valid_substitution: BOOLEAN
640         -- Used in assertion only.
641      local
642         i, size: INTEGER
643      do
644         if substrings_first_indexes.valid_index(-1) then
645            from
646               i := -1
647               Result := substitution_pattern.valid_index(substrings_first_indexes.item(-1))
648            until
649               not Result or i < substrings_first_indexes.lower
650            loop
651               Result := substrings_first_indexes.item(i) <= substrings_last_indexes.item(i)
652               if substrings_first_indexes.valid_index(i - 1) then
653                  Result := Result and then substrings_last_indexes.item(i) < substrings_first_indexes.item(i - 1)
654               end
655               size := size + substrings_last_indexes.item(i) - substrings_first_indexes.item(i) + 1
656               i := i - 1
657            end
658            Result := Result and then substitution_pattern.valid_index(substrings_last_indexes.first)
659         else
660            Result := compiled_substitution_pattern.count = 0
661         end
662         from
663            i := compiled_substitution_pattern.upper
664         until
665            i < compiled_substitution_pattern.lower
666         loop
667            Result := Result and then substrings_last_indexes.valid_index(compiled_substitution_pattern.item(i))
668            i := i - 1
669         end
670         if Result then
671            Result := substitution_pattern.count - size = (compiled_substitution_pattern.count - -substrings_last_indexes.lower) * 2
672         end
673      ensure
674         Result -- Method for assertion only
675      end
676
677   substitute_all_without_tail (text: ABSTRACT_STRING): INTEGER
678         -- Substitute all matching parts from `text'. The resulting text
679         -- in `last_substitution', except the end. The part of `text' from
680         -- `Result' up to the end is not copied.
681      require
682         substitution_pattern_ready
683         text /= Void
684      local
685         i: INTEGER; src: ABSTRACT_STRING; index: INTEGER
686      do
687         from
688            last_substitution.clear_count
689            Result := 1
690         until
691            not match_from(text, Result)
692         loop
693            last_substitution.append_substring(text, Result, last_match_first_index - 1)
694            from
695               i := compiled_substitution_pattern.lower
696            until
697               i > compiled_substitution_pattern.upper
698            loop
699               index := compiled_substitution_pattern.item(i)
700               if index < 0 then
701                  src := substitution_pattern
702               else
703                  src := text
704               end
705               last_substitution.append_substring(src, substrings_first_indexes.item(index), substrings_last_indexes.item(index))
706               i := i + 1
707            end
708            Result := last_match_last_index + 1
709         end
710      ensure
711         last_substitution /= Void
712         substitution_pattern_ready
713      end
714
715   substrings_first_indexes: ARRAY[INTEGER]
716         -- Item(0) is the starting position in the text where
717         -- starts the substring who is matching the whole pattern.
718         -- Next elements are the starting positions in the text of
719         -- substrings matching sub-elements of the pattern.
720         --
721         -- Elements before item(0) refers to positions in the
722         -- `substitution_pattern'. They are stored in reverse order,
723         -- the first verbatim string being at index -1, the
724         -- second one at index -2...
725
726   substrings_last_indexes: ARRAY[INTEGER]
727         -- The ending position of the string starting at position
728         -- found in `matching_position' at the same index.
729
730   substrings_names: BIJECTIVE_DICTIONARY[INTEGER, FIXED_STRING]
731         -- The names of the groups, if those names exist
732
733   group_names_memory: COLLECTION[FIXED_STRING]
734         -- Cache for `group_names'
735
736   substitution_pattern: STRING
737      once
738         create Result.make_empty
739      end
740
741   compiled_substitution_pattern: FAST_ARRAY[INTEGER]
742         -- This array describe the substitution text as a suite of
743         -- strings from `substrings_first_indexes'.
744
745   last_match_text: STRING
746         -- For assertion only.
747      do
748         Result := last_match_text_memory
749         if Result = Void then
750            create Result.make(128)
751            last_match_text_memory := Result
752         end
753      end
754
755   set_group_names_memory
756      do
757         if group_names_memory = Void then
758            create {FAST_ARRAY[FIXED_STRING]} group_names_memory.with_capacity(substrings_names.count)
759         else
760            group_names_memory.clear_count
761         end
762         substrings_names.key_map_in(group_names_memory)
763      end
764
765   last_match_text_memory: STRING -- For assertion only.
766
767   last_substitution_memory: STRING
768
769invariant
770   substrings_first_indexes.lower = substrings_last_indexes.lower
771   substrings_first_indexes.upper = substrings_last_indexes.upper
772   substrings_names.is_empty or else (substrings_names.count <= substrings_first_indexes.count
773                                      and then substrings_names.for_all(agent (i: INTEGER; s: FIXED_STRING): BOOLEAN
774                                                                           do
775                                                                              Result := s /= Void
776                                                                                 and then substrings_first_indexes.valid_index(i)
777                                                                           end (?, ?)))
778
779end -- class REGULAR_EXPRESSION
780--
781-- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file.
782--
783-- Permission is hereby granted, free of charge, to any person obtaining a copy
784-- of this software and associated documentation files (the "Software"), to deal
785-- in the Software without restriction, including without limitation the rights
786-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
787-- copies of the Software, and to permit persons to whom the Software is
788-- furnished to do so, subject to the following conditions:
789--
790-- The above copyright notice and this permission notice shall be included in
791-- all copies or substantial portions of the Software.
792--
793-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
794-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
795-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
796-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
797-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
798-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
799-- THE SOFTWARE.