PageRenderTime 119ms CodeModel.GetById 45ms app.highlight 65ms RepoModel.GetById 1ms app.codeStats 1ms

/src/lib/string/abstract_string.e

http://github.com/tybor/Liberty
Specman e | 1420 lines | 1080 code | 76 blank | 264 comment | 77 complexity | 54013e666f8f6a744d76535ad7f042fb MD5 | raw file
   1-- This file is part of a Liberty Eiffel library.
   2-- See the full copyright at the end.
   3--
   4deferred class ABSTRACT_STRING
   5   --
   6   -- Character STRINGs indexed from `1' to `count'.
   7   --
   8   -- See also FIXED_STRING and STRING
   9
  10inherit
  11   HASHABLE
  12      redefine print_on, copy, out_in_tagged_out_memory, fill_tagged_out_memory
  13      end
  14   COMPARABLE
  15      redefine print_on, copy, out_in_tagged_out_memory, fill_tagged_out_memory, is_equal, compare, three_way_comparison,
  16         infix ">", infix "<=", infix ">="
  17      end
  18   STORABLE
  19      redefine print_on, copy, out_in_tagged_out_memory, fill_tagged_out_memory, is_equal
  20      end
  21   TRAVERSABLE[CHARACTER]
  22      redefine print_on, copy, out_in_tagged_out_memory, fill_tagged_out_memory, is_equal
  23      end
  24   SEARCHABLE[CHARACTER]
  25      redefine print_on, copy, out_in_tagged_out_memory, fill_tagged_out_memory, is_equal
  26      end
  27
  28insert
  29   PLATFORM
  30      redefine print_on, copy, out_in_tagged_out_memory, fill_tagged_out_memory, is_equal
  31      end
  32   RECYCLABLE
  33      undefine print_on, copy, out_in_tagged_out_memory, fill_tagged_out_memory, is_equal
  34      end
  35   STRING_HANDLER
  36      undefine print_on, copy, out_in_tagged_out_memory, fill_tagged_out_memory, is_equal
  37      end
  38
  39feature {ANY}
  40   lower: INTEGER 1
  41         -- Minimum index; actually, this is always 1 (this feature
  42         -- here to mimic the one of the COLLECTION hierarchy).
  43         --
  44         -- See also `upper', `valid_index', `item'.
  45
  46   upper: INTEGER
  47         -- Maximum index; actually the same value as `count' (this
  48         -- feature is here to mimic the one of the COLLECTION hierarchy).
  49         --
  50         -- See also `lower', `valid_index', `item'.
  51      do
  52         Result := count
  53      ensure
  54         Result = count
  55      end
  56
  57   copy (other: like Current)
  58      deferred
  59      ensure then
  60         count = other.count
  61      end
  62
  63   print_on (file: OUTPUT_STREAM)
  64      do
  65         file.put_abstract_string(Current)
  66      end
  67
  68feature {ANY} -- Testing:
  69   is_empty: BOOLEAN
  70         -- Has string length 0?
  71         --
  72         -- See also `count'.
  73      do
  74         Result := count = 0
  75      end
  76
  77   item (i: INTEGER): CHARACTER
  78         -- Character at position `i'.
  79         --
  80         -- See also `lower', `upper', `valid_index', `put'.
  81      deferred
  82      end
  83
  84   frozen infix "@" (i: INTEGER): CHARACTER
  85         -- The infix notation which is actually just a synonym for `item'.
  86         --
  87         -- See also `item', `put'.
  88      require
  89         valid_index: valid_index(i)
  90      do
  91         Result := item(i)
  92      ensure
  93         definition: Result = item(i)
  94      end
  95
  96    infix "^" (a_range: INTEGER_RANGE[INTEGER]): ABSTRACT_STRING
  97        -- Substring of items in `a_range' .
  98    require valid_range: valid_index(a_range.lower) and valid_index(a_range.upper)
  99    do
 100        Result := substring(a_range.lower, a_range.upper)
 101    ensure
 102        Result.count = a_range.count
 103        has_substring(Result) -- This is the same of writing "substring_index(Result,lower)=a_range.lower"
 104    end
 105
 106   infix "<" (other: ABSTRACT_STRING): BOOLEAN
 107         -- Is `Current' less than `other'?
 108         --
 109         -- See also `>', `<=', `>=', `min', `max'.
 110      local
 111         i: INTEGER; maxi: INTEGER
 112      do
 113         from
 114            i := lower
 115            maxi := count.min(other.count)
 116         until
 117            i > maxi or else item(i) /= other.item(i)
 118         loop
 119            i := i + 1
 120         end
 121         if i <= maxi then
 122            Result := item(i) < other.item(i)
 123         else
 124            Result := i <= other.count
 125         end
 126      end
 127
 128   infix "<=" (other: ABSTRACT_STRING): BOOLEAN
 129      do
 130         Result := not (other < Current)
 131      end
 132
 133   infix ">" (other: ABSTRACT_STRING): BOOLEAN
 134      do
 135         Result := other < Current
 136      end
 137
 138   infix ">=" (other: ABSTRACT_STRING): BOOLEAN
 139      do
 140         Result := not (Current < other)
 141      end
 142
 143   compare, three_way_comparison (other: ABSTRACT_STRING): INTEGER
 144      local
 145         i: INTEGER; maxi: INTEGER
 146      do
 147         from
 148            i := lower
 149            maxi := count.min(other.count)
 150         until
 151            i > maxi or else item(i) /= other.item(i)
 152         loop
 153            i := i + 1
 154         end
 155         if count < i then
 156            if other.count < i then
 157            else
 158               Result := -1
 159            end
 160         elseif other.count < i then
 161            Result := 1
 162         elseif item(i) < other.item(i) then
 163            Result := -1
 164         else
 165            Result := 1
 166         end
 167      end
 168
 169   is_equal (other: ABSTRACT_STRING): BOOLEAN
 170         -- Do both strings have the same character sequence?
 171         --
 172         -- See also `same_as'.
 173      deferred
 174      end
 175
 176   same_as (other: ABSTRACT_STRING): BOOLEAN
 177         -- Case insensitive `is_equal'.
 178      require
 179         other /= Void
 180      deferred
 181      end
 182
 183   item_code (i: INTEGER): INTEGER
 184         -- Code of character at position `i'.
 185         --
 186         -- See also `item'.
 187      require
 188         valid_index: valid_index(i)
 189      do
 190         -- TODO: find any technically sound reason to restore previous
 191         -- implementation: "Result := storage.item(i - 1).code"
 192         Result := item(i).code
 193      ensure
 194         definition: Result = item(i).code
 195      end
 196
 197   first_index_of, fast_first_index_of (c: CHARACTER): INTEGER
 198         -- Index of first occurrence of `c', `upper` + 1 if none.
 199         --
 200         -- See also `last_index_of', `index_of', `reverse_index_of'.
 201      do
 202         Result := index_of(c, lower)
 203      end
 204
 205   last_index_of, fast_last_index_of (c: CHARACTER): INTEGER
 206         -- Index of last occurrence of `c', `lower` - 1 if none.
 207         --
 208         -- See also `first_index_of', `reverse_index_of', `index_of'.
 209      do
 210         Result := reverse_index_of(c, upper)
 211      end
 212
 213   has_substring (other: ABSTRACT_STRING): BOOLEAN
 214         -- True if `Current' contains `other'.
 215         --
 216         -- See also `substring_index', `has'.
 217      require
 218         other_not_void: other /= Void
 219      do
 220         Result := substring_index(other, lower)>=lower
 221      end
 222
 223   occurrences (c: CHARACTER): INTEGER
 224         -- Number of times character `c' appears in the string.
 225         --
 226         -- See also `remove_all_occurrences', `has'.
 227      deferred
 228      ensure
 229         Result >= 0
 230      end
 231
 232   has_suffix (s: ABSTRACT_STRING): BOOLEAN
 233         -- True if suffix of `Current' is `s'.
 234         --
 235         -- See also `remove_suffix', `has_prefix', `has_substring'.
 236      require
 237         s /= Void
 238      local
 239         i1, i2: INTEGER
 240      do
 241         if s.count <= count then
 242            from
 243               i1 := count - s.count + lower
 244               i2 := lower
 245            until
 246               i1 > count or else item(i1) /= s.item(i2)
 247            loop
 248               i1 := i1 + 1
 249               i2 := i2 + 1
 250            end
 251            Result := i1 > count
 252         end
 253      end
 254
 255   has_prefix (p: ABSTRACT_STRING): BOOLEAN
 256         -- True if prefix of `Current' is `p'.
 257         --
 258         -- See also `remove_prefix', `has_suffix', `has_substring'.
 259      require
 260         p /= Void
 261      local
 262         i, ip: INTEGER
 263      do
 264         if p.count <= count then
 265            from
 266               i := lower + p.count - 1
 267                           ip := p.upper
 268            until
 269               i < lower or else item(i) /= p.item(ip)
 270            loop
 271               i := i - 1
 272                           ip := ip - 1
 273            end
 274            Result := i < lower
 275         end
 276      end
 277
 278feature {ANY} -- Testing and Conversion:
 279   is_ascii: BOOLEAN
 280         -- Is `Current' only made of (7 bit) ASCII characters?
 281      local
 282         i: INTEGER
 283      do
 284         from
 285            i := upper
 286            Result := True
 287         until
 288            i < lower or else not Result
 289         loop
 290            Result := item(i).is_ascii
 291            i := i - 1
 292         end
 293      ensure
 294         Result = for_all(agent {CHARACTER}.is_ascii)
 295      end
 296
 297   is_boolean: BOOLEAN
 298         -- Does `Current' represent a BOOLEAN?
 299         -- Valid BOOLEANs are "True" and "False".
 300      do
 301         Result := (once "True").is_equal(Current) or else (once "False").is_equal(Current)
 302      end
 303
 304   to_boolean: BOOLEAN
 305         -- Boolean value
 306         -- "True" yields True, "False" yields False (what a surprise).
 307      require
 308         represents_a_boolean: is_boolean
 309      do
 310         Result := (once "True").is_equal(Current)
 311      end
 312
 313   is_integer: BOOLEAN
 314         -- Does 'Current' represent an INTEGER?
 315         -- `Result' is True if and only if the following two conditions
 316         -- hold:
 317         --
 318         -- 1. In the following BNF grammar, the value of `Current' can be
 319         -- produced by "Integer_literal", if leading and trailing
 320         -- separators are ignored:
 321         --
 322         -- Integer_literal = [Sign] Integer
 323         -- Sign            = "+" | "-"
 324         -- Integer         = Digit | Digit Integer
 325         -- Digit           = "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
 326         --
 327         -- 2. The numerical value represented by `Current' is within the
 328         -- range that can be represented by an instance of type INTEGER.
 329      local
 330         i, state, value, bound, critical_bound: INTEGER; cc: CHARACTER
 331      do
 332         -- state 0: nothing read.
 333         -- state 1: "+" or "-" read.
 334         -- state 2: error.
 335         -- state 3: in the number.
 336         -- state 4: last digit of a critically big number
 337         -- state 5: after the number.
 338         from
 339            i := lower
 340         variant
 341            upper - i
 342         until
 343            state = 2 or else i > upper
 344         loop
 345            cc := item(i)
 346            inspect
 347               state
 348            when 0 then
 349               if cc.is_separator then
 350               elseif cc = '+' then
 351                  bound := Maximum_integer #\\ 10
 352                  critical_bound := Maximum_integer #// 10
 353                  state := 1
 354               elseif cc = '-' then
 355                  bound := -(Minimum_integer #\\ 10)
 356                  critical_bound := -(Minimum_integer #// 10)
 357                  state := 1
 358               elseif cc.is_digit then
 359                  bound := Maximum_integer #\\ 10
 360                  critical_bound := Maximum_integer #// 10
 361                  value := cc.decimal_value
 362                  state := 3
 363               else
 364                  state := 2
 365               end
 366            when 1 then
 367               if cc.is_digit then
 368                  value := cc.decimal_value
 369                  state := 3
 370               else
 371                  state := 2
 372               end
 373            when 3 then
 374               if cc.is_digit then
 375                  value := 10 * value + cc.decimal_value
 376                  if value >= critical_bound then
 377                     state := 4
 378                  end
 379               elseif cc.is_separator then
 380                  state := 5
 381               else
 382                  state := 2
 383               end
 384            when 4 then
 385               if cc.is_digit then
 386                  if value > critical_bound then
 387                     state := 2
 388                  else
 389                     if cc.decimal_value <= bound then
 390                        state := 5
 391                     else
 392                        state := 2
 393                     end
 394                  end
 395               elseif cc.is_separator then
 396                  state := 5
 397               else
 398                  state := 2
 399               end
 400            when 5 then
 401               if cc.is_separator then
 402               else
 403                  state := 2
 404               end
 405            end
 406            i := i + 1
 407         end
 408         Result := state >= 3
 409      end
 410
 411   to_integer: INTEGER
 412         -- `Current' must look like an INTEGER.
 413      require
 414         is_integer
 415      local
 416         i: INTEGER; cc: CHARACTER; negative: BOOLEAN
 417      do
 418         from
 419            i := lower
 420         variant
 421            count - i
 422         until
 423            not item(i).is_separator
 424         loop
 425            i := i + 1
 426         end
 427         cc := item(i)
 428         i := i + 1
 429         if cc = '+' then
 430            cc := item(i)
 431            i := i + 1
 432         elseif cc = '-' then
 433            negative := True
 434            cc := item(i)
 435            i := i + 1
 436         end
 437         check
 438            cc.is_digit
 439         end
 440         Result := -cc.value
 441         from
 442         variant
 443            count - i
 444         until
 445            i > count
 446         loop
 447            cc := item(i)
 448            if cc.is_digit then
 449               Result := 10 * Result - cc.decimal_value -- Should not overflow since `is_integer' is true
 450            else
 451               check
 452                  cc.is_separator
 453               end
 454               i := count -- terminate the loop
 455            end
 456            i := i + 1
 457         end
 458         if negative then
 459         else
 460            Result := -Result
 461         end
 462      end
 463
 464   is_integer_64: BOOLEAN
 465         -- Does 'Current' represent an INTEGER_64?
 466         -- `Result' is True if and only if the following two conditions
 467         -- hold:
 468         --
 469         -- 1. In the following BNF grammar, the value of `Current' can be
 470         -- produced by "Integer_literal", if leading and trailing
 471         -- separators are ignored:
 472         --
 473         -- Integer_literal = [Sign] Integer
 474         -- Sign            = "+" | "-"
 475         -- Integer         = Digit | Digit Integer
 476         -- Digit           = "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
 477         --
 478         -- 2. The numerical value represented by `Current' is within the
 479         -- range that can be represented by an instance of type INTEGER_64.
 480      local
 481         i, state: INTEGER; value, bound, critical_bound: INTEGER_64; cc: CHARACTER
 482      do
 483         -- state 0: nothing read.
 484         -- state 1: "+" or "-" read.
 485         -- state 2: error.
 486         -- state 3: in the number.
 487         -- state 4: last digit of a critically big number
 488         -- state 5: after the number.
 489         from
 490            i := lower
 491         variant
 492            count - i
 493         until
 494            state = 2 or else i > count
 495         loop
 496            cc := item(i)
 497            inspect
 498               state
 499            when 0 then
 500               if cc.is_separator then
 501               elseif cc = '+' then
 502                  bound := Maximum_integer_64 #\\ 10
 503                  critical_bound := Maximum_integer_64 #// 10
 504                  state := 1
 505               elseif cc = '-' then
 506                  bound := -(Minimum_integer_64 #\\ 10)
 507                  critical_bound := -(Minimum_integer_64 #// 10)
 508                  state := 1
 509               elseif cc.is_digit then
 510                  bound := Maximum_integer_64 #\\ 10
 511                  critical_bound := Maximum_integer_64 #// 10
 512                  value := cc.decimal_value
 513                  state := 3
 514               else
 515                  state := 2
 516               end
 517            when 1 then
 518               if cc.is_digit then
 519                  value := cc.decimal_value
 520                  state := 3
 521               else
 522                  state := 2
 523               end
 524            when 3 then
 525               if cc.is_digit then
 526                  value := 10 * value + cc.decimal_value
 527                  if value >= critical_bound then
 528                     state := 4
 529                  end
 530               elseif cc.is_separator then
 531                  state := 5
 532               else
 533                  state := 2
 534               end
 535            when 4 then
 536               if cc.is_digit then
 537                  if value > critical_bound then
 538                     state := 2
 539                  else
 540                     if cc.decimal_value <= bound then
 541                        state := 5
 542                     else
 543                        state := 2
 544                     end
 545                  end
 546               elseif cc.is_separator then
 547                  state := 5
 548               else
 549                  state := 2
 550               end
 551            when 5 then
 552               if cc.is_separator then
 553               else
 554                  state := 2
 555               end
 556            end
 557            i := i + 1
 558         end
 559         Result := state >= 3
 560      end
 561
 562   to_integer_64: INTEGER_64
 563         -- `Current' must look like an INTEGER_64.
 564      require
 565         is_integer_64
 566      local
 567         i: INTEGER; cc: CHARACTER; negative: BOOLEAN
 568      do
 569         from
 570            i := lower
 571         variant
 572            count - i
 573         until
 574            not item(i).is_separator
 575         loop
 576            i := i + 1
 577         end
 578         cc := item(i)
 579         i := i + 1
 580         if cc = '+' then
 581            cc := item(i)
 582            i := i + 1
 583         elseif cc = '-' then
 584            negative := True
 585            cc := item(i)
 586            i := i + 1
 587         end
 588         check
 589            cc.is_digit
 590         end
 591         Result := -cc.value
 592         from
 593         variant
 594            count - i
 595         until
 596            i > count
 597         loop
 598            cc := item(i)
 599            if cc.is_digit then
 600               Result := 10 * Result - cc.decimal_value -- Should not overflow since `is_integer_64' is true
 601            else
 602               check
 603                  cc.is_separator
 604               end
 605               i := count -- terminate the loop
 606            end
 607            i := i + 1
 608         end
 609         if negative then
 610         else
 611            Result := -Result
 612         end
 613      end
 614
 615   is_real: BOOLEAN
 616         -- Can contents be read as a REAL ?
 617         -- Fails for numbers where the base or "10 ^ exponent" are not in
 618         -- the range `Minimum_real' ... `Maximum_real'. Parsing is done
 619         -- positive. That means if `Minimum_real.abs' is not equal to
 620         -- `Maximum_real' it will not work correctly. Furthermore the
 621         -- arithmetic package used must support the value 'inf' for a
 622         -- number greater than Maximum_real.
 623         -- `Result' is True if and only if the following two conditions
 624         -- hold:
 625         --
 626         -- 1. In the following BNF grammar, the value of `Current' can be
 627         -- produced by "Real_literal", if leading or trailing separators
 628         -- are ignored.
 629         --
 630         -- Real_literal    = Mantissa [Exponent_part]
 631         -- Exponent_part   = "E" Exponent
 632         --                 | "e" Exponent
 633         -- Exponent        = Integer_literal
 634         -- Mantissa        = Decimal_literal
 635         -- Decimal_literal = Integer_literal ["." Integer]
 636         -- Integer_literal = [Sign] Integer
 637         -- Sign            = "+" | "-"
 638         -- Integer         = Digit | Digit Integer
 639         -- Digit           = "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
 640         --
 641         --
 642         -- 2. The numerical value represented by `Current' is within the
 643         -- range that can be represented by an instance of type REAL.
 644      local
 645         i, state: INTEGER; cc: CHARACTER; base, exp, value, multiplier: REAL; negative, neg_exp: BOOLEAN
 646      do
 647         -- state 0: nothing read.
 648         -- state 1: "+" or "-" read.
 649         -- state 2: in the number.
 650         -- state 3: decimal point read
 651         -- state 4: in fractional part
 652         -- state 5: read 'E' or 'e' for scientific notation
 653         -- state 6: read "-" or "+" sign of exponent
 654         -- state 7: in exponent
 655         -- state 8: after the number.
 656         -- state 9: error.
 657         from
 658            i := lower
 659         variant
 660            count - i
 661         until
 662            state = 9 or else i > count
 663         loop
 664            cc := item(i)
 665            inspect
 666               state
 667            when 0 then
 668               if cc.is_separator then
 669               elseif cc = '+' then
 670                  state := 1
 671               elseif cc = '-' then
 672                  negative := True
 673                  state := 1
 674               elseif cc.is_digit then
 675                  base := cc.decimal_value
 676                  state := 2
 677               elseif cc = '.' then
 678                  state := 3
 679               else
 680                  state := 9
 681               end
 682            when 1 then
 683               if cc.is_digit then
 684                  base := cc.decimal_value
 685                  state := 2
 686               elseif cc = '.' then
 687                  state := 3
 688               else
 689                  state := 9
 690               end
 691            when 2 then
 692               if cc.is_digit then
 693                  base := base * 10 + cc.decimal_value
 694               elseif cc = '.' then
 695                  state := 3
 696               elseif cc = 'e' or else cc = 'E' then
 697                  state := 5
 698               elseif cc.is_separator then
 699                  state := 8
 700               else
 701                  state := 9
 702               end
 703            when 3 then
 704               multiplier := 0.1
 705               if cc.is_digit then
 706                  base := base + multiplier * cc.decimal_value
 707                  state := 4
 708               else
 709                  state := 9
 710               end
 711            when 4 then
 712               multiplier := multiplier * 0.1
 713               if cc.is_digit then
 714                  base := base + multiplier * cc.decimal_value
 715               elseif cc.is_separator then
 716                  state := 8
 717               elseif cc = 'e' or else cc = 'E' then
 718                  state := 5
 719               else
 720                  state := 9
 721               end
 722            when 5 then
 723               if cc = '-' then
 724                  neg_exp := True
 725                  state := 6
 726               elseif cc = '+' then
 727                  state := 6
 728               elseif cc.is_digit then
 729                  exp := cc.decimal_value
 730                  state := 7
 731               else
 732                  state := 9
 733               end
 734            when 6 then
 735               if cc.is_digit then
 736                  exp := cc.decimal_value
 737                  state := 7
 738               else
 739                  state := 9
 740               end
 741            when 7 then
 742               if cc.is_digit then
 743                  exp := exp * 10 + cc.decimal_value
 744               elseif cc.is_separator then
 745                  state := 8
 746               else
 747                  state := 9
 748               end
 749            when 8 then
 750               if cc.is_separator then
 751               else
 752                  state := 9
 753               end
 754            end
 755            i := i + 1
 756         end
 757         if state /= 0 and then state /= 9 and then state /= 1 then
 758            Result := True
 759            if neg_exp then
 760               exp := -1 * exp
 761            end
 762            value := base * 10.0.pow(exp)
 763            if value > Maximum_real then
 764               -- can only happen if value = inf
 765               Result := False
 766            end
 767         end
 768      end
 769
 770   to_real: REAL
 771         -- Conversion to the corresponding REAL value. The string must looks like a REAL (or like an
 772         -- INTEGER because the fractional part is optional). For an exact definition see 'is_real'.
 773         -- Note that this conversion might not be exact.
 774      require
 775         represents_a_real: is_real
 776      local
 777         i, state: INTEGER; cc: CHARACTER; base, exp, multiplier: REAL; negative, neg_exp: BOOLEAN
 778      do
 779         -- state 0: nothing read.
 780         -- state 1: "+" or "-" read.
 781         -- state 2: in the number.
 782         -- state 3: decimal point read
 783         -- state 4: in fractional part
 784         -- state 5: read 'E' or 'e' for scientific notation
 785         -- state 6: read "-" or "+" sign of exponent
 786         -- state 7: in exponent
 787         -- state 8: after the number.
 788         from
 789            i := lower
 790         variant
 791            count - i
 792         until
 793            i > count
 794         loop
 795            cc := item(i)
 796            inspect
 797               state
 798            when 0 then
 799               if cc.is_separator then
 800               elseif cc = '+' then
 801                  state := 1
 802               elseif cc = '-' then
 803                  negative := True
 804                  state := 1
 805               elseif cc.is_digit then
 806                  base := cc.decimal_value
 807                  state := 2
 808               else
 809                  -- cc = '.'
 810                  state := 3
 811               end
 812            when 1 then
 813               if cc.is_digit then
 814                  base := cc.decimal_value
 815                  state := 2
 816               else
 817                  -- cc = '.'
 818                  state := 3
 819               end
 820            when 2 then
 821               if cc.is_digit then
 822                  base := base * 10 + cc.decimal_value
 823               elseif cc = '.' then
 824                  state := 3
 825               elseif cc.is_separator then
 826                  state := 8
 827               else
 828                  -- cc = 'e' or else cc = 'E'
 829                  state := 5
 830               end
 831            when 3 then
 832               multiplier := 0.1
 833               if cc.is_separator then
 834                  state := 8
 835               else
 836                  -- cc.is_digit
 837                  base := base + multiplier * cc.decimal_value
 838                  state := 4
 839               end
 840            when 4 then
 841               multiplier := multiplier * 0.1
 842               if cc.is_digit then
 843                  base := base + multiplier * cc.decimal_value
 844               elseif cc.is_separator then
 845                  state := 8
 846               else
 847                  -- cc = 'e' or else cc = 'E'
 848                  state := 5
 849               end
 850            when 5 then
 851               if cc = '-' then
 852                  neg_exp := True
 853                  state := 6
 854               elseif cc = '+' then
 855                  state := 6
 856               else
 857                  -- cc.is_digit
 858                  exp := cc.decimal_value
 859                  state := 7
 860               end
 861            when 6 then
 862               -- cc.is_digit
 863               exp := cc.decimal_value
 864               state := 7
 865            when 7 then
 866               if cc.is_digit then
 867                  exp := exp * 10 + cc.decimal_value
 868               else
 869                  -- cc.is_separator
 870                  state := 8
 871               end
 872            when 8 then
 873               -- cc.is_separator
 874               i := count -- terminate the loop
 875            end
 876            i := i + 1
 877         end
 878         if neg_exp then
 879            exp := -1 * exp
 880         end
 881         if negative then
 882            Result := -1 * base * 10.0.pow(exp)
 883         else
 884            Result := base * 10.0.pow(exp)
 885         end
 886      end
 887
 888   is_number: BOOLEAN
 889         -- Can contents be read as a NUMBER?
 890      local
 891         number_tools: NUMBER_TOOLS
 892      do
 893         Result := number_tools.is_number(Current)
 894      end
 895
 896   to_number: NUMBER
 897         -- Current must looks like an INTEGER.
 898      require
 899         is_number
 900      local
 901         number_tools: NUMBER_TOOLS
 902      do
 903         Result := number_tools.from_string(Current)
 904      end
 905
 906   is_bit: BOOLEAN
 907         -- True when the contents is a sequence of bits (i.e., mixed
 908         -- characters `0' and characters `1').
 909      local
 910         i: INTEGER
 911      do
 912         from
 913            i := count
 914            Result := True
 915         until
 916            not Result or else i < lower
 917         loop
 918            Result := item(i).is_bit
 919            i := i - 1
 920         end
 921      ensure
 922         Result = (count = occurrences('0') + occurrences('1'))
 923      end
 924
 925   binary_to_integer: INTEGER
 926         -- Assume there is enough space in the INTEGER to store
 927         -- the corresponding decimal value.
 928      require
 929         is_bit
 930         count <= Integer_bits
 931      local
 932         i: INTEGER
 933      do
 934         from
 935            i := lower
 936         until
 937            i > count
 938         loop
 939            if item(i) = '1' then
 940               Result := Result |<< 1 + 1
 941            else
 942               Result := Result |<< 1
 943            end
 944            i := i + 1
 945         end
 946      end
 947
 948feature {ANY} -- Concatenation
 949   infix "+" (other: ABSTRACT_STRING): STRING
 950         -- Create a new STRING which is the concatenation of `Current' and `other'.
 951         --
 952         -- See also `append'.
 953      require
 954         other_exists: other /= Void
 955      do
 956         create Result.make(count + other.count)
 957         Result.append(Current)
 958         Result.append(other)
 959      ensure
 960         result_count: Result.count = count + other.count
 961      end
 962
 963   infix "|" (other: ABSTRACT_STRING): ROPE
 964         -- Current and `other' concatenated into a new ROPE, an ABSTRACT_STRING that can be efficiently
 965         -- concatenated.
 966      require
 967         other_exists: other /= Void
 968      do
 969         create Result.from_strings(Current,other)
 970      ensure
 971         Result.out.is_equal(Current + other)
 972      end
 973
 974   infix "&" (other: ABSTRACT_STRING): ABSTRACT_STRING
 975         -- Current and `other' concatenating into a new object. The actual effective type of Result
 976         -- chosen by the implementation, possibly based on heuristics.
 977      require
 978         other_exists: other /= Void
 979      deferred
 980      ensure
 981         Result.out.is_equal(Current + other)
 982      end
 983
 984    arg (an_index: INTEGER; a_value: ABSTRACT_STRING): ABSTRACT_STRING
 985        -- A copy of Current with the placeholder "#(an_index)" is replaced (if present) with the content of `a_value'.
 986    local
 987         i, backtrack_i: INTEGER
 988         index: INTEGER
 989         state: INTEGER
 990         ch: CHARACTER
 991         delimeter, opening_brace, closing_brace: CHARACTER
 992         accumulator: STRING
 993      do
 994          delimeter := '#'; opening_brace := '('; closing_brace := ')'
 995          -- The above constants are not put in the class to avoid "polluting"
 996          -- its namespace. Feel free to move it outside this feature if it
 997          -- fitter, i.e. redefining them in an heir.
 998          accumulator := ""
 999          Result := accumulator
1000          from i := lower; state := normal_state
1001          until i > upper
1002          loop
1003              from until i > upper loop
1004                  ch := item(i)
1005                  inspect state
1006                  when always_print_state then
1007                      accumulator.append_character(ch)
1008                      state := normal_state
1009                  when normal_state then
1010                      if ch.is_equal(delimeter) then
1011                          backtrack_i := i - 1
1012                          state := after_delimiter_state
1013                      else accumulator.append_character(ch)
1014                      end
1015                  when after_delimiter_state then
1016                      if ch.is_equal(delimeter) then
1017                          accumulator.append_character(ch)
1018                          state := normal_state
1019                      elseif ch.is_equal(opening_brace) then
1020                          index := 0
1021                          state := after_brace_state
1022                      else
1023                          i := backtrack_i
1024                          state := always_print_state
1025                      end
1026                  when after_brace_state then
1027                      if ch.is_decimal_digit then
1028                          index := 10*index + ch.decimal_value
1029                      elseif ch.is_equal(closing_brace) and then index = an_index then
1030                          accumulator := "" -- newly allocated empty string
1031                          Result := Result | a_value | accumulator
1032                          state := normal_state
1033                      else
1034                          i := backtrack_i
1035                          state := always_print_state
1036                      end
1037                  end
1038                  i := i + 1
1039              end
1040
1041              check
1042                  i >= 0
1043              end
1044              if state > normal_state then
1045                  i := backtrack_i + 1
1046                  state := always_print_state
1047              end
1048        end
1049    ensure
1050        definition: has_substring("#("+an_index.out+")") implies Result.has_substring(a_value) and not Result.has_substring("#("+an_index.out+")")
1051        substitution_not_made: not has_substring("#("+an_index.out+")") implies Current.is_equal(Result)
1052    end
1053
1054    infix "#" (a_value: ABSTRACT_STRING): ABSTRACT_STRING
1055         -- A copy of Current with a placeholder "#(n)" is replaced with the content of `a_value'. A chain of # queries will progressively replace placeholder 1, 2 ...
1056         --
1057         -- For example a_string#"foo"#"bar"#"maman" is equivalent to a_string.arg(1,"foo").arg(2,"bar").arg(3,"maman")
1058         --
1059         -- See also `arg'.
1060      do
1061         create {PARTIALLY_FILLED_STRING} Result.from_string_and_arg(Current, a_value)
1062      end
1063
1064feature {ANY} -- Case conversion
1065   as_lower: STRING
1066         -- New object with all letters in lower case.
1067         --
1068         -- See also `as_upper', `to_lower', `to_upper'.
1069      do
1070         create Result.make_from_string(Current)
1071         Result.to_lower
1072      end
1073
1074   as_upper: STRING
1075         -- New object with all letters in upper case.
1076         --
1077         -- See also `as_lower', `to_upper', `to_lower'.
1078      do
1079         create Result.make_from_string(Current)
1080         Result.to_upper
1081      end
1082
1083feature {ANY} -- Printing:
1084   out_in_tagged_out_memory
1085      do
1086         tagged_out_memory.append(Current)
1087      end
1088
1089   fill_tagged_out_memory
1090      deferred
1091      end
1092
1093feature {ANY} -- String replacing
1094   replacing (an_old, a_new: ABSTRACT_STRING): STRING
1095         -- Current with all occurrences of `an_old' string replaced with `a_new'.
1096      require
1097         not an_old.is_empty
1098         a_new /= Void
1099      do
1100         create Result.with_capacity(count)
1101         replacing_in(an_old, a_new, Result)
1102      ensure
1103         Result /= Current
1104         not Result.valid_index(Result.first_substring_index(an_old))
1105         Result.first_substring_index(a_new) = first_substring_index(an_old) --| **** TODO to be improved
1106      end
1107
1108   replacing_in (an_old, a_new: ABSTRACT_STRING; buffer: STRING)
1109         -- Current with all occurrences of `an_old' string replaced with `a_new' in `buffer'.
1110      require
1111         not an_old.is_empty
1112         a_new /= Void
1113         buffer /= Current
1114      local
1115         cut_from, oldsize, i: INTEGER
1116      do
1117         i := first_substring_index(an_old)
1118         oldsize := an_old.count
1119         from
1120            cut_from := lower
1121         until
1122            not valid_index(i)
1123         loop
1124            buffer.append_substring(Current, cut_from, i - 1)
1125            buffer.append(a_new)
1126            cut_from := i + oldsize
1127            i := substring_index(an_old, i + oldsize)
1128         end
1129         buffer.append_substring(Current, cut_from, upper)
1130      ensure
1131         not buffer.valid_index(buffer.substring_index(an_old, old (buffer.upper + buffer.lower)))
1132         buffer.substring_index(a_new, old (buffer.upper + buffer.lower)) = old buffer.upper + first_substring_index(an_old) --| **** TODO to be improved
1133      end
1134
1135feature {ANY} -- Other features:
1136   first: CHARACTER
1137         -- Access to the very `first' character.
1138         --
1139         -- See also `last', `item'.
1140      deferred
1141      end
1142
1143   last: CHARACTER
1144         -- Access to the very `last' character.
1145         --
1146         -- See also `first', `item'.
1147      deferred
1148      end
1149
1150   substring (start_index, end_index: INTEGER): like Current
1151         -- New string consisting of items [`start_index'.. `end_index'].
1152         --
1153         -- See also `substring_index' and `copy_substring' to save memory.
1154      require
1155         valid_start_index: lower <= start_index
1156         valid_end_index: end_index <= upper
1157         meaningful_interval: start_index <= end_index + 1
1158      deferred
1159      ensure
1160         substring_count: Result.count = end_index - start_index + 1
1161      end
1162
1163   substring_index (other: ABSTRACT_STRING; start_index: INTEGER): INTEGER
1164         -- Position of first occurrence of `other' at or after `start_index'.
1165         -- If there is no occurrence Result will be an invalid index, usually 0 when lower is 1.
1166         --
1167         -- See also `substring', `first_substring_index'.
1168      require
1169         other_not_void: other /= Void
1170         valid_start_index: start_index >= lower and start_index <= upper + 1
1171      local
1172         i, s: INTEGER
1173      do
1174         from
1175            s := start_index
1176         until
1177            Result /= 0 or else s - lower + other.upper > upper
1178         loop
1179            from
1180               i := other.lower
1181            until
1182               i > other.upper or else item(s + i - other.lower) /= other.item(i)
1183            loop
1184               i := i + 1
1185            end
1186            if i > other.upper then
1187               Result := s
1188            else
1189               s := s + 1
1190            end
1191         end
1192      end
1193
1194   first_substring_index (other: ABSTRACT_STRING): INTEGER
1195         -- Position of first occurrence of `other' at or after 1, 0 if none.
1196         --
1197         -- See also `substring_index'.
1198      require
1199         other_not_void: other /= Void
1200      do
1201         Result := substring_index(other, lower)
1202      ensure
1203         definition: Result = substring_index(other, lower)
1204      end
1205
1206feature {ANY} -- Splitting a STRING:
1207   split: ARRAY[STRING]
1208         -- Split the string into an array of words. Uses `is_separator' of
1209         -- CHARACTER to find words. Gives Void or a non empty array.
1210         --
1211         -- See also `split_in'.
1212      do
1213         if count > 0 then
1214            split_buffer.clear_count
1215            split_in(split_buffer)
1216            if not split_buffer.is_empty then
1217               Result := split_buffer.twin
1218            end
1219         end
1220      ensure
1221         Result /= Void implies not Result.is_empty
1222      end
1223
1224   split_in (words: COLLECTION[STRING])
1225         -- Same jobs as `split' but result is appended in `words'.
1226         --
1227         -- See also `split'.
1228      require
1229         words /= Void
1230      local
1231         state, i: INTEGER; c: CHARACTER
1232      do
1233         -- state = 0: waiting next word.
1234         -- state = 1: inside a new word.
1235         if count > 0 then
1236            from
1237               i := lower
1238            until
1239               i > count
1240            loop
1241               c := item(i)
1242               if state = 0 then
1243                  if not c.is_separator then
1244                     string_buffer.clear_count
1245                     string_buffer.add_last(c)
1246                     state := 1
1247                  end
1248               else
1249                  if not c.is_separator then
1250                     string_buffer.add_last(c)
1251                  else
1252                     words.add_last(string_buffer.twin)
1253                     state := 0
1254                  end
1255               end
1256               i := i + 1
1257            end
1258            if state = 1 then
1259               words.add_last(string_buffer.twin)
1260            end
1261         end
1262      ensure
1263         words.count >= old words.count
1264      end
1265
1266feature {ANY} -- Other features:
1267   new_iterator: ITERATOR[CHARACTER]
1268      do
1269         create {ITERATOR_ON_STRING} Result.make(Current)
1270      end
1271
1272   intern: FIXED_STRING
1273         -- A shared version of this string.
1274      deferred
1275      ensure
1276         Result /= Void
1277         Result.is_equal(Current)
1278         Result.is_interned
1279         interned.fast_has(Result.hash_code) and then interned.fast_reference_at(Result.hash_code).fast_has(Result)
1280      end
1281
1282feature {ANY} -- Interfacing with C string:
1283   to_external: POINTER
1284             -- The address of a memory region containing the text of Current, useful to interact with the C language.
1285
1286                 -- A NATIVELY_STORED_STRING implementation usually gives direct access
1287                 -- to its internal `storage' (may be dangerous).
1288                 -- Other non-natively stored heirs provide access to an Eiffel-owned
1289                 -- buffer containing a  copy of its content.
1290
1291         -- To be compatible with C, a null character is added at the end
1292         -- of the internal `storage'. This extra null character is not
1293         -- part of the Eiffel STRING.
1294      deferred
1295      ensure
1296         -- TODO: generalize this postcondition
1297         -- (is_empty or else storage.item(count) /= '%U') implies (capacity > count and then storage.item(count) = '%U')
1298         count = old count
1299         Result.is_not_null
1300      end
1301
1302feature {ANY} -- Other features here for ELKS compatibility:
1303   same_string (other: ABSTRACT_STRING): BOOLEAN
1304         -- (Here for ELKS compatibility.)
1305         -- Do `Current' and `other' have the same character sequence?
1306         -- Useful in proper descendants of STRING.
1307      require
1308         other_not_void: other /= Void
1309      do
1310         Result := string.is_equal(other.string)
1311      end
1312
1313   string: STRING
1314         -- (Here for ELKS compatibility.)
1315         -- New STRING having the same character sequence as `Current'.
1316         -- Useful in proper descendants of STRING.
1317      do
1318         create Result.make_from_string(Current)
1319      end
1320
1321feature {}
1322   string_buffer: STRING
1323         -- Private, temporary once buffer.
1324      once
1325         create Result.make(256)
1326      end
1327
1328   split_buffer: ARRAY[STRING]
1329      once
1330         create Result.with_capacity(4, 1)
1331      end
1332
1333   computed_hash_code: INTEGER
1334      local
1335         i: INTEGER
1336      do
1337         from
1338            i := lower
1339         until
1340            i > upper
1341         loop
1342            Result := {INTEGER 5} #* Result #+ item(i).code
1343            i := i + 1
1344         end
1345         if Result < 0 then
1346            Result := ~Result
1347         end
1348      end
1349
1350   interned: HASHED_DICTIONARY[FAST_ARRAY[FIXED_STRING], INTEGER]
1351         -- Key: `hash_code'
1352         -- Item: interned string
1353      once
1354         create Result.make
1355      end
1356
1357feature {STRING_HANDLER}
1358   copy_slice_to_native (start_index, end_index: INTEGER; target: NATIVE_ARRAY[CHARACTER]; target_offset: INTEGER)
1359      require
1360         start_index >= lower
1361         end_index <= upper
1362         start_index <= end_index
1363      local
1364         i, j: INTEGER
1365      do
1366         from
1367            i := start_index
1368            j := 0
1369         until
1370            i > end_index
1371         loop
1372            target.put(item(i), target_offset + j)
1373            i := i + 1
1374            j := j + 1
1375         end
1376      end
1377
1378feature {} -- The states of the finite state automaton used in `arg' feature
1379   always_print_state: INTEGER -1
1380   normal_state: INTEGER 0
1381   after_delimiter_state: INTEGER 1
1382   after_brace_state: INTEGER 2
1383         -- Please note that we picked the same values used in MESSAGE_FORMATTER. It may also be written like
1384
1385         -- "always_print_state, normal_state, after_delimiter_state, after_brace_state: INTEGER is unique"
1386
1387         -- but I'm not sure that the compiler will actually choose sequential
1388         -- values necessary in the last if tense in the arg query
1389
1390
1391
1392feature {}
1393   debug_string: STRING
1394         -- only used to display the content of the FIXED_STRING in the trace stack
1395
1396invariant
1397   0 <= count
1398   lower = 1
1399
1400end -- class ABSTRACT_STRING
1401--
1402-- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file.
1403--
1404-- Permission is hereby granted, free of charge, to any person obtaining a copy
1405-- of this software and associated documentation files (the "Software"), to deal
1406-- in the Software without restriction, including without limitation the rights
1407-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1408-- copies of the Software, and to permit persons to whom the Software is
1409-- furnished to do so, subject to the following conditions:
1410--
1411-- The above copyright notice and this permission notice shall be included in
1412-- all copies or substantial portions of the Software.
1413--
1414-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1415-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1416-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1417-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1418-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1419-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1420-- THE SOFTWARE.