note description: "Decimal number parsers, whose BNF syntax follows: %N sign ::= '+' | '-' %N digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' %N indicator ::= 'e' | 'E' %N digits ::= digit [digit]... %N point ::= '.' | ',' %N decimal-part ::= digits point [digits] | [point] digits %N exponent-part ::= indicator [sign] digits %N infinity ::= 'Infinity' | 'Inf' %N nan ::= 'NaN' | 'sNaN' %N numeric-value ::= decimal-part [exponent-part] | infinity %N numeric-string ::= [sign] numeric-value | nan%N" library: "Gobo Eiffel Decimal Arithmetic Library" copyright: "Copyright (c) 2004, Paul G. Crismer and others" license: "MIT License" date: "$Date: 2016-05-06 19:15:38 +0000 (Fri, 06 May 2016) $" revision: "$Revision: 98678 $" class MA_DECIMAL_TEXT_PARSER create make feature {NONE} -- Initialization default_create -- Process instances of classes with no creation clause. -- (Default: do nothing.) -- (from ANY) do end make -- Create a new decimal parser. -- (from MA_DECIMAL_PARSER) do end feature -- Access Character_: KL_CHARACTER_ROUTINES -- Routines that ought to be in class CHARACTER -- (from KL_IMPORTED_CHARACTER_ROUTINES) once create Result ensure -- from KL_IMPORTED_CHARACTER_ROUTINES instance_free: class character_routines_not_void: Result /= Void end coefficient_begin: INTEGER_32 -- Index of last parsed coefficient begin coefficient_count: INTEGER_32 -- Number of characters in coefficient part do Result := coefficient_end - coefficient_begin + 1 end coefficient_end: INTEGER_32 -- Index of last parsed coefficient end decimal_point_index: INTEGER_32 -- Index of decimal point if any error_code: INTEGER_32 -- Description of last error exponent_as_double: REAL_64 -- Exponent expressed as DOUBLE exponent_begin: INTEGER_32 -- Index of last parsed first exponent character exponent_count: INTEGER_32 -- Count of significant digits in exponent; -- Synonym of exponent_significant_digits do Result := exponent_significant_digits end exponent_end: INTEGER_32 -- Index of last parsed last exponent character exponent_sign: INTEGER_32 -- Sign of exponent of last parsed decimal exponent_significant_digits: INTEGER_32 -- Count of significant digits in exponent fractional_part_count: INTEGER_32 -- Number of characters in the fractional part do if decimal_point_index > 0 then Result := coefficient_end - decimal_point_index end end generating_type: TYPE [detachable MA_DECIMAL_TEXT_PARSER] -- Type of current object -- (type of which it is a direct instance) -- (from ANY) external "built_in" ensure -- from ANY generating_type_not_void: Result /= Void end generator: STRING_8 -- Name of current object's generating class -- (base class of the type of which it is a direct instance) -- (from ANY) external "built_in" ensure -- from ANY generator_not_void: Result /= Void generator_not_empty: not Result.is_empty end last_decimal: detachable MA_DECIMAL -- Last decimal parsed -- (from MA_DECIMAL_PARSER) last_parsed: detachable STRING_8 -- Last parsed string sign: INTEGER_32 -- Sign of last parsed decimal state: INTEGER_32 -- Last state of parsing finite state automaton String_: KL_STRING_ROUTINES -- Routines that ought to be in class STRING -- (from KL_IMPORTED_STRING_ROUTINES) once create Result ensure -- from KL_IMPORTED_STRING_ROUTINES instance_free: class string_routines_not_void: Result /= Void end feature {NONE} -- Access Default_context: MA_DECIMAL_CONTEXT -- Default context for general purpose arithmetic -- (from MA_SHARED_DECIMAL_CONTEXT) once create Result.make_default ensure -- from MA_SHARED_DECIMAL_CONTEXT instance_free: class default_context_not_void: Result /= Void end shared_decimal_context: MA_DECIMAL_CONTEXT -- Decimal context for operations where it does not explicitly appear in the signature; -- Return Default_context by default, but can be changed by calling set_shared_decimal_context -- (from MA_SHARED_DECIMAL_CONTEXT) do Result := Cell.item ensure -- from MA_SHARED_DECIMAL_CONTEXT instance_free: class shared_decimal_context_not_void: Result /= Void end feature -- Comparison frozen deep_equal (a: detachable ANY; b: like arg #1): BOOLEAN -- Are a and b either both void -- or attached to isomorphic object structures? -- (from ANY) do if a = Void then Result := b = Void else Result := b /= Void and then a.is_deep_equal (b) end ensure -- from ANY instance_free: class shallow_implies_deep: standard_equal (a, b) implies Result both_or_none_void: (a = Void) implies (Result = (b = Void)) same_type: (Result and (a /= Void)) implies (b /= Void and then a.same_type (b)) symmetric: Result implies deep_equal (b, a) end frozen equal (a: detachable ANY; b: like arg #1): BOOLEAN -- Are a and b either both void or attached -- to objects considered equal? -- (from ANY) do if a = Void then Result := b = Void else Result := b /= Void and then a.is_equal (b) end ensure -- from ANY instance_free: class definition: Result = (a = Void and b = Void) or else ((a /= Void and b /= Void) and then a.is_equal (b)) end frozen is_deep_equal (other: MA_DECIMAL_TEXT_PARSER): BOOLEAN -- Are Current and other attached to isomorphic object structures? -- (from ANY) require -- from ANY other_not_void: other /= Void external "built_in" ensure -- from ANY shallow_implies_deep: standard_is_equal (other) implies Result same_type: Result implies same_type (other) symmetric: Result implies other.is_deep_equal (Current) end is_equal (other: MA_DECIMAL_TEXT_PARSER): BOOLEAN -- Is other attached to an object considered -- equal to current object? -- (from ANY) require -- from ANY other_not_void: other /= Void external "built_in" ensure -- from ANY symmetric: Result implies other ~ Current consistent: standard_is_equal (other) implies Result end frozen standard_equal (a: detachable ANY; b: like arg #1): BOOLEAN -- Are a and b either both void or attached to -- field-by-field identical objects of the same type? -- Always uses default object comparison criterion. -- (from ANY) do if a = Void then Result := b = Void else Result := b /= Void and then a.standard_is_equal (b) end ensure -- from ANY instance_free: class definition: Result = (a = Void and b = Void) or else ((a /= Void and b /= Void) and then a.standard_is_equal (b)) end frozen standard_is_equal (other: MA_DECIMAL_TEXT_PARSER): BOOLEAN -- Is other attached to an object of the same type -- as current object, and field-by-field identical to it? -- (from ANY) require -- from ANY other_not_void: other /= Void external "built_in" ensure -- from ANY same_type: Result implies same_type (other) symmetric: Result implies other.standard_is_equal (Current) end feature -- Status report conforms_to (other: ANY): BOOLEAN -- Does type of current object conform to type -- of other (as per Eiffel: The Language, chapter 13)? -- (from ANY) require -- from ANY other_not_void: other /= Void external "built_in" end decimal_point_is_comma: BOOLEAN -- Has last parsed number a comma as decimal point? error: BOOLEAN -- Has there been an error in last parse operation? do Result := state = State_error end has_exponent: BOOLEAN -- Has last parsed number an exponent? do Result := exponent_begin > 0 end has_point: BOOLEAN -- Has last parsed number a fractional part? do Result := decimal_point_index /= 0 end is_comma_allowed: BOOLEAN -- Is ',' allowed as fractional part separator? is_infinity: BOOLEAN -- Is last parsed number an 'Infinity'? do Result := not error and then state = State_infinity end is_nan: BOOLEAN -- Is last parsed number a 'Not a Number'? do Result := not error and then state = State_nan end is_snan: BOOLEAN -- Is last parsed number a 'Signaling NaN'? do Result := not error and then state = State_snan end same_type (other: ANY): BOOLEAN -- Is type of current object identical to type of other? -- (from ANY) require -- from ANY other_not_void: other /= Void external "built_in" ensure -- from ANY definition: Result = (conforms_to (other) and other.conforms_to (Current)) end feature -- Duplication frozen clone (other: detachable ANY): like other obsolete "Use `twin' instead. [2017-05-31]" -- Void if other is void; otherwise new object -- equal to other -- -- For non-void other, clone calls copy; -- to change copying/cloning semantics, redefine copy. -- (from ANY) do if other /= Void then Result := other.twin end ensure -- from ANY instance_free: class equal: Result ~ other end copy (other: MA_DECIMAL_TEXT_PARSER) -- Update current object using fields of object attached -- to other, so as to yield equal objects. -- (from ANY) require -- from ANY other_not_void: other /= Void type_identity: same_type (other) external "built_in" ensure -- from ANY is_equal: Current ~ other end frozen deep_clone (other: detachable ANY): like other obsolete "Use `deep_twin' instead. [2017-05-31]" -- Void if other is void: otherwise, new object structure -- recursively duplicated from the one attached to other -- (from ANY) do if other /= Void then Result := other.deep_twin end ensure -- from ANY instance_free: class deep_equal: deep_equal (other, Result) end frozen deep_copy (other: MA_DECIMAL_TEXT_PARSER) -- Effect equivalent to that of: -- copy (other . deep_twin) -- (from ANY) require -- from ANY other_not_void: other /= Void do copy (other.deep_twin) ensure -- from ANY deep_equal: deep_equal (Current, other) end frozen deep_twin: MA_DECIMAL_TEXT_PARSER -- New object structure recursively duplicated from Current. -- (from ANY) external "built_in" ensure -- from ANY deep_twin_not_void: Result /= Void deep_equal: deep_equal (Current, Result) end frozen standard_clone (other: detachable ANY): like other obsolete "Use `standard_twin' instead. [2017-05-31]" -- Void if other is void; otherwise new object -- field-by-field identical to other. -- Always uses default copying semantics. -- (from ANY) do if other /= Void then Result := other.standard_twin end ensure -- from ANY instance_free: class equal: standard_equal (Result, other) end frozen standard_copy (other: MA_DECIMAL_TEXT_PARSER) -- Copy every field of other onto corresponding field -- of current object. -- (from ANY) require -- from ANY other_not_void: other /= Void type_identity: same_type (other) external "built_in" ensure -- from ANY is_standard_equal: standard_is_equal (other) end frozen standard_twin: MA_DECIMAL_TEXT_PARSER -- New object field-by-field identical to other. -- Always uses default copying semantics. -- (from ANY) external "built_in" ensure -- from ANY standard_twin_not_void: Result /= Void equal: standard_equal (Result, Current) end frozen twin: MA_DECIMAL_TEXT_PARSER -- New object equal to Current -- twin calls copy; to change copying/twinning semantics, redefine copy. -- (from ANY) external "built_in" ensure -- from ANY twin_not_void: Result /= Void is_equal: Result ~ Current end feature -- Basic operations frozen as_attached: attached MA_DECIMAL_TEXT_PARSER obsolete "Remove calls to this feature. [2017-05-31]" -- Attached version of Current. -- (Can be used during transitional period to convert -- non-void-safe classes to void-safe ones.) -- (from ANY) do Result := Current end frozen default: detachable MA_DECIMAL_TEXT_PARSER -- Default value of object's type -- (from ANY) do end frozen default_pointer: POINTER -- Default value of type POINTER -- (Avoid the need to write p.default for -- some p of type POINTER.) -- (from ANY) do ensure -- from ANY instance_free: class end default_rescue -- Process exception for routines with no Rescue clause. -- (Default: do nothing.) -- (from ANY) do end frozen do_nothing -- Execute a null action. -- (from ANY) do ensure -- from ANY instance_free: class end parse (s: STRING_8) -- Parse s. require -- from MA_DECIMAL_PARSER a_string_not_void: s /= Void a_string_not_empty: not s.is_empty do parse_ctx (s, shared_decimal_context, False) ensure -- from MA_DECIMAL_PARSER last_decimal_not_void_when_no_error: not error implies last_decimal /= Void ensure then last_parsed_string_affected: last_parsed = s end parse_ctx (s: STRING_8; ctx: MA_DECIMAL_CONTEXT; parse_comma_as_decimal_point: BOOLEAN) -- Parse s using ctx wrt parse_comma_as_decimal_point. require s_not_void: s /= Void s_not_empty: not s.is_empty local old_allowed: BOOLEAN do old_allowed := is_comma_allowed is_comma_allowed := parse_comma_as_decimal_point parse_and_create_last_decimal (s, ctx) is_comma_allowed := old_allowed ensure no_mode_change: is_comma_allowed = old is_comma_allowed last_parsed_string_affected: last_parsed = s last_decimal_not_void_when_no_error: not error implies last_decimal /= Void end parse_with_decimal_point_comma (s: STRING_8) -- Parse s with comma as decimal point. require s_not_void: s /= Void s_not_empty: not s.is_empty do parse_ctx (s, shared_decimal_context, True) ensure no_mode_change: is_comma_allowed = old is_comma_allowed last_parsed_string_affected: last_parsed = s last_decimal_not_void_when_no_error: not error implies last_decimal /= Void end feature {MA_DECIMAL} -- Basic operations case_insensitive_substring_equal (s: STRING_8; i_begin, i_end: INTEGER_32; t: STRING_8): BOOLEAN -- Is s[i_begin, i_end] equal to t[1, tcount] - case insensitive? require s_not_void: s /= Void t_not_void: t /= Void local i, j: INTEGER_32 do from i := i_begin j := 1 until i > s.count or else j > t.count or else i > i_end or else Character_.as_lower (s.item (i)) /= Character_.as_lower (t.item (j)) loop i := i + 1 j := j + 1 end Result := (i > i_end and then j > t.count) ensure definition: Result = String_.same_string (s.substring (i_begin, i_end).as_lower, t.as_lower) end decimal_parse (s: STRING_8) -- Effective parse of s. require s_not_void: s /= Void local c: CHARACTER_8 i, nb: INTEGER_32 do from state := State_start sign := 1 exponent_sign := 1 exponent_as_double := 0.to_double i := 1 nb := s.count coefficient_begin := 0 coefficient_end := 0 exponent_begin := 0 exponent_end := 0 exponent_significant_digits := 0 decimal_point_index := 0 decimal_point_is_comma := False until state = State_error or else i > nb loop c := s.item (i) inspect state when State_start then process_start (c, i, s) when State_sign then process_sign (c, i, s) when State_integer_part then process_integer_part (c, i) when State_starting_point then process_starting_point (c, i) when State_point then process_point (c, i) when State_comma then process_comma (c, i) when State_fractional_part then process_fractional_part (c, i) when State_start_exponent then process_start_exponent (c, i) when State_exponent_sign then process_exponent_sign (c, i) when State_exponent then process_exponent (c, i) when State_infinity, State_snan, State_nan then i := nb else state := State_error end i := i + 1 end inspect state when State_start, State_sign, State_comma, State_start_exponent, State_exponent_sign, State_starting_point then state := State_error else end if decimal_point_is_comma and then not is_comma_allowed then state := State_error end last_parsed := s ensure last_parsed_is_s: last_parsed = s end handle_i (s: STRING_8; index: INTEGER_32) -- Handle the case of 'i' or 'I' recognized in s at index. require s_not_void: s /= Void index_in_s: index > 0 and then index <= s.count do if case_insensitive_substring_equal (s, index + 1, index + 2, "nf") then if (s.count - index + 1) = 3 or else case_insensitive_substring_equal (s, index + 3, s.count, "inity") then state := State_infinity else state := State_error error_code := Error_invalid_value end else state := State_error error_code := Error_invalid_value end ensure definition: not error implies is_infinity and then state = State_infinity end parse_and_create_last_decimal (s: STRING_8; ctx: MA_DECIMAL_CONTEXT) -- Parse s and create last_decimal using ctx. do decimal_parse (s) if not error then create last_decimal.make_from_parser (Current, ctx) else last_decimal := Void end ensure last_parsed_is_s: last_parsed = s last_decimal_created_if_no_error: not error implies last_decimal /= Void end process_comma (c: CHARACTER_8; index: INTEGER_32) -- Process c at index when in State_comma. require state_comma: state = State_comma do inspect c when '0'..'9' then coefficient_end := coefficient_end + 1 state := State_fractional_part else state := State_error error_code := Error_invalid_character end end process_exponent (c: CHARACTER_8; index: INTEGER_32) -- Process c at index when in State_exponent. require state_exponent: state = State_exponent do inspect c when '0'..'9' then exponent_as_double := exponent_as_double * 10.to_double + (c.code - ('0').code).to_double exponent_end := exponent_end + 1 if exponent_as_double > 0.to_double then exponent_significant_digits := exponent_significant_digits + 1 end state := State_exponent else state := State_error error_code := Error_invalid_character_in_exponent end end process_exponent_sign (c: CHARACTER_8; index: INTEGER_32) -- Process c at index when in State_exponent_sign. require state_exponent_sign: state = State_exponent_sign do inspect c when '0'..'9' then exponent_as_double := c.code - ('0').code.to_double exponent_begin := index exponent_end := index if exponent_as_double > 0.to_double then exponent_significant_digits := 1 end state := State_exponent else state := State_error error_code := Error_invalid_character_in_exponent end end process_fractional_part (c: CHARACTER_8; index: INTEGER_32) -- Process c at inddex when in State_fractional_part. require state_fractional_part: state = State_fractional_part do inspect c when '0'..'9' then coefficient_end := coefficient_end + 1 when 'e', 'E' then state := State_start_exponent else state := State_error error_code := Error_invalid_character_in_decimal_part end end process_integer_part (c: CHARACTER_8; index: INTEGER_32) -- Process c at index when in State_integer_part. require state_integer_part: state = State_integer_part do inspect c when '0'..'9' then coefficient_end := coefficient_end + 1 when 'e', 'E' then state := State_start_exponent when '.' then coefficient_end := coefficient_end + 1 decimal_point_index := index state := State_point when ',' then coefficient_end := coefficient_end + 1 decimal_point_index := index decimal_point_is_comma := True state := State_comma else state := State_error error_code := Error_invalid_character_in_integer_part end end process_point (c: CHARACTER_8; index: INTEGER_32) -- Process c at index when in State_point. require state_point: state = State_point do inspect c when '0'..'9' then coefficient_end := coefficient_end + 1 state := State_fractional_part when 'e', 'E' then state := State_start_exponent else state := State_error error_code := Error_invalid_character end end process_sign (c: CHARACTER_8; index: INTEGER_32; s: STRING_8) -- Process c at index in s when in State_sign. require state_sign: state = State_sign s_not_void: s /= Void index_in_s: index > 0 and then index <= s.count do inspect c when 'i', 'I' then handle_i (s, index) when '0'..'9' then coefficient_begin := index coefficient_end := index state := State_integer_part when '.' then coefficient_begin := index coefficient_end := index decimal_point_index := index state := State_starting_point else state := State_error error_code := Error_invalid_character end end process_start (c: CHARACTER_8; index: INTEGER_32; s: STRING_8) -- Process c at index in s when in State_start. require state_start: state = State_start s_not_void: s /= Void index_in_s: index > 0 and then index <= s.count do inspect c when '0'..'9' then coefficient_begin := index coefficient_end := index state := State_integer_part when '+', '-' then if c = '-' then sign := -1 else sign := 1 end state := State_sign when 'n', 'N' then if case_insensitive_substring_equal (s, index + 1, s.count, "an") then state := State_nan else state := State_error error_code := Error_invalid_value end when 's', 'S' then if case_insensitive_substring_equal (s, index + 1, s.count, "nan") then state := State_snan else state := State_error error_code := Error_invalid_value end when 'i', 'I' then handle_i (s, index) when '.' then coefficient_begin := index coefficient_end := index decimal_point_index := index state := State_starting_point else state := State_error error_code := Error_invalid_character end ensure next_state: error or else state /= State_start end process_start_exponent (c: CHARACTER_8; index: INTEGER_32) -- Process c at index when in State_start_exponent. require state_start_exponent: state = State_start_exponent do inspect c when '0'..'9' then exponent_as_double := c.code - ('0').code.to_double exponent_begin := index exponent_end := index if exponent_as_double > 0.to_double then exponent_significant_digits := 1 end state := State_exponent when '+', '-' then if c = '-' then exponent_sign := -1 else exponent_sign := 1 end state := State_exponent_sign else state := State_error error_code := Error_invalid_character_in_exponent end end process_starting_point (c: CHARACTER_8; index: INTEGER_32) -- Process c at index when in State_starting_point. require state_starting_point: state = State_starting_point do inspect c when '0'..'9' then coefficient_begin := index coefficient_end := index state := State_fractional_part else state := State_error error_code := Error_invalid_character end end feature {NONE} -- Implementation Cell: KL_CELL [MA_DECIMAL_CONTEXT] -- Cell containing shared decimal context -- (from MA_SHARED_DECIMAL_CONTEXT) once create Result.make (Default_context) ensure -- from MA_SHARED_DECIMAL_CONTEXT instance_free: class cell_not_void: Result /= Void context_not_void: Cell.item /= Void end Error_invalid_character: INTEGER_32 = 102 Error_invalid_character_in_decimal_part: INTEGER_32 = 104 Error_invalid_character_in_exponent: INTEGER_32 = 105 Error_invalid_character_in_integer_part: INTEGER_32 = 103 Error_invalid_state: INTEGER_32 = 106 Error_invalid_value: INTEGER_32 = 101 feature -- Constants State_comma: INTEGER_32 = 11 State_error: INTEGER_32 = 14 State_exponent: INTEGER_32 = 9 State_exponent_sign: INTEGER_32 = 13 State_fractional_part: INTEGER_32 = 8 State_infinity: INTEGER_32 = 5 State_integer_part: INTEGER_32 = 6 State_nan: INTEGER_32 = 2 State_point: INTEGER_32 = 7 State_sign: INTEGER_32 = 10 State_snan: INTEGER_32 = 3 State_start: INTEGER_32 = 1 State_start_exponent: INTEGER_32 = 12 State_starting_point: INTEGER_32 = 4 feature -- Output Io: STD_FILES -- Handle to standard file setup -- (from ANY) once create Result Result.set_output_default ensure -- from ANY instance_free: class io_not_void: Result /= Void end out: STRING_8 -- New string containing terse printable representation -- of current object -- (from ANY) do Result := tagged_out ensure -- from ANY out_not_void: Result /= Void end print (o: detachable ANY) -- Write terse external representation of o -- on standard output. -- (from ANY) do if o /= Void then Io.put_string (o.out) end ensure -- from ANY instance_free: class end frozen tagged_out: STRING_8 -- New string containing terse printable representation -- of current object -- (from ANY) external "built_in" ensure -- from ANY tagged_out_not_void: Result /= Void end feature -- Platform Operating_environment: OPERATING_ENVIRONMENT -- Objects available from the operating system -- (from ANY) once create Result ensure -- from ANY instance_free: class operating_environment_not_void: Result /= Void end feature {NONE} -- Retrieval frozen internal_correct_mismatch -- Called from runtime to perform a proper dynamic dispatch on correct_mismatch -- from MISMATCH_CORRECTOR. -- (from ANY) local l_msg: STRING_8 l_exc: EXCEPTIONS do if attached {MISMATCH_CORRECTOR} Current as l_corrector then l_corrector.correct_mismatch else create l_msg.make_from_string ("Mismatch: ") create l_exc l_msg.append (generating_type.name) l_exc.raise_retrieval_exception (l_msg) end end feature {NONE} -- Setting set_shared_decimal_context (new_context: MA_DECIMAL_CONTEXT) -- Set shared_decimal_context to new_context. -- It is best practice to call this routine once and for all -- at the beginning of the application to avoid unexpected -- behaviors. -- (from MA_SHARED_DECIMAL_CONTEXT) require -- from MA_SHARED_DECIMAL_CONTEXT new_context_not_void: new_context /= Void do Cell.put (new_context) ensure -- from MA_SHARED_DECIMAL_CONTEXT instance_free: class context_set: shared_decimal_context = new_context end invariant decimal_point_is_comma_implies_has_fractional_part: decimal_point_is_comma implies has_point -- from ANY reflexive_equality: standard_is_equal (Current) reflexive_conformance: conforms_to (Current) end -- class MA_DECIMAL_TEXT_PARSER
Generated by ISE EiffelStudio