PageRenderTime 21ms CodeModel.GetById 12ms app.highlight 4ms RepoModel.GetById 1ms app.codeStats 0ms

/src/lib/regular_expression/low_level/backtracking_regular_expression_builder.e

http://github.com/tybor/Liberty
Specman e | 784 lines | 578 code | 60 blank | 146 comment | 30 complexity | 3623d408503c0f2b7c70270debebac5a MD5 | raw file
  1-- This file is part of a Liberty Eiffel library.
  2-- See the full copyright at the end.
  3--
  4deferred class BACKTRACKING_REGULAR_EXPRESSION_BUILDER
  5   --
  6   -- The frame class of all the regular expression builders.
  7   --
  8
  9insert
 10   REGULAR_EXPRESSION_STRING_SCANNER
 11      rename scanned_string as expression,
 12         set_scanned_string as set_expression
 13      export {ANY} expression, set_expression, has_error, last_error, position;
 14         {BACKTRACKING_REGULAR_EXPRESSION_BUILDER} all
 15      redefine make
 16      end
 17   REGULAR_EXPRESSION_ITEM_GLOBALS
 18      export {BACKTRACKING_REGULAR_EXPRESSION_BUILDER} all
 19      end
 20
 21feature {ANY} -- make
 22   make
 23         -- Initialise the attributes.
 24      do
 25         Precursor
 26         create stack.with_capacity(10)
 27         create group_stack.with_capacity(10)
 28      end
 29
 30feature {ANY} -- behaviors
 31   is_case_insensitive: BOOLEAN
 32         -- Is the match case insensitive?
 33         -- Default is False
 34
 35   is_case_sensitive: BOOLEAN
 36         -- Is the match case sensitive?
 37         -- Default is True
 38      do
 39         Result := not is_case_insensitive
 40      end
 41
 42   set_case_sensitive
 43         -- Set the match as case sensitive.
 44      do
 45         is_case_insensitive := False
 46      ensure
 47         definition: is_case_insensitive = False and is_case_sensitive = True
 48      end
 49
 50   set_case_insensitive
 51         -- Set the match as case insensitive.
 52      do
 53         is_case_insensitive := True
 54      ensure
 55         definition: is_case_insensitive = True and is_case_sensitive = False
 56      end
 57
 58   does_any_match_newline: BOOLEAN
 59         -- Does the "any character" mark match a newline?
 60         -- Default is False
 61
 62   set_any_match_newline
 63         -- The "any character" mark will match a newline.
 64      do
 65         does_any_match_newline := True
 66      ensure
 67         definition: does_any_match_newline = True
 68      end
 69
 70   set_any_dont_match_newline
 71         -- The "any character" mark will not match a newline.
 72      do
 73         does_any_match_newline := False
 74      ensure
 75         definition: does_any_match_newline = False
 76      end
 77
 78   does_match_line_boundary: BOOLEAN
 79         -- Does the begin/end marks match line boundary?
 80         -- Default is False
 81
 82   does_match_text_boundary: BOOLEAN
 83         -- Does the begin/end marks match text boundary?
 84         -- Default is True
 85      do
 86         Result := not does_match_line_boundary
 87      ensure
 88         definition: Result = not does_match_line_boundary
 89      end
 90
 91   set_match_line_boundary
 92         -- The begin/end marks will match line boundary.
 93      do
 94         does_match_line_boundary := True
 95      ensure
 96         definition: does_match_line_boundary = True and does_match_text_boundary = False
 97      end
 98
 99   set_match_text_boundary
100         -- The begin/end marks will match text boundary.
101      do
102         does_match_line_boundary := False
103      ensure
104         definition: does_match_line_boundary = False and does_match_text_boundary = True
105      end
106
107   set_default_options
108         -- Set the default options
109      do
110         set_case_sensitive
111         set_any_dont_match_newline
112         set_match_text_boundary
113      ensure
114         is_case_sensitive
115         not does_any_match_newline
116         does_match_text_boundary
117      end
118
119feature {} -- internal behavior
120   is_greedy: BOOLEAN
121         -- Does match a maximal repeat?
122         -- Default is False
123
124   set_greedy
125         -- Will match a maximal repeat.
126      do
127         is_greedy := True
128      ensure
129         definition: is_greedy = True
130      end
131
132   set_not_greedy
133         -- Will match a minimal repeat.
134      do
135         is_greedy := False
136      ensure
137         definition: is_greedy = False
138      end
139
140   is_looking_ahead: BOOLEAN
141         -- Is building a look-ahead term?
142
143   is_looking_behind: BOOLEAN
144         -- Is building a look-behind term?
145
146   is_looking_around: BOOLEAN
147         -- Is building look-ahead or look-behind?
148      do
149         Result := is_looking_ahead or else is_looking_behind
150      end
151
152   is_looking_positive: BOOLEAN
153         -- Is the current look-around positive or negative?
154
155feature {ANY} -- parsing
156   parse_expression (expr: like expression)
157         -- Set the expression to parse and parse it.
158         -- When no error the result if put in feature
159         -- 'last_regular_expression'.
160         -- If there is an error, a human readable explanation
161         -- is retrievable by the feature 'last_error'.
162      require
163         expression_not_void: expr /= Void
164      do
165         set_expression(expr)
166         parse
167      ensure
168         error_or_result: has_error xor has_result
169      end
170
171   parse
172         -- Parse the current expression.
173         -- The result if any is got through 'last_regular_expression'
174      require
175         expression_not_void: expression /= Void
176         internal_state_ok: stack.is_empty and group_stack.is_empty
177      local
178         pattern: BACKTRACKING_REGULAR_EXPRESSION_PATTERN
179      do
180         last_group_count := 0
181         last_substrings_names.clear_count
182         clear_error
183         internal_parse
184         if not has_error then
185            pattern.make(stack.first, last_group_count, last_substrings_names)
186         end
187         stack.clear_count
188         group_stack.clear_count
189         last_pattern := pattern
190      ensure
191         internal_state_ok: stack.is_empty and group_stack.is_empty
192         error_or_result: has_error xor has_result
193      end
194
195feature {ANY} -- results
196   has_result: BOOLEAN
197         -- Did the last 'parse' or 'parse_expression' produced
198         -- a result in 'last_regular_expression'?
199      do
200         Result := last_pattern.is_valid
201      ensure
202         definition: Result = last_pattern.is_valid
203      end
204
205   last_pattern: BACKTRACKING_REGULAR_EXPRESSION_PATTERN
206         -- The last regular expression pattern built by
207         -- 'parse' or 'parse_expression'
208
209feature {}
210   internal_parse
211         -- The parse function to be implemented by the
212         -- effective builders.
213      require
214         at_first_position: position = expression.lower
215         stack_is_empty: stack.is_empty
216         no_groups: last_group_count = 0 and group_stack.is_empty
217      deferred
218      ensure
219         error_or_result: has_error or else stack.count = 1 and group_stack.is_empty
220      end
221
222feature {} -- build
223   stack: FAST_ARRAY[BACKTRACKING_NODE]
224         -- The stack of items.
225
226   group_stack: FAST_ARRAY[INTEGER]
227         -- The stack of groups
228
229   last_group_count: INTEGER
230         -- The count of groups currently found.
231
232   last_substrings_names: HASHED_BIJECTIVE_DICTIONARY[INTEGER, FIXED_STRING]
233         -- The names of the named subgroups
234      once
235         create Result.with_capacity(10)
236      end
237
238   Repeat_infiny: INTEGER -1
239         -- Constant that means "infinite repetition".
240
241   emit (item: BACKTRACKING_NODE)
242         -- Pushes 'item' on the stack.
243         --  [..] -> [.., item]
244      require
245         item_not_void: item /= Void
246      do
247         stack.add_last(item)
248      ensure
249         stack_count_increased_by_one: stack.count = old stack.count + 1
250         stack_not_empty: stack.count > 0
251      end
252
253   unemit: BACKTRACKING_NODE
254         -- Pops the Result from the stack.
255         --  [... Result] -> [...]
256      require
257         stack_not_empty: stack.count > 0
258      do
259         Result := stack.last
260         stack.remove_last
261      ensure
262         stack_count_decreased_by_one: stack.count = old stack.count - 1
263      end
264
265   emit_any_character
266         -- Push the match to any characters
267      do
268         if does_any_match_newline then
269            emit(the_any_character_item)
270         else
271            emit(the_not_end_of_line_item)
272         end
273      end
274
275   emit_begin_of_line
276         -- Push the match to begin of a line
277      do
278         if does_match_line_boundary then
279            emit(the_begin_of_line_item)
280         else
281            emit(the_begin_of_text_item)
282         end
283      end
284
285   emit_end_of_line
286         -- Push the match to end of a line
287      do
288         if does_match_line_boundary then
289            emit(the_end_of_line_item)
290         else
291            emit(the_end_of_text_item)
292         end
293      end
294
295   prepare_group
296         -- Declares that a new group begins.
297      do
298         last_group_count := last_group_count + 1
299         group_stack.add_last(last_group_count)
300      ensure
301         last_group_count_increased_by_one: last_group_count = old last_group_count + 1
302         group_greater_than_zero: last_group_count > 0
303         group_stack_count_increased_by_one: group_stack.count = old group_stack.count + 1
304         group_pushed: group_stack.last = last_group_count
305      end
306
307   emit_group
308         -- Push the "end of group" item and update the
309         -- group indicators
310         --  [.. X] -> [.., end_group(i)]
311      require
312         group_greater_than_zero: last_group_count > 0
313         group_stack_not_empty: not group_stack.is_empty
314         enough_data: stack.count > 0
315      local
316         x: BACKTRACKING_NODE_AND_LIST; y: BACKTRACKING_NODE
317      do
318         y := create {REGULAR_EXPRESSION_ITEM_END_GROUP}.make(group_stack.last)
319         x := create {BACKTRACKING_NODE_AND_LIST}.make(y, Void)
320         x := create {BACKTRACKING_NODE_AND_LIST}.make(unemit, x)
321         y := create {REGULAR_EXPRESSION_ITEM_BEGIN_GROUP}.make(group_stack.last)
322         x := create {BACKTRACKING_NODE_AND_LIST}.make(y, x)
323         emit(x)
324         group_stack.remove_last
325      ensure
326         constant_stack_count: stack.count = old stack.count
327         stack_not_empty: stack.count > 0
328         last_group_count_unchanged: last_group_count = old last_group_count
329         group_stack_count_decreased_by_one: group_stack.count = old group_stack.count - 1
330      end
331
332   emit_begin_group
333         -- Push the "begin of group" item and update the
334         -- group indicators
335         --  [..] -> [.., begin_group(i)]
336      obsolete "Use `declare_group'/`emit_group' instead (February 2006)."
337      do
338         last_group_count := last_group_count + 1
339         group_stack.add_last(last_group_count)
340         emit(create {REGULAR_EXPRESSION_ITEM_BEGIN_GROUP}.make(last_group_count))
341      ensure
342         stack_count_increased_by_one: stack.count = old stack.count + 1
343         stack_not_empty: stack.count > 0
344         last_group_count_increased_by_one: last_group_count = old last_group_count + 1
345         group_greater_than_zero: last_group_count > 0
346         group_stack_count_increased_by_one: group_stack.count = old group_stack.count + 1
347         group_pushed: group_stack.last = last_group_count
348      end
349
350   emit_end_group
351         -- Push the "end of group" item and update the
352         -- group indicators
353         --  [..] -> [.., end_group(i)]
354      obsolete "Use `declare_group'/`emit_group' instead (February 2006)."
355      require
356         group_greater_than_zero: last_group_count > 0
357         group_stack_not_empty: not group_stack.is_empty
358      do
359         emit(create {REGULAR_EXPRESSION_ITEM_END_GROUP}.make(group_stack.last))
360         group_stack.remove_last
361      ensure
362         stack_count_increased_by_one: stack.count = old stack.count + 1
363         stack_not_empty: stack.count > 0
364         last_group_count_unchanged: last_group_count = old last_group_count
365         group_stack_count_decreased_by_one: group_stack.count = old group_stack.count - 1
366      end
367
368   emit_match_previous_group (group: INTEGER)
369         -- Push the item that matches the character 'char'
370         --  [..] -> [.., previous_group(group)]
371      require
372         valid_group: 0 < group and group <= last_group_count
373         closed_group: not group_stack.has(group)
374      do
375         if is_case_sensitive then
376            emit(create {REGULAR_EXPRESSION_ITEM_PREVIOUS_GROUP}.make(group))
377         else
378            emit(create {REGULAR_EXPRESSION_ITEM_PREVIOUS_GROUP_NO_CASE}.make(group))
379         end
380      ensure
381         stack_count_increased_by_one: stack.count = old stack.count + 1
382         stack_not_empty: stack.count > 0
383      end
384
385   emit_match_single (char: CHARACTER)
386         -- Push the item that matches the character 'char'
387         --  [..] -> [.., char]
388      do
389         if is_case_sensitive then
390            emit(create {REGULAR_EXPRESSION_ITEM_SINGLE}.make(char))
391         else
392            emit(create {REGULAR_EXPRESSION_ITEM_SINGLE_NO_CASE}.make(char))
393         end
394      ensure
395         stack_count_increased_by_one: stack.count = old stack.count + 1
396         stack_not_empty: stack.count > 0
397      end
398
399   emit_match_range (lower, upper: CHARACTER)
400         -- Push the item that matches the character range 'lower'..'upper'.
401         --  [..] -> [.., lower..upper]
402      require
403         valid_range: lower <= upper
404      local
405         binf, bsup, low, up: CHARACTER
406      do
407         if is_case_sensitive then
408            emit(create {REGULAR_EXPRESSION_ITEM_RANGE}.make(lower, upper))
409         else
410            low := lower
411            up := upper
412            if up >= 'A' and then low <= 'Z' then
413               binf := low.max('A').to_lower
414               bsup := up.min('Z').to_lower
415               if binf < low and then bsup >= low then
416                  low := binf
417               end
418               if binf <= up and then bsup > up then
419                  up := bsup
420               end
421            end
422            if up >= 'a' and then low <= 'z' then
423               binf := low.max('a').to_upper
424               bsup := up.min('z').to_upper
425               if binf < low and then bsup >= low then
426                  low := binf
427               end
428               if binf <= up and then bsup > up then
429                  up := bsup
430               end
431            end
432            begin_collect
433            emit(create {REGULAR_EXPRESSION_ITEM_RANGE}.make(low, up))
434            if up >= 'A' and then low <= 'Z' then
435               binf := low.max('A').to_lower
436               bsup := up.min('Z').to_lower
437               if bsup > up or else binf < low then
438                  emit(create {REGULAR_EXPRESSION_ITEM_RANGE}.make(binf, bsup))
439               end
440            end
441            if up >= 'a' and then low <= 'z' then
442               binf := low.max('a').to_upper
443               bsup := up.min('z').to_upper
444               if bsup > up or else binf < low then
445                  emit(create {REGULAR_EXPRESSION_ITEM_RANGE}.make(binf, bsup))
446               end
447            end
448            end_collect_or
449         end
450      ensure
451         stack_count_increased_by_one: stack.count = old stack.count + 1
452         stack_not_empty: stack.count > 0
453      end
454
455   emit_match_text (text: STRING)
456         -- Push the item that matches the 'text'
457         --  [..] -> [.., text]
458      do
459         if is_looking_behind then
460            text.reverse
461         end
462         if is_case_sensitive then
463            emit(create {REGULAR_EXPRESSION_ITEM_TEXT}.make(text.twin))
464         else
465            emit(create {REGULAR_EXPRESSION_ITEM_TEXT_NO_CASE}.make(text))
466         end
467      ensure
468         stack_count_increased_by_one: stack.count = old stack.count + 1
469         stack_not_empty: stack.count > 0
470      end
471
472   begin_collect
473         -- Begin to collect a collection of items by pushing Void on the stack.
474         -- After calling 'begin_collect', one of the features
475         -- 'end_collect_or' or 'end_collect_and' have to be called.
476         -- That kind of group is intended to manage the collections
477         -- of alternatives or sequences in an optimal way.
478         --  [..] -> [.., Void]
479      do
480         stack.add_last(Void)
481      ensure
482         has_collect: stack.fast_occurrences(Void) > 0
483         emit_group_empty: stack.last = Void
484         emit_group_count_incremented: stack.fast_occurrences(Void) = old stack.fast_occurrences(Void) + 1
485      end
486
487   is_collect_empty: BOOLEAN
488         -- True if currently begun collect is empty
489      require
490         is_collecting: stack.fast_occurrences(Void) > 0
491      do
492         Result := stack.last = Void
493      ensure
494         definition: Result = (stack.last = Void)
495      end
496
497   end_collect_true
498         -- Replace an empty collection by TRUE
499         --  [.., Void] -> [.., TRUE]
500      require
501         is_collecting: stack.fast_occurrences(Void) > 0
502         collect_empty: is_collect_empty
503      do
504         stack.put(the_true_node, stack.upper)
505      end
506
507   end_collect_or
508         -- Collects the item on the stack until the collect mark (a Void)
509         -- and replace it by a single item that is a or of all of them.
510         -- The collection must not be empty.
511         -- The order of evaluation will remain.
512         -- The binary or's tree is recursive on right for efficiency.
513         --  [.., Void, X] -> [.., X]
514         --  [.., Void, Y, X] -> [.., Y or X]
515         --  [.., Void, Z, Y, X] -> [.., Z or (Y or X)]
516         --  ...
517      require
518         is_collecting: stack.fast_occurrences(Void) > 0
519         collect_not_empty: not is_collect_empty
520      local
521         x: BACKTRACKING_NODE_OR_LIST; y: BACKTRACKING_NODE
522      do
523         y := unemit
524         if stack.last = Void then
525            stack.remove_last
526            emit(y)
527         else
528            from
529            until
530               y = Void
531            loop
532               x := create {BACKTRACKING_NODE_OR_LIST}.make(y, x)
533               y := unemit
534            end
535            emit(x)
536         end
537      ensure
538         stack_not_empty: stack.count > 0
539         emit_group_count_decremented: stack.fast_occurrences(Void) = old stack.fast_occurrences(Void) - 1
540      end
541
542   revert_and_list(x: BACKTRACKING_NODE_AND_LIST): BACKTRACKING_NODE_AND_LIST
543      local
544         y, n: BACKTRACKING_NODE_AND_LIST
545      do
546         from
547            y := x
548         until
549            y = Void
550         loop
551            n := y.next
552            y.set_next(Result)
553            Result := y
554            y := n
555         end
556      end
557
558   end_collect_and
559         -- Collects the item on the stack until the collect mark (a Void)
560         -- and replace it by a single item that is a and of all of them.
561         -- The collection must not be empty.
562         -- The order of evaluation will remain.
563         -- The binary and's tree is recursive on right for efficiency.
564         --  [.., Void, X] -> [.., X]
565         --  [.., Void, Y, X] -> [.., Y and X]
566         --  [.., Void, Z, Y, X] -> [.., Z and (Y and X)]
567         --  ...
568      require
569         is_collecting: stack.fast_occurrences(Void) > 0
570         collect_not_empty: not is_collect_empty
571      local
572         x: BACKTRACKING_NODE_AND_LIST; y: BACKTRACKING_NODE
573      do
574         y := unemit
575         if stack.last = Void then
576            stack.remove_last
577            emit(y)
578         else
579            from
580            until
581               y = Void
582            loop
583               x := create {BACKTRACKING_NODE_AND_LIST}.make(y, x)
584               y := unemit
585            end
586            if is_looking_behind then
587               x := revert_and_list(x)
588            end
589            emit(x)
590         end
591      ensure
592         stack_not_empty: stack.count > 0
593         emit_group_count_decremented: stack.fast_occurrences(Void) = old stack.fast_occurrences(Void) - 1
594      end
595
596   emit_not
597         -- Replaces the top of the stack by its negation.
598         --  [.., X] -> [.., not(X)]
599         -- (where not(X) is like (X and (CUT and FALSE)) or TRUE)
600      require
601         enough_data: stack.count > 0
602      local
603         node, x: BACKTRACKING_NODE
604      do
605         x := unemit
606         node := create {BACKTRACKING_NODE_NOT}.make(x)
607         emit(node)
608      ensure
609         constant_stack_count: stack.count = old stack.count
610         stack_not_empty: stack.count > 0
611      end
612
613   emit_not_then_any
614         -- Replaces the top of the stack by its negation followed by any.
615         --  [.., X] -> [.., not(X)]
616         -- (where not(X) is like (X and (CUT and FALSE)) or ANY)
617      require
618         enough_data: stack.count > 0
619      local
620         node, x: BACKTRACKING_NODE
621      do
622         x := unemit
623         node := create {REGULAR_EXPRESSION_ITEM_NOT_THEN_ANY}.make(x)
624         emit(node)
625      ensure
626         constant_stack_count: stack.count = old stack.count
627         stack_not_empty: stack.count > 0
628      end
629
630   emit_true_or
631         -- Replaces the top of the stack by true or it
632         --  [.., X] -> [.., true or X]
633      require
634         enough_data: stack.count > 0
635      local
636         node, x: BACKTRACKING_NODE
637      do
638         x := unemit
639         node := create {BACKTRACKING_NODE_TRUE_OR}.make(x)
640         emit(node)
641      ensure
642         constant_stack_count: stack.count = old stack.count
643         stack_not_empty: stack.count > 0
644      end
645
646   emit_or_true
647         -- Replaces the top of the stack by it or true
648         --  [.., X] -> [.., X or true]
649      require
650         enough_data: stack.count > 0
651      local
652         node, x: BACKTRACKING_NODE
653      do
654         x := unemit
655         node := create {BACKTRACKING_NODE_OR_TRUE}.make(x)
656         emit(node)
657      ensure
658         constant_stack_count: stack.count = old stack.count
659         stack_not_empty: stack.count > 0
660      end
661
662   emit_controled_or_true
663         -- Replaces the top of the stack by
664         --  if is_greedy then [.., X] -> [.., X or true]
665         --               else [.., X] -> [.., true or X]
666      local
667         x: BACKTRACKING_NODE
668      do
669         x := unemit
670         emit(controled_or_true_item(x))
671      end
672
673   controled_or_true_item (x: BACKTRACKING_NODE): BACKTRACKING_NODE
674         -- Returns an item for " 'x' or true ". The returned item depend on
675         -- the flag 'is_greedy'.
676         --  if is_greedy then Result = (X or true)
677         --               else Result = (true or X)
678      do
679         if is_greedy then
680            Result := create {BACKTRACKING_NODE_OR_TRUE}.make(x)
681         else
682            Result := create {BACKTRACKING_NODE_TRUE_OR}.make(x)
683         end
684      end
685
686   emit_repeat (mini, maxi: INTEGER)
687         -- Takes the top of the stack and replace it with
688         -- a construction that will evaluate the repeating of
689         -- it from 'mini' to 'maxi' times.
690         -- The feature boolean 'is_greedy' controls if
691         -- the matched repeat will be of minimal length
692         -- or of maximal length. That feature
693         -- is reset to its default (False) value.
694      require
695         enough_data: stack.count > 0
696         mini_is_valid: mini >= 0 and then mini /= Repeat_infiny
697         maxi_is_valid: maxi = Repeat_infiny or else maxi >= mini
698         not_droping: mini = 0 implies maxi /= 0
699      local
700         expr, resu: BACKTRACKING_NODE; exp_and: BACKTRACKING_NODE_AND_PAIR; i: INTEGER
701      do
702         expr := unemit
703         if maxi = Repeat_infiny then
704            create exp_and.make(expr, the_false_node)
705            resu := controled_or_true_item(exp_and)
706            exp_and.set_second(resu)
707         elseif mini < maxi then
708            from
709               resu := controled_or_true_item(expr)
710               i := mini + 2
711            until
712               i > maxi
713            loop
714               create exp_and.make(expr, resu)
715               resu := controled_or_true_item(exp_and)
716               i := i + 1
717            end
718         end
719         from
720            if mini = maxi then
721               check
722                  resu = Void
723               end
724               i := 2
725               resu := expr
726            else
727               check
728                  resu /= Void
729               end
730               i := 1
731            end
732         until
733            i > mini
734         loop
735            create exp_and.make(expr, resu)
736            resu := exp_and
737            i := i + 1
738         end
739         emit(resu)
740      ensure
741         constant_stack_count: stack.count = old stack.count
742         stack_not_empty: stack.count > 0
743      end
744
745   emit_looking
746      require
747         enough_data: stack.count > 0
748         is_looking: is_looking_around
749      local
750         node, x: BACKTRACKING_NODE
751      do
752         x := unemit
753         node := create {REGULAR_EXPRESSION_ITEM_LOOK}.make(x, is_looking_ahead, is_looking_positive)
754         emit(node)
755      ensure
756         constant_stack_count: stack.count = old stack.count
757         stack_not_empty: stack.count > 0
758      end
759
760invariant
761   last_string_not_void: last_string /= Void
762   stack_not_void: stack /= Void
763
764end -- class BACKTRACKING_REGULAR_EXPRESSION_BUILDER
765--
766-- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file.
767--
768-- Permission is hereby granted, free of charge, to any person obtaining a copy
769-- of this software and associated documentation files (the "Software"), to deal
770-- in the Software without restriction, including without limitation the rights
771-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
772-- copies of the Software, and to permit persons to whom the Software is
773-- furnished to do so, subject to the following conditions:
774--
775-- The above copyright notice and this permission notice shall be included in
776-- all copies or substantial portions of the Software.
777--
778-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
779-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
780-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
781-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
782-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
783-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
784-- THE SOFTWARE.