ST_STRING
Colin Paul Adams <[email protected]> Sat, 15 Nov 2008 22:05:45 +0000
| Newsgroups | gmane.comp.lang.eiffel.gobo.devel |
|---|---|
| Message-ID | <[email protected]> |
indexing
description:
"[
Fast UTF-32 read-only strings.
The emphasis is on cheap substring operations.
Therefore `area' does not include an extra item for NULL termination (for C-strings)
nor an initial extra item to avoid the cost of index translation.
]"
library: "Gobo Eiffel String Library"
copyright: "Copyright (c) 2008, Colin Adams and others"
license: "MIT License"
date: "$Date: $"
revision: "$Revision: $"
class ST_STRING
inherit
IMMUTABLE_STRING_GENERAL
redefine
is_equal
end
HASHABLE
redefine
is_equal
end
TO_SPECIAL [NATURAL_32]
rename
item as special_item,
infix "@" as special_item_alias
export
{NONE} all
{ST_STRING} area
redefine
is_equal, valid_index
end
ASCII
export
{NONE} all
redefine
is_equal
end
UC_IMPORTED_UNICODE_ROUTINES
export
{NONE} all
redefine
is_equal
end
ST_STRING_ROUTINES
export
{NONE} all
redefine
is_equal
end
ST_SHARED_STRINGS
export
{NONE} all
redefine
is_equal
end
ST_IMPORTED_UNICODE_FULL_CASE_MAPPING
export
{NONE} all
redefine
is_equal
end
ST_IMPORTED_UNICODE_NORMALIZATION_ROUTINES
export
{NONE} all
redefine
is_equal
end
create
make, make_from_string, make_shared, make_from_codepoints,
make_from_codes_list, make_empty, make_filled
convert
to_string: {STRING_32}, to_utf8_string: {UC_UTF8_STRING, STRING_8}
feature {NONE} -- Initialization
make (a_size: INTEGER) is
-- Initialize to `a_size' NULLs.
require
a_size_non_negative: a_size >= 0
do
make_area (a_size)
if a_size > 0 then
lower := 1
end
ensure
correct_count: count = a_size
lower_is_zero_for_empty_string: a_size = 0 implies lower = 0
lower_is_one: a_size > 0 implies lower = 1
end
make_empty is
-- Initialize as empty string.
do
make (0)
ensure
empty: is_empty
end
make_filled (a_code: NATURAL_32; a_size: INTEGER) is
-- Initialize with `a_size' characters all of codepoint `a_code'.
require
a_code_valid: valid_code (a_code)
a_size_non_negative: a_size >= 0
local
i: INTEGER
do
make_area (a_size)
if a_size > 0 then
lower := 1
end
from
i := 1
upper := a_size
until
i > upper
loop
area.put (a_code, i - 1)
i := i + 1
end
ensure
correct_count: count = a_size
all_characters_correct: -- not yet supported by ISE: for_all (agent a_code.is_equal)
end
make_from_string (a_string: READABLE_STRING_GENERAL) is
-- Initialize to same characters as `a_string'.
require
a_string_not_void: a_string /= Void
local
i: INTEGER
do
make_area (a_string.count)
if a_string.count > 0 then
lower := 1
end
from
i := 1
upper := a_string.count
until
i > upper
loop
area.put (a_string.code (i), i - 1)
i := i + 1
end
ensure
correct_count: count = a_string.count
correct_character_sequence: for_all_with_index (agent same_item_as_string (a_string, ?, ?))
end
make_from_codepoints (a_array: ARRAY [NATURAL_32]) is
-- Initialize `Current' by copying codepoints from `a_array'.
require
a_array_not_void: a_array /= Void
a_array_all_valid: a_array.for_all (agent valid_code)
local
i: INTEGER
do
make_area (a_array.count)
if a_array.count > 0 then
lower := 1
end
from
i := 1
upper := a_array.count
until
i > upper
loop
area.put (a_array.item (i), i - 1)
i := i + 1
end
ensure
correct_count: count = a_array.count
correct_character_sequence: for_all_with_index (agent same_item_as_array (a_array, ?, ?))
end
make_from_codes_list (a_array: DS_ARRAYED_LIST [INTEGER_32]) is
-- Initialize `Current' by copying codepoints from `a_array'.
require
a_array_not_void: a_array /= Void
local
i: INTEGER
do
make_area (a_array.count)
if a_array.count > 0 then
lower := 1
end
from
i := 1
upper := a_array.count
until
i > upper
loop
area.put (a_array.item (i).as_natural_32, i - 1)
i := i + 1
end
ensure
correct_count: count = a_array.count
end
make_shared (a_area: like area; a_start, a_end: INTEGER) is
-- Initializing sharing `a_area' from `a_start' to `a_end' inclusive.
require
a_area_not_void: a_area /= Void
a_start_large_enough: a_start > 0
a_end_large_enough: a_end > 0
a_end_not_smaller_than_a_start: a_end >= a_start
do
area := a_area
lower := a_start
upper := a_end
ensure
area_set: area = a_area
lower_set: lower = a_start
upper_set: upper = a_end
end
feature -- Access
lower: INTEGER
-- Index of first character
upper: INTEGER
-- Index of last character
code (a_index: INTEGER): NATURAL_32 is
-- Unicode codepoint at `a_index'
do
Result := area.item (a_index + lower - 2)
ensure then
valid_code: valid_code (Result)
end
item, infix "@" (a_index: INTEGER): CHARACTER_32 is
-- Unicode character at `a_index'
require
a_index_valid: valid_index (a_index)
do
Result := code (a_index).to_character_32
ensure
valid_character: unicode.valid_non_surrogate_code (Result.code)
end
hash_code: INTEGER is
-- Hash code value
local
i: INTEGER
l_area: like area
do
Result := internal_hash_code
if Result = 0 then
if not is_empty then
-- The magic number `8388593' below is the greatest prime lower than
-- 2^23 so that this magic number shifted to the left does not exceed 2^31.
from
i := lower - 1
l_area := area
until
i = upper
loop
Result := ((Result \\ 8388593) |<< 8) + l_area.item (i).to_integer_32
i := i + 1
end
internal_hash_code := Result
end
end
end
maximal_split (a_separators: READABLE_STRING_GENERAL): DS_LIST [ST_STRING] is
-- `Current' tokenized by `a_separators';
-- Each character in `Current' matching any in `a_separators' is discarded,
-- and a (possibly empty) token is extracted.
-- E.g. "//my/shares/top\directory/in/path/".maximal_split ("/\") yields:
-- ("","","my","shares","top","directory","in","path","").
require
a_separators_not_void: a_separators /= Void
do
Result := internal_split (a_separators, True)
ensure
maximal_split_not_void: Result /= Void
no_void_item: not Result.has (Void)
end
minimal_split (a_separators: READABLE_STRING_GENERAL): DS_LIST [ST_STRING] is
-- `Current' tokenized by `a_separators';
-- Each character in `Current' matching any in `a_separators' is discarded,
-- and a token is extracted. Consecutive separator characters do not yield
-- additional tokens.
-- E.g. "//my/shares/top\directory/in/path/".maximal_split ("/\") yields:
-- ("","my","shares","top","directory","in","path","").
require
a_separators_not_void: a_separators /= Void
do
Result := internal_split (a_separators, False)
ensure
minimal_split_not_void: Result /= Void
no_void_item: not Result.has (Void)
end
trimmed: ST_STRING is
-- Version of `Current' without leading or trailing XML white space characters (SPACE, TAB, LF and CR)
do
Result := stripped_of (" %T%N%R", True, True)
ensure
trimmed_not_void: Result /= Void
not_longer: Result.count <= count
no_leading_white_space: not Result.is_empty implies not (" %T%N%R").has_code (Result.code (1))
no_trailing_forbidden_character: not Result.is_empty implies not (" %T%N%R").has_code (Result.code (Result.count))
end
stripped_of (a_forbidden: READABLE_STRING_GENERAL; a_leading_stripped, a_trailing_stripped: BOOLEAN): ST_STRING is
-- Version of `Current' without any leading/trailing characters from `a_forbidden';
require
a_forbidden_not_void: a_forbidden /= Void
local
l_leading_index, l_trailing_index: INTEGER
do
l_leading_index := 1
l_trailing_index := count
if a_leading_stripped then
from
until
not valid_index (l_leading_index) or else not a_forbidden.has_code (code (l_leading_index))
loop
l_leading_index := l_leading_index + 1
end
end
if a_trailing_stripped then
from
until
(not valid_index (l_trailing_index) or l_trailing_index = l_leading_index) or else (not a_forbidden.has_code (code (l_trailing_index)))
loop
l_trailing_index := l_trailing_index - 1
end
end
Result := substring (l_leading_index, l_trailing_index)
ensure
stripped_of_not_void: Result /= Void
not_longer: Result.count <= count
no_leading_forbidden_character: (a_leading_stripped and not Result.is_empty) implies not a_forbidden.has_code (Result.code (1))
no_trailing_forbidden_character: (a_trailing_stripped and not Result.is_empty) implies not a_forbidden.has_code (Result.code (Result.count))
end
substring_index (a_other: ST_STRING; a_start_index: INTEGER): INTEGER is
-- Index of first occurrence of `a_other' at or after `a_start_index';
-- 0 if none
require
a_other_not_void: a_other /= Void
valid_a_start_index: a_start_index >= 1 and a_start_index <= count + 1
do
Result := string_searcher.substring_index (Current, a_other, a_start_index, count)
ensure
valid_result: Result = 0 or else
(a_start_index <= Result and Result <= count - a_other.count + 1)
zero_if_absent: (Result = 0) =
not substring (a_start_index, count).has_substring (a_other)
at_this_index: Result >= a_start_index implies
a_other.same_string (substring (Result, Result + a_other.count - 1))
none_before: Result > a_start_index implies
not substring (a_start_index, Result + a_other.count - 2).has_substring (a_other)
end
feature -- Measurement
count: INTEGER
-- Number of characters in `Current'
do
if upper = 0 then
Result := 0
else
Result := upper - lower + 1
end
end
capacity: INTEGER is
-- Number of characters allocated in Current
do
Result := count
end
occurrences (c: NATURAL_32): INTEGER is
-- Number of times `c' appears in the string
local
i, l_count: INTEGER
a: SPECIAL [NATURAL_32]
do
from
l_count := count
a := area
until
i = l_count
loop
if a.item (i) = c then
Result := Result + 1
end
i := i + 1
end
ensure then
zero_if_empty: count = 0 implies Result = 0
recurse_if_not_found_at_first_position:
(count > 0 and then code (1) /= c) implies
Result = substring (2, count).occurrences (c)
recurse_if_found_at_first_position:
(count > 0 and then code (1) = c) implies
Result = 1 + substring (2, count).occurrences (c)
end
feature -- Comparison
infix "<" (a_other: like Current): BOOLEAN is
-- Is current object less than `other'?
local
l_count, l_other_count: INTEGER
do
if a_other /= Current then
l_count := count
l_other_count := a_other.count
if l_count < l_other_count then
Result := (leading_string_comparison (a_other, l_count) >= 0)
else
Result := (leading_string_comparison (a_other, l_other_count) > 0)
end
end
end
is_equal (a_other: like Current): BOOLEAN is
-- Is `a_other' attached to an object of the same type
-- as current object and identical to it?
do
Result := same_string (a_other)
end
same_case_insensitive (a_other: READABLE_STRING_GENERAL): BOOLEAN is
-- Does `Current' represent the same character sequence (ignoring_case) as `a_other'?
-- Full case folding is not yet implemented (TODO).
-- Needs a more efficient implementation (TODO).
-- Normalization is not considered. To check that two strings are
-- equal under a given normalization form, compare normalized
-- copies of the two strings.
require
a_other_not_void: a_other /= Void
local
l_string: ST_STRING
do
if a_other = Current then
Result := True
elseif a_other.count = count then
l_string ?= a_other
if l_string = Void then
create l_string.make_from_string (a_other)
end
Result := to_lower.same_string (l_string.to_lower)
end
end
feature -- Status report
valid_index (i: INTEGER): BOOLEAN is
-- Is `i' within the bounds of the string?
do
Result := i > 0 and i <= count
end
valid_code (a_code: like code): BOOLEAN is
-- Is `a_code' a valid Unicode codepoint?
do
Result := unicode.valid_non_surrogate_code (a_code.as_integer_32)
end
is_natural, is_natural_32: BOOLEAN is
-- Does `Current' represent a 32-bit unsigned integer?
do
Result := is_valid_integer_or_natural ({NUMERIC_INFORMATION}.type_natural_32)
end
is_integer, is_integer_32: BOOLEAN is
-- Does `Current' represent a 32-bit signed integer?
do
Result := is_valid_integer_or_natural ({NUMERIC_INFORMATION}.type_integer_32)
end
is_integer_64: BOOLEAN is
-- Does `Current' represent a 64-bit signed_integer?
do
Result := is_valid_integer_or_natural ({NUMERIC_INFORMATION}.type_integer_64)
end
is_double: BOOLEAN is
-- Does `Current' represent a DOUBLE?
local
l_convertor: like ctor_convertor
do
if is_valid_as_string_8 then
l_convertor := ctor_convertor
l_convertor.parse_string_with_type (Current, {NUMERIC_INFORMATION}.type_double)
Result := l_convertor.is_integral_double
end
ensure
syntax_and_range:
-- 'Result' is True if and only if the following two
-- conditions are satisfied:
--
-- 1. In the following BNF grammar, the value of
-- 'Current' can be produced by "Real_literal":
--
-- Real_literal = Mantissa [Exponent_part]
-- Exponent_part = "E" Exponent
-- | "e" Exponent
-- Exponent = Integer_literal
-- Mantissa = Decimal_literal
-- Decimal_literal = Integer_literal ["." [Integer]] | "." Integer
-- Integer_literal = [Sign] Integer
-- Sign = "+" | "-"
-- Integer = Digit | Digit Integer
-- Digit = "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
--
-- 2. The numerical value represented by 'Current'
-- is within the range that can be represented
-- by an instance of type DOUBLE.
end
is_real: BOOLEAN is
-- Does `Current' represent a REAL?
do
Result := is_double
ensure
syntax_and_range:
-- 'Result' is True if and only if the following two
-- conditions are satisfied:
--
-- 1. In the following BNF grammar, the value of
-- 'Current' can be produced by "Real_literal":
--
-- Real_literal = Mantissa [Exponent_part]
-- Exponent_part = "E" Exponent
-- | "e" Exponent
-- Exponent = Integer_literal
-- Mantissa = Decimal_literal
-- Decimal_literal = Integer_literal ["." [Integer]] | "." Integer
-- Integer_literal = [Sign] Integer
-- Sign = "+" | "-"
-- Integer = Digit | Digit Integer
-- Digit = "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
--
-- 2. The numerical value represented by 'Current'
-- is within the range that can be represented
-- by an instance of type REAL.
end
is_empty: BOOLEAN is
-- Is structure empty?
do
Result := (count = 0)
end
is_string_8: BOOLEAN is
-- Is `Current' a STRING_8?
do
end
is_string_32: BOOLEAN is
-- Is `Current' a STRING_32?
do
end
is_valid_as_string_8: BOOLEAN is
-- Is `Current' convertible to STRING_8 without information loss?
do
Result := for_all (agent is_character_8)
end
is_hexadecimal: BOOLEAN is
-- Is `Current' made up of characters 0-9 or A-F or a-f?
local
i, l_count: INTEGER
l_code: NATURAL_32
do
l_count := count
if l_count = 0 then
Result := False
else
Result := True
from i := 1 until i > l_count loop
l_code := code (i)
if (l_code < Zero.as_natural_32 or l_code > Nine.as_natural_32) and (l_code < Lower_a.as_natural_32 or l_code > Lower_f.as_natural_32) and
(l_code < Upper_a.as_natural_32 or l_code > Upper_f.as_natural_32)
then
Result := False
i := l_count + 1 -- Jump out of the loop.
else
i := i + 1
end
end
end
end
is_base64: BOOLEAN is
-- Is `Current' made up of characters +, /, =, XML whitespace, 0-9 or A-Z or a-z?
local
i, l_count: INTEGER
l_code: NATURAL_32
do
l_count := count
if l_count = 0 then
Result := False
else
Result := True
from i := 1 until i > l_count loop
l_code := code (i)
if (l_code < Zero.as_natural_32 or l_code > Nine.as_natural_32) and (l_code < Lower_a.as_natural_32 or l_code > Lower_z.as_natural_32) and
(l_code < Upper_a.as_natural_32 or l_code > Upper_z.as_natural_32) and
l_code /= Plus.as_natural_32 and l_code /= Slash.as_natural_32 and
l_code /= Equal_sign.as_natural_32 and l_code /= Blank.as_natural_32 and
l_code /= Tabulation.as_natural_32 and l_code /= Carriage_return.as_natural_32 and l_code /= Line_feed.as_natural_32
then
Result := False
i := l_count + 1 -- Jump out of the loop.
else
i := i + 1
end
end
end
end
has_substring (a_other: ST_STRING): BOOLEAN is
-- Does `Current' contain `a_other'?
require
a_other_not_void: a_other /= Void
do
if a_other = Current then
Result := True
elseif a_other.count <= count then
Result := substring_index (a_other, 1) > 0
end
ensure
false_if_too_small: count < a_other.count implies not Result
true_if_initial: (count >= a_other.count and then
a_other.same_string (substring (1, a_other.count))) implies Result
recurse: (count >= a_other.count and then
not a_other.same_string (substring (1, a_other.count))) implies
(Result = substring (2, count).has_substring (a_other))
end
is_nfd: BOOLEAN is
-- Is `Current' in NFD normal form?
do
Result := normalization.is_nfd (Current)
end
is_nfkd: BOOLEAN is
-- Is `Current' in NFKD normal form?
do
Result := normalization.is_nfkd (Current)
end
is_nfc: BOOLEAN is
-- Is `Current' in NFC normal form?
do
Result := normalization.is_nfc (Current)
end
is_nfkc: BOOLEAN is
-- Is `Current' in NFKC normal form?
do
Result := normalization.is_nfkc (Current)
end
feature -- New strings
concatenated_before (a_other: READABLE_STRING_GENERAL): ST_STRING is
-- New string formed by concatenating `Current' before `a_other'
require
a_other_not_void: a_other /= Void
local
l_builder: ST_STRING_BUILDER
do
create l_builder.make (count + a_other.count)
l_builder.append_strings (<<Current, a_other>>)
l_builder.set_built
Result := l_builder.new_string
ensure
concatenated_with_not_void: Result /= Void
correct_count: Result.count = count + a_other.count
correct_prefix: Result.substring (1, count).same_string (Current)
correct_suffix: Result.substring (1 + count, Result.count).same_string (a_other)
end
concatenated_after (a_other: READABLE_STRING_GENERAL): ST_STRING is
-- New string formed by concatenating `Current' after `a_other'
require
a_other_not_void: a_other /= Void
local
l_builder: ST_STRING_BUILDER
do
create l_builder.make (count + a_other.count)
l_builder.append_strings (<<a_other, Current>>)
l_builder.set_built
Result := l_builder.new_string
ensure
concatenated_with_not_void: Result /= Void
correct_count: Result.count = count + a_other.count
correct_suffix: Result.substring (1 + a_other.count, Result.count).same_string (Current)
correct_prefix: Result.substring (1, a_other.count).same_string (a_other)
end
concatenated_before_strings (a_strings: ARRAY [READABLE_STRING_GENERAL]): ST_STRING is
-- New string formed by concatenating `Current' before all of `a_strings'.
require
a_strings_not_void: a_strings /= Void
all_strings_not_void: not a_strings.has (Void)
local
l_builder: ST_STRING_BUILDER
do
create l_builder.make (count + string_sums (a_strings))
l_builder.append_string (Current)
l_builder.append_strings (a_strings)
l_builder.set_built
Result := l_builder.new_string
ensure
concatenated_before_strings_not_void: Result /= Void
correct_count: Result.count = count + string_sums (a_strings)
correct_prefix: Result.substring (1, count).same_string (Current)
correct_suffix: True -- TODO
end
concatenated_before_code (a_code: NATURAL_32): ST_STRING is
-- New string formed by concatenating `Current' before `a_code'
local
l_builder: ST_STRING_BUILDER
do
create l_builder.make (count + 1)
l_builder.append_string (Current)
l_builder.append_code (a_code)
l_builder.set_built
Result := l_builder.new_string
ensure
concatenated_with_not_void: Result /= Void
correct_count: Result.count = count + 1
correct_prefix: Result.substring (1, count).same_string (Current)
correct_suffix: Result.code (Result.count) = a_code
end
feature -- Iteration
for_all (a_test: PREDICATE [ANY, TUPLE [NATURAL_32]]): BOOLEAN is
-- Is `a_test' true for all codepoints?
require
a_test_not_void: a_test /= Void
local
l_tuple: TUPLE [NATURAL_32]
i: INTEGER
do
Result := True
if count > 0 then
from
create l_tuple
i := lower
until
i > upper or not Result
loop
l_tuple.put (area.item (i - 1), 1)
Result := a_test.item (l_tuple)
i := i + 1
end
end
end
for_all_with_index (a_test: PREDICATE [ANY, TUPLE [NATURAL_32, INTEGER]]): BOOLEAN is
-- Is `a_test' true for all codepoints?
require
a_test_not_void: a_test /= Void
local
l_tuple: TUPLE [NATURAL_32, INTEGER]
i: INTEGER
do
Result := True
if count > 0 then
from
create l_tuple
i := lower
until
i > upper or not Result
loop
l_tuple.put (area.item (i - 1), 1)
l_tuple.put (i - lower + 1, 2)
Result := a_test.item (l_tuple)
i := i + 1
end
end
end
do_all, do_forward (a_action: PROCEDURE [ANY, TUPLE [NATURAL_32]]) is
-- Apply `a_action' to all codepoints.
-- `a_action' must be precondition-free.
require
a_action_not_void: a_action /= Void
local
l_tuple: TUPLE [NATURAL_32]
i: INTEGER
do
if count > 0 then
from
create l_tuple
i := lower
until
i > upper
loop
l_tuple.put (area.item (i - 1), 1)
a_action.call (l_tuple)
i := i + 1
end
end
end
do_all_with_index, do_forward_with_index (a_action: PROCEDURE [ANY, TUPLE [NATURAL_32, INTEGER]]) is
-- Apply `a_action' to all codepoints, passing index relative to `lower'.
-- `a_action' must be precondition-free.
require
a_action_not_void: a_action /= Void
local
l_tuple: TUPLE [NATURAL_32, INTEGER]
i: INTEGER
do
if count > 0 then
from
create l_tuple
i := lower
until
i > upper
loop
l_tuple.put (area.item (i - 1), 1)
l_tuple.put (i - lower + 1, 2)
a_action.call (l_tuple)
i := i + 1
end
end
end
feature -- Conversion
to_string: STRING_32 is
-- `Current' converted to STRING_32
do
create Result.make_filled (('%/0/').to_character_32, count)
do_all_with_index (agent Result.put_code)
ensure
to_string_not_void: Result /= Void
correct_count: count = Result.count
correct_character_sequence: for_all_with_index (agent same_item_as_string (Result, ?, ?))
end
to_latin_1_string: STRING_8 is
-- `Current' converted to ISO-8859-1
require
latin_1_string: is_valid_as_string_8
do
create Result.make_filled ('%/0/', count)
do_all_with_index (agent Result.put_code)
end
to_utf8_string: UC_UTF8_STRING is
-- `Current' converted to UTF-8
do
create Result.make_from_string_general (Current)
ensure
to_utf_8_not_void: Result /= Void
correct_count: count = Result.count
correct_character_sequence: for_all_with_index (agent same_item_as_string (Result, ?, ?))
end
to_natural, to_natural_32: NATURAL_32 is
-- 32-bit natural value
require
is_natural: is_natural_32
local
l_convertor: like ctoi_convertor
do
l_convertor := ctoi_convertor
l_convertor.parse_string_with_type (Current, {NUMERIC_INFORMATION}.type_no_limitation)
Result := l_convertor.parsed_natural_32
end
to_integer, to_integer_32: INTEGER_32 is
-- 32-bit signed integer value
require
is_integer: is_integer_32
local
l_convertor: like ctoi_convertor
do
l_convertor := ctoi_convertor
l_convertor.parse_string_with_type (Current, {NUMERIC_INFORMATION}.type_no_limitation)
Result := l_convertor.parsed_integer
end
to_integer_64: INTEGER_64 is
-- 64-bit integer value
require
is_integer_64: is_integer_64
local
l_convertor: like ctoi_convertor
do
l_convertor := ctoi_convertor
l_convertor.parse_string_with_type (Current, {NUMERIC_INFORMATION}.type_no_limitation)
Result := l_convertor.parsed_integer_64
end
to_double: DOUBLE is
-- "Double" value;
-- for example, when applied to "123.0", will yield 123.0 (double)
require
represents_a_double: is_double
local
l_convertor: like ctor_convertor
do
l_convertor := ctor_convertor
l_convertor.parse_string_with_type (Current, {NUMERIC_INFORMATION}.type_no_limitation)
Result := l_convertor.parsed_double
end
to_real: REAL is
-- Real value;
-- for example, when applied to "123.0", will yield 123.0
require
represents_a_real: is_real
do
Result := to_double
end
to_lower: ST_STRING is
-- New lower-case version of `Current'
do
Result := case_mapping.lower_string (Current)
ensure
to_lower_not_void: Result /= Void
end
to_upper: ST_STRING is
-- New upper-case version of `Current'
do
Result := case_mapping.upper_string (Current)
ensure
to_upper_not_void: Result /= Void
end
as_nfd: ST_STRING is
-- Canonical decomposition of `Current';
do
Result := normalization.as_nfd_st_string (Current)
ensure
as_nfd_not_void: Result /= Void
is_nfd: Result.is_nfd
end
to_nfd: ST_STRING is
-- Canonical decomposition of `Current';
do
Result := normalization.to_nfd_st_string (Current)
ensure
as_nfd_not_void: Result /= Void
is_nfd: Result.is_nfd
new_object: Result /= Current
end
as_nfkd: ST_STRING is
-- Compatibility decomposition of `Current';
do
Result := normalization.as_nfkd_st_string (Current)
ensure
as_nfkd_not_void: Result /= Void
is_nfkd: Result.is_nfkd
end
to_nfkd: ST_STRING is
-- Compatibility decomposition of `Current';
do
Result := normalization.to_nfkd_st_string (Current)
ensure
as_nfkd_not_void: Result /= Void
is_nfkd: Result.is_nfkd
new_object: Result /= Current
end
to_nfc: ST_STRING is
-- Canonical decomposition then canonical composition of `Current';
do
Result := normalization.to_nfc_st_string (Current)
ensure
as_nfc_not_void: Result /= Void
is_nfc: Result.is_nfc
new_object: Result /= Current
end
to_nfkc: ST_STRING is
-- Compatibility decomposition then canonical composition of `Current';
do
Result := normalization.to_nfkc_st_string (Current)
ensure
as_nfkc_not_void: Result /= Void
is_nfkc: Result.is_nfkc
new_object: Result /= Current
end
feature -- Duplication
substring (a_start, a_end: INTEGER): ST_STRING is
-- Shared-memory substring containing all characters at indices between `a_start' and `a_end';
-- Memory is not shared if `Result' is empty string.
do
if a_end < a_start then
Result := shared_empty_string
else
create Result.make_shared (area, a_start + lower - 1, a_end + lower - 1)
end
ensure then
empty_string: a_end < a_start implies Result.is_empty
lower: not Result.is_empty implies Result.lower = lower + a_start - 1
upper: not Result.is_empty implies Result.upper = a_end +lower - 1
same_area: not Result.is_empty implies Result.area = area
substring_not_void: Result /= Void
end
feature {NONE} -- Contract support
same_item_as_string (a_string: READABLE_STRING_GENERAL; a_code: NATURAL_32; a_index: INTEGER): BOOLEAN is
-- Does `a_code' equal `a_string.code (a_index)'?
require
a_string_not_void: a_string /= Void
a_index_valid: valid_index (a_index)
do
Result := a_code = a_string.code (a_index)
ensure
definition: Result = (a_code = a_string.code (a_index))
end
same_item_as_array (a_array: ARRAY [NATURAL_32]; a_code: NATURAL_32; a_index: INTEGER): BOOLEAN is
-- Does `a_code' equal `a_array.item (a_index)'?
require
a_array_not_void: a_array /= Void
a_index_valid: valid_index (a_index)
do
Result := a_code = a_array.item (a_index)
ensure
definition: Result = (a_code = a_array.item (a_index))
end
feature {NONE} -- Implementation
leading_string_comparison (a_other: READABLE_STRING_GENERAL; a_count: INTEGER): INTEGER is
-- Comparison of first `a_count' characters of `Current' with `a_other'
-- 0 if equal, < 0 if `Current' < `a_other',
-- Otherwise > 0
require
a_other_not_void: a_other /= Void
valid_count: a_count <= count and a_count <= a_other.count
local
i: INTEGER
l_code, l_other_code: NATURAL_32
do
from
i := 1
until
i > a_count
loop
l_code := code (i)
l_other_code := a_other.code (i)
if l_code /= l_other_code then
Result := (l_code.to_integer_32 - l_other_code.to_integer_32)
i := a_count -- Jump out of loop
end
i := i + 1
end
end
internal_split (a_separators: READABLE_STRING_GENERAL; a_maximal: BOOLEAN): DS_LINKED_LIST [ST_STRING] is
require
a_separators_not_void: a_separators /= Void
local
i, l_count, l_last: INTEGER
l_last_separator: INTEGER
l_code: NATURAL_32
l_item: ST_STRING
do
create Result.make
l_count := count
if l_count > 0 then
from
i := 1
l_last_separator := 0
l_last := 1
invariant
last_separator: l_last_separator < i
last_split: l_last <= i
until
i > l_count
loop
l_code := code (i)
if a_separators.has_code (l_code) then
if l_last_separator = i - 1 then
if a_maximal then
Result.force_last (shared_empty_string)
end
else
if l_item = Void then
l_item := substring (l_last, i - 1)
else
l_item := l_item.concatenated_before (substring (l_last, i - 1))
end
Result.force_last (l_item)
end
l_item := Void
l_last_separator := i
l_last := i + 1
end
i := i + 1
end
if l_last_separator = 0 then
if l_item = Void then
l_item := Current
else
l_item := l_item.concatenated_before (substring (l_last, l_count))
end
Result.force_last (l_item)
elseif l_last_separator < l_count then
if l_item = Void then
l_item := substring (l_last, l_count)
else
l_item := l_item.concatenated_before (substring (l_last, l_count))
end
Result.force_last (l_item)
elseif a_maximal then
Result.force_last (shared_empty_string)
end
end
ensure
iternal_split_not_void: Result /= Void
no_void_item: not Result.has (Void)
end
is_valid_integer_or_natural (type: INTEGER): BOOLEAN is
-- Is `Current' a valid number according to given `type'?
local
l_convertor: like ctoi_convertor
do
if is_valid_as_string_8 then
l_convertor := ctoi_convertor
l_convertor.reset (type)
l_convertor.parse_string_with_type (Current, type)
Result := l_convertor.is_integral_integer
end
end
feature {NONE} -- Agents
is_same_code (a_other: READABLE_STRING_GENERAL; a_code: NATURAL_32; a_index: INTEGER): BOOLEAN is
-- Does `a_other.code (a_index)' = `a_code'?
require
a_other_not_void: a_other /= Void
a_index_valid: valid_index (a_index)
do
Result := a_other.code (a_index) = a_code
ensure
definition: Result = (a_other.code (a_index) = a_code)
end
is_character_8 (a_code: NATURAL_32): BOOLEAN is
-- Is `a_code' in range for for CHARACTER_8?
do
Result := a_code < 256
ensure
definition: Result = (a_code < 256)
end
invariant
area_not_void: area /= Void
all_code_points_valid: for_all (agent valid_code)
count_is_not_greater_than_area_count: count <= area.count
lower_large_enough: lower >= 0
lower_small_enough: lower <= area.count + 1
upper_large_enough: upper >= 0
upper_not_larger_than_lower: upper >= lower
end
--
Colin Adams
Preston Lancashire
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/