/src/lib/regular_expression/regular_expression.e
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.