blob: f5ec598a42c5f11698da27cec7e502e90d0370b3 [file] [log] [blame]
------------------------------------------------------------------------------
-- --
-- GNAT COMPILER COMPONENTS --
-- --
-- G N A T . F O R M A T T E D _ S T R I N G --
-- --
-- B o d y --
-- --
-- Copyright (C) 2014-2025, Free Software Foundation, Inc. --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
-- ware Foundation; either version 3, or (at your option) any later ver- --
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
-- GNAT was originally developed by the GNAT team at New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc. --
-- --
------------------------------------------------------------------------------
with Ada.Characters.Handling;
with Ada.Float_Text_IO;
with Ada.Integer_Text_IO;
with Ada.Long_Float_Text_IO;
with Ada.Long_Integer_Text_IO;
with Ada.Strings;
with Ada.Strings.Fixed;
with Ada.Unchecked_Deallocation;
with System.Address_Image;
package body GNAT.Formatted_String is
type F_Kind is (Decimal_Int, -- %d %i
Unsigned_Decimal_Int, -- %u
Unsigned_Octal, -- %o
Unsigned_Hexadecimal_Int, -- %x
Unsigned_Hexadecimal_Int_Up, -- %X
Decimal_Float, -- %f %F
Decimal_Scientific_Float, -- %e
Decimal_Scientific_Float_Up, -- %E
G_Specifier, -- %g
G_Specifier_Up, -- %G
Char, -- %c
Str, -- %s
Pointer -- %p
);
type Sign_Kind is (Neg, Zero, Pos);
subtype Is_Number is F_Kind range Decimal_Int .. G_Specifier_Up;
type F_Sign is (If_Neg, Forced, Space) with Default_Value => If_Neg;
type F_Base is (None, C_Style, Ada_Style) with Default_Value => None;
Unset : constant Integer := -1;
type F_Data is record
Kind : F_Kind;
Width : Natural := 0;
Precision : Integer := Unset;
Left_Justify : Boolean := False;
Sign : F_Sign;
Base : F_Base;
Zero_Pad : Boolean := False;
Value_Needed : Natural range 0 .. 2 := 0;
end record;
type Notation is (Decimal, Scientific);
procedure Advance_And_Accumulate_Until_Next_Specifier
(Format : Formatted_String);
-- Advance Format.D.Index until either the next format specifier is
-- encountered, or the end of Format.D.Format is reached. The characters
-- advanced over are appended to Format.D.Result.
procedure Next_Format
(Format : Formatted_String;
F_Spec : out F_Data;
Start : out Positive);
-- Parse the next format specifier, a format specifier has the following
-- syntax: %[flags][width][.precision][length]specifier
procedure Determine_Notation_And_Aft
(Exponent : Integer;
Precision : Text_IO.Field;
Nota : out Notation;
Aft : out Text_IO.Field);
-- Determine whether to use scientific or decimal notation and the value of
-- Aft given the exponent and precision of a real number, as described in
-- the C language specification, section 7.21.6.1.
function Get_Formatted
(F_Spec : F_Data;
Value : String;
Len : Positive) return String;
-- Returns Value formatted given the information in F_Spec
procedure Increment_Integral_Part
(Buffer : in out String;
First_Non_Blank : in out Positive;
Last_Digit_Position : Positive);
-- Buffer must contain the textual representation of a number.
-- Last_Digit_Position must be the position of the rightmost digit of the
-- integral part. Buffer must have at least one padding blank. Increment
-- the integral part.
procedure Raise_Wrong_Format (Format : Formatted_String) with No_Return;
-- Raise the Format_Error exception which information about the context
generic
type Flt is private;
with procedure Put
(To : out String;
Item : Flt;
Aft : Text_IO.Field;
Exp : Text_IO.Field);
function P_Flt_Format
(Format : Formatted_String;
Var : Flt) return Formatted_String;
-- Generic routine which handles all floating point numbers
generic
type Int is private;
with function To_Integer (Item : Int) return Integer;
with function Sign (Item : Int) return Sign_Kind;
with procedure Put
(To : out String;
Item : Int;
Base : Text_IO.Number_Base);
function P_Int_Format
(Format : Formatted_String;
Var : Int) return Formatted_String;
-- Generic routine which handles all the integer numbers
procedure Remove_Extraneous_Decimal_Digit
(Textual_Rep : in out String;
First_Non_Blank : in out Positive);
-- Remove the unique digit to the right of the point in Textual_Rep
procedure Trim_Fractional_Part
(Textual_Rep : in out String;
First_Non_Blank : in out Positive);
-- Remove trailing zeros from Textual_Rep, which must be the textual
-- representation of a real number. If the fractional part only contains
-- zeros, also remove the point.
---------
-- "+" --
---------
function "+" (Format : String) return Formatted_String is
begin
return Formatted_String'
(Finalization.Controlled with
D => new Data'(Format'Length, 1, 1,
Null_Unbounded_String, 0, 0, [0, 0], Format));
end "+";
---------
-- "-" --
---------
function "-" (Format : Formatted_String) return String is
begin
-- Make sure we get the remaining character up to the next unhandled
-- format specifier.
Advance_And_Accumulate_Until_Next_Specifier (Format);
return To_String (Format.D.Result);
end "-";
---------
-- "&" --
---------
function "&"
(Format : Formatted_String;
Var : Character) return Formatted_String
is
F : F_Data;
Start : Positive;
begin
Next_Format (Format, F, Start);
if F.Value_Needed > 0 then
Raise_Wrong_Format (Format);
end if;
case F.Kind is
when Char =>
Append (Format.D.Result, Get_Formatted (F, String'(1 => Var), 1));
when others =>
Raise_Wrong_Format (Format);
end case;
return Format;
end "&";
function "&"
(Format : Formatted_String;
Var : String) return Formatted_String
is
F : F_Data;
Start : Positive;
begin
Next_Format (Format, F, Start);
if F.Value_Needed > 0 then
Raise_Wrong_Format (Format);
end if;
case F.Kind is
when Str =>
declare
S : constant String := Get_Formatted (F, Var, Var'Length);
begin
if F.Precision = Unset then
Append (Format.D.Result, S);
else
Append
(Format.D.Result,
S (S'First .. S'First + F.Precision - 1));
end if;
end;
when others =>
Raise_Wrong_Format (Format);
end case;
return Format;
end "&";
function "&"
(Format : Formatted_String;
Var : Boolean) return Formatted_String is
begin
return Format & Boolean'Image (Var);
end "&";
function "&"
(Format : Formatted_String;
Var : Float) return Formatted_String
is
function Float_Format is new Flt_Format (Float, Float_Text_IO.Put);
begin
return Float_Format (Format, Var);
end "&";
function "&"
(Format : Formatted_String;
Var : Long_Float) return Formatted_String
is
function Float_Format is
new Flt_Format (Long_Float, Long_Float_Text_IO.Put);
begin
return Float_Format (Format, Var);
end "&";
function "&"
(Format : Formatted_String;
Var : Duration) return Formatted_String
is
package Duration_Text_IO is new Text_IO.Fixed_IO (Duration);
function Duration_Format is
new P_Flt_Format (Duration, Duration_Text_IO.Put);
begin
return Duration_Format (Format, Var);
end "&";
function "&"
(Format : Formatted_String;
Var : Integer) return Formatted_String
is
function Integer_Format is
new Int_Format (Integer, Integer_Text_IO.Put);
begin
return Integer_Format (Format, Var);
end "&";
function "&"
(Format : Formatted_String;
Var : Long_Integer) return Formatted_String
is
function Integer_Format is
new Int_Format (Long_Integer, Long_Integer_Text_IO.Put);
begin
return Integer_Format (Format, Var);
end "&";
function "&"
(Format : Formatted_String;
Var : System.Address) return Formatted_String
is
A_Img : constant String := System.Address_Image (Var);
F : F_Data;
Start : Positive;
begin
Next_Format (Format, F, Start);
if F.Value_Needed > 0 then
Raise_Wrong_Format (Format);
end if;
case F.Kind is
when Pointer =>
Append (Format.D.Result, Get_Formatted (F, A_Img, A_Img'Length));
when others =>
Raise_Wrong_Format (Format);
end case;
return Format;
end "&";
------------
-- Adjust --
------------
overriding procedure Adjust (F : in out Formatted_String) is
begin
F.D.Ref_Count := F.D.Ref_Count + 1;
end Adjust;
-------------------------------------------------
-- Advance_And_Accumulate_Until_Next_Specifier --
-------------------------------------------------
procedure Advance_And_Accumulate_Until_Next_Specifier
(Format : Formatted_String)
is
begin
loop
if Format.D.Index > Format.D.Format'Last then
exit;
end if;
if Format.D.Format (Format.D.Index) /= '%' then
Append (Format.D.Result, Format.D.Format (Format.D.Index));
Format.D.Index := Format.D.Index + 1;
elsif Format.D.Index + 1 <= Format.D.Format'Last
and then Format.D.Format (Format.D.Index + 1) = '%'
then
Append (Format.D.Result, '%');
Format.D.Index := Format.D.Index + 2;
else
exit;
end if;
end loop;
end Advance_And_Accumulate_Until_Next_Specifier;
--------------------------------
-- Determine_Notation_And_Aft --
--------------------------------
procedure Determine_Notation_And_Aft
(Exponent : Integer;
Precision : Text_IO.Field;
Nota : out Notation;
Aft : out Text_IO.Field)
is
-- The constants use the same names as those from the C specification
-- in order to match the description of the predicate.
P : constant Text_IO.Field := (if Precision /= 0 then Precision else 1);
X : constant Integer := Exponent;
begin
if P > X and X >= -4 then
Nota := Decimal;
Aft := P - (X + 1);
else
Nota := Scientific;
Aft := P - 1;
end if;
end Determine_Notation_And_Aft;
--------------------
-- Decimal_Format --
--------------------
function Decimal_Format
(Format : Formatted_String;
Var : Flt) return Formatted_String
is
function Flt_Format is new P_Flt_Format (Flt, Put);
begin
return Flt_Format (Format, Var);
end Decimal_Format;
-----------------
-- Enum_Format --
-----------------
function Enum_Format
(Format : Formatted_String;
Var : Enum) return Formatted_String is
begin
return Format & Enum'Image (Var);
end Enum_Format;
--------------
-- Finalize --
--------------
overriding procedure Finalize (F : in out Formatted_String) is
procedure Unchecked_Free is
new Unchecked_Deallocation (Data, Data_Access);
D : Data_Access := F.D;
begin
F.D := null;
D.Ref_Count := D.Ref_Count - 1;
if D.Ref_Count = 0 then
Unchecked_Free (D);
end if;
end Finalize;
------------------
-- Fixed_Format --
------------------
function Fixed_Format
(Format : Formatted_String;
Var : Flt) return Formatted_String
is
function Flt_Format is new P_Flt_Format (Flt, Put);
begin
return Flt_Format (Format, Var);
end Fixed_Format;
----------------
-- Flt_Format --
----------------
function Flt_Format
(Format : Formatted_String;
Var : Flt) return Formatted_String
is
function Flt_Format is new P_Flt_Format (Flt, Put);
begin
return Flt_Format (Format, Var);
end Flt_Format;
-------------------
-- Get_Formatted --
-------------------
function Get_Formatted
(F_Spec : F_Data;
Value : String;
Len : Positive) return String
is
use Ada.Strings.Fixed;
Res : Unbounded_String;
S : Positive := Value'First;
begin
-- Handle the flags
if F_Spec.Kind in Is_Number then
if F_Spec.Sign = Forced and then Value (Value'First) /= '-' then
Append (Res, "+");
elsif F_Spec.Sign = Space and then Value (Value'First) /= '-' then
Append (Res, " ");
end if;
if Value (Value'First) = '-' then
Append (Res, "-");
S := S + 1;
end if;
end if;
-- Zero padding if required and possible
if not F_Spec.Left_Justify
and then F_Spec.Zero_Pad
and then F_Spec.Width > Len + Value'First - S
then
Append (Res, String'((F_Spec.Width - (Len + Value'First - S)) * '0'));
end if;
-- Add the value now
Append (Res, Value (S .. Value'Last));
declare
R : String (1 .. Natural'Max (Natural'Max (F_Spec.Width, Len),
Length (Res))) := [others => ' '];
begin
if F_Spec.Left_Justify then
R (1 .. Length (Res)) := To_String (Res);
else
R (R'Last - Length (Res) + 1 .. R'Last) := To_String (Res);
end if;
return R;
end;
end Get_Formatted;
-----------------------------
-- Increment_Integral_Part --
-----------------------------
procedure Increment_Integral_Part
(Buffer : in out String;
First_Non_Blank : in out Positive;
Last_Digit_Position : Positive)
is
Cursor : Natural := Last_Digit_Position;
begin
while Buffer (Cursor) = '9' loop
Buffer (Cursor) := '0';
Cursor := Cursor - 1;
end loop;
pragma Assert (Cursor > 0);
if Buffer (Cursor) in '0' .. '8' then
Buffer (Cursor) := Character'Succ (Buffer (Cursor));
else
Ada.Strings.Fixed.Insert
(Buffer,
Cursor + 1,
"1");
First_Non_Blank := First_Non_Blank - 1;
end if;
end Increment_Integral_Part;
----------------
-- Int_Format --
----------------
function Int_Format
(Format : Formatted_String;
Var : Int) return Formatted_String
is
function Sign (Var : Int) return Sign_Kind is
(if Var < 0 then Neg elsif Var = 0 then Zero else Pos);
function To_Integer (Var : Int) return Integer is
(Integer (Var));
function Int_Format is new P_Int_Format (Int, To_Integer, Sign, Put);
begin
return Int_Format (Format, Var);
end Int_Format;
----------------
-- Mod_Format --
----------------
function Mod_Format
(Format : Formatted_String;
Var : Int) return Formatted_String
is
function Sign (Var : Int) return Sign_Kind is
(if Var < 0 then Neg elsif Var = 0 then Zero else Pos);
function To_Integer (Var : Int) return Integer is
(Integer (Var));
function Int_Format is new P_Int_Format (Int, To_Integer, Sign, Put);
begin
return Int_Format (Format, Var);
end Mod_Format;
-----------------
-- Next_Format --
-----------------
procedure Next_Format
(Format : Formatted_String;
F_Spec : out F_Data;
Start : out Positive)
is
F : String renames Format.D.Format;
J : Natural renames Format.D.Index;
S : Natural;
Width_From_Var : Boolean := False;
begin
Format.D.Current := Format.D.Current + 1;
F_Spec.Value_Needed := 0;
-- Got to next %
Advance_And_Accumulate_Until_Next_Specifier (Format);
if J >= F'Last or else F (J) /= '%' then
raise Format_Error with "no format specifier found for parameter"
& Positive'Image (Format.D.Current);
end if;
Start := J;
J := J + 1;
-- Check for any flags
Flags_Check : while J < F'Last loop
if F (J) = '-' then
F_Spec.Left_Justify := True;
elsif F (J) = '+' then
F_Spec.Sign := Forced;
elsif F (J) = ' ' then
F_Spec.Sign := Space;
elsif F (J) = '#' then
F_Spec.Base := C_Style;
elsif F (J) = '~' then
F_Spec.Base := Ada_Style;
elsif F (J) = '0' then
F_Spec.Zero_Pad := True;
else
exit Flags_Check;
end if;
J := J + 1;
end loop Flags_Check;
-- Check width if any
if F (J) in '0' .. '9' then
-- We have a width parameter
S := J;
while J < F'Last and then F (J + 1) in '0' .. '9' loop
J := J + 1;
end loop;
F_Spec.Width := Natural'Value (F (S .. J));
J := J + 1;
elsif F (J) = '*' then
-- The width will be taken from the integer parameter
F_Spec.Value_Needed := 1;
Width_From_Var := True;
J := J + 1;
end if;
if F (J) = '.' then
-- We have a precision parameter
J := J + 1;
if F (J) in '0' .. '9' then
S := J;
while J < F'Length and then F (J + 1) in '0' .. '9' loop
J := J + 1;
end loop;
if F (J) = '.' then
-- No precision, 0 is assumed
F_Spec.Precision := 0;
else
F_Spec.Precision := Natural'Value (F (S .. J));
end if;
J := J + 1;
elsif F (J) = '*' then
-- The prevision will be taken from the integer parameter
F_Spec.Value_Needed := F_Spec.Value_Needed + 1;
J := J + 1;
end if;
end if;
-- Skip the length specifier, this is not needed for this implementation
-- but yet for compatibility reason it is handled.
Length_Check :
while J <= F'Last
and then F (J) in 'h' | 'l' | 'j' | 'z' | 't' | 'L'
loop
J := J + 1;
end loop Length_Check;
if J > F'Last then
Raise_Wrong_Format (Format);
end if;
-- Read next character which should be the expected type
case F (J) is
when 'c' => F_Spec.Kind := Char;
when 's' => F_Spec.Kind := Str;
when 'd' | 'i' => F_Spec.Kind := Decimal_Int;
when 'u' => F_Spec.Kind := Unsigned_Decimal_Int;
when 'f' | 'F' => F_Spec.Kind := Decimal_Float;
when 'e' => F_Spec.Kind := Decimal_Scientific_Float;
when 'E' => F_Spec.Kind := Decimal_Scientific_Float_Up;
when 'g' => F_Spec.Kind := G_Specifier;
when 'G' => F_Spec.Kind := G_Specifier_Up;
when 'o' => F_Spec.Kind := Unsigned_Octal;
when 'x' => F_Spec.Kind := Unsigned_Hexadecimal_Int;
when 'X' => F_Spec.Kind := Unsigned_Hexadecimal_Int_Up;
when others =>
raise Format_Error with "unknown format specified for parameter"
& Positive'Image (Format.D.Current);
end case;
J := J + 1;
if F_Spec.Value_Needed > 0
and then F_Spec.Value_Needed = Format.D.Stored_Value
then
if F_Spec.Value_Needed = 1 then
if Width_From_Var then
F_Spec.Width := Format.D.Stack (1);
else
F_Spec.Precision := Format.D.Stack (1);
end if;
else
F_Spec.Width := Format.D.Stack (1);
F_Spec.Precision := Format.D.Stack (2);
end if;
end if;
end Next_Format;
------------------
-- P_Flt_Format --
------------------
function P_Flt_Format
(Format : Formatted_String;
Var : Flt) return Formatted_String
is
procedure Compute_Exponent
(Var : Flt;
Valid : out Boolean;
Exponent : out Integer);
-- If Var is invalid (for example, a NaN of an inf), set Valid False and
-- set Exponent to 0. Otherwise, set Valid True, and store the exponent
-- of the scientific notation representation of Var in Exponent. The
-- exponent can also be defined as:
-- - If Var = 0, 0.
-- - Otherwise, Floor (Log_10 (Abs (Var))).
procedure Format_With_Notation
(Var : Flt;
Nota : Notation;
Aft : Text_IO.Field;
Buffer : out String);
-- Fill buffer with the formatted value of Var following the notation
-- specified through Nota.
procedure Handle_G_Specifier
(Buffer : out String;
First_Non_Blank : out Positive;
Aft : Text_IO.Field);
-- Fill Buffer with the formatted value of Var according to the rules of
-- the "%g" specifier. Buffer is right-justified and padded with blanks.
----------------------
-- Compute_Exponent --
----------------------
procedure Compute_Exponent
(Var : Flt;
Valid : out Boolean;
Exponent : out Integer)
is
-- The way the exponent is computed is convoluted. It is not possible
-- to use the logarithm in base 10 of Var and floor it, because the
-- math functions for this are not available for fixed point types.
-- Instead, use the generic Put procedure to produce a scientific
-- representation of Var, and parse the exponent part of that back
-- into an Integer.
Scientific_Rep : String (1 .. 50);
E_Position : Natural;
begin
Put (Scientific_Rep, Var, Aft => 1, Exp => 1);
E_Position := Ada.Strings.Fixed.Index (Scientific_Rep, "E");
if E_Position = 0 then
Valid := False;
Exponent := 0;
else
Valid := True;
Exponent :=
Integer'Value
(Scientific_Rep (E_Position + 1 .. Scientific_Rep'Last));
end if;
end Compute_Exponent;
--------------------------
-- Format_With_Notation --
--------------------------
procedure Format_With_Notation
(Var : Flt;
Nota : Notation;
Aft : Text_IO.Field;
Buffer : out String)
is
Exp : constant Text_IO.Field :=
(case Nota is when Decimal => 0, when Scientific => 3);
begin
Put (Buffer, Var, Aft, Exp);
end Format_With_Notation;
------------------------
-- Handle_G_Specifier --
------------------------
procedure Handle_G_Specifier
(Buffer : out String;
First_Non_Blank : out Positive;
Aft : Text_IO.Field)
is
-- There is nothing that is directly equivalent to the "%g" specifier
-- in the standard Ada functionality provided by Ada.Text_IO. The
-- procedure Put will still be used, but significant postprocessing
-- will be performed on the output of that procedure.
-- The following code is intended to match the behavior of C's printf
-- for %g, as described by paragraph "7.21.6.1 The fprintf function"
-- of the C language specification.
-- As explained in the C specification, we're going to have to make a
-- choice between decimal notation and scientific notation. One of
-- the elements we need in order to make that choice is the value of
-- the exponent in the decimal representation of Var. We will store
-- that value in Exponent.
Exponent : Integer;
Valid : Boolean;
Nota : Notation;
-- The value of the formal Aft comes from the precision specifier in
-- the format string. For %g, the precision specifier corresponds to
-- the number of significant figures desired, whereas the formal Aft
-- in Put corresponds to the number of digits after the point.
-- Effective_Aft is what will be passed to Put as Aft in order to
-- respect the semantics of %g.
Effective_Aft : Text_IO.Field;
Textual_Rep : String (Buffer'Range);
begin
Compute_Exponent (Var, Valid, Exponent);
Determine_Notation_And_Aft
(Exponent, Aft, Nota, Effective_Aft);
Format_With_Notation (Var, Nota, Effective_Aft, Textual_Rep);
First_Non_Blank := Strings.Fixed.Index_Non_Blank (Textual_Rep);
if not Valid then
null;
elsif Effective_Aft = 0 then
-- Special case: it is possible at this point that Effective_Aft
-- is zero. But when Put is passed zero through Aft, it still
-- outputs one digit after the point. See the reference manual,
-- A.10.9.25.
Remove_Extraneous_Decimal_Digit (Textual_Rep, First_Non_Blank);
else
Trim_Fractional_Part
(Textual_Rep, First_Non_Blank);
end if;
Buffer := Textual_Rep;
end Handle_G_Specifier;
-- Local variables
F : F_Data;
Buffer : String (1 .. 50);
S, E : Positive := 1;
Start : Positive;
Aft : Text_IO.Field;
-- Start of processing for P_Flt_Format
begin
Next_Format (Format, F, Start);
if F.Value_Needed /= Format.D.Stored_Value then
Raise_Wrong_Format (Format);
end if;
Format.D.Stored_Value := 0;
if F.Precision = Unset then
Aft := 6;
else
Aft := F.Precision;
end if;
case F.Kind is
when Decimal_Float =>
Put (Buffer, Var, Aft, Exp => 0);
S := Strings.Fixed.Index_Non_Blank (Buffer);
E := Buffer'Last;
when Decimal_Scientific_Float
| Decimal_Scientific_Float_Up
=>
Put (Buffer, Var, Aft, Exp => 3);
S := Strings.Fixed.Index_Non_Blank (Buffer);
E := Buffer'Last;
if F.Kind = Decimal_Scientific_Float then
Buffer (S .. E) :=
Characters.Handling.To_Lower (Buffer (S .. E));
end if;
when G_Specifier
| G_Specifier_Up
=>
Handle_G_Specifier (Buffer, S, Aft);
E := Buffer'Last;
if F.Kind = G_Specifier then
Buffer (S .. E) :=
Characters.Handling.To_Lower (Buffer (S .. E));
end if;
when others =>
Raise_Wrong_Format (Format);
end case;
Append (Format.D.Result,
Get_Formatted (F, Buffer (S .. E), Buffer (S .. E)'Length));
return Format;
end P_Flt_Format;
------------------
-- P_Int_Format --
------------------
function P_Int_Format
(Format : Formatted_String;
Var : Int) return Formatted_String
is
function Handle_Precision return Boolean;
-- Return True if nothing else to do
F : F_Data;
Buffer : String (1 .. 50);
S, E : Positive := 1;
Len : Natural := 0;
Start : Positive;
----------------------
-- Handle_Precision --
----------------------
function Handle_Precision return Boolean is
begin
if F.Precision = 0 and then Sign (Var) = Zero then
return True;
elsif F.Precision = Natural'Last then
null;
elsif F.Precision > E - S + 1 then
Len := F.Precision - (E - S + 1);
Buffer (S - Len .. S - 1) := [others => '0'];
S := S - Len;
end if;
return False;
end Handle_Precision;
-- Start of processing for P_Int_Format
begin
Next_Format (Format, F, Start);
if Format.D.Stored_Value < F.Value_Needed then
Format.D.Stored_Value := Format.D.Stored_Value + 1;
Format.D.Stack (Format.D.Stored_Value) := To_Integer (Var);
Format.D.Index := Start;
return Format;
end if;
Format.D.Stored_Value := 0;
case F.Kind is
when Unsigned_Octal =>
if Sign (Var) = Neg then
Raise_Wrong_Format (Format);
end if;
Put (Buffer, Var, Base => 8);
S := Strings.Fixed.Index (Buffer, "8#") + 2;
E := Strings.Fixed.Index (Buffer (S .. Buffer'Last), "#") - 1;
if Handle_Precision then
return Format;
end if;
case F.Base is
when None => null;
when C_Style => Len := 1;
when Ada_Style => Len := 3;
end case;
when Unsigned_Hexadecimal_Int =>
if Sign (Var) = Neg then
Raise_Wrong_Format (Format);
end if;
Put (Buffer, Var, Base => 16);
S := Strings.Fixed.Index (Buffer, "16#") + 3;
E := Strings.Fixed.Index (Buffer (S .. Buffer'Last), "#") - 1;
Buffer (S .. E) := Characters.Handling.To_Lower (Buffer (S .. E));
if Handle_Precision then
return Format;
end if;
case F.Base is
when None => null;
when C_Style => Len := 2;
when Ada_Style => Len := 4;
end case;
when Unsigned_Hexadecimal_Int_Up =>
if Sign (Var) = Neg then
Raise_Wrong_Format (Format);
end if;
Put (Buffer, Var, Base => 16);
S := Strings.Fixed.Index (Buffer, "16#") + 3;
E := Strings.Fixed.Index (Buffer (S .. Buffer'Last), "#") - 1;
if Handle_Precision then
return Format;
end if;
case F.Base is
when None => null;
when C_Style => Len := 2;
when Ada_Style => Len := 4;
end case;
when Unsigned_Decimal_Int =>
if Sign (Var) = Neg then
Raise_Wrong_Format (Format);
end if;
Put (Buffer, Var, Base => 10);
S := Strings.Fixed.Index_Non_Blank (Buffer);
E := Buffer'Last;
if Handle_Precision then
return Format;
end if;
when Decimal_Int =>
Put (Buffer, Var, Base => 10);
S := Strings.Fixed.Index_Non_Blank (Buffer);
E := Buffer'Last;
if Handle_Precision then
return Format;
end if;
when Char =>
S := Buffer'First;
E := Buffer'First;
Buffer (S) := Character'Val (To_Integer (Var));
if Handle_Precision then
return Format;
end if;
when others =>
Raise_Wrong_Format (Format);
end case;
-- Then add base if needed
declare
N : String := Get_Formatted (F, Buffer (S .. E), E - S + 1 + Len);
P : constant Positive :=
(if F.Left_Justify
then N'First
else Natural'Max (Strings.Fixed.Index_Non_Blank (N) - 1,
N'First));
begin
case F.Base is
when None =>
null;
when C_Style =>
case F.Kind is
when Unsigned_Octal =>
N (P) := 'O';
when Unsigned_Hexadecimal_Int =>
if F.Left_Justify then
N (P .. P + 1) := "Ox";
else
N (P - 1 .. P) := "0x";
end if;
when Unsigned_Hexadecimal_Int_Up =>
if F.Left_Justify then
N (P .. P + 1) := "OX";
else
N (P - 1 .. P) := "0X";
end if;
when others =>
null;
end case;
when Ada_Style =>
case F.Kind is
when Unsigned_Octal =>
if F.Left_Justify then
N (N'First + 2 .. N'Last) := N (N'First .. N'Last - 2);
else
N (P .. N'Last - 1) := N (P + 1 .. N'Last);
end if;
N (N'First .. N'First + 1) := "8#";
N (N'Last) := '#';
when Unsigned_Hexadecimal_Int
| Unsigned_Hexadecimal_Int_Up
=>
if F.Left_Justify then
N (N'First + 3 .. N'Last) := N (N'First .. N'Last - 3);
else
N (P .. N'Last - 1) := N (P + 1 .. N'Last);
end if;
N (N'First .. N'First + 2) := "16#";
N (N'Last) := '#';
when others =>
null;
end case;
end case;
Append (Format.D.Result, N);
end;
return Format;
end P_Int_Format;
------------------------
-- Raise_Wrong_Format --
------------------------
procedure Raise_Wrong_Format (Format : Formatted_String) is
begin
raise Format_Error with
"wrong format specified for parameter"
& Positive'Image (Format.D.Current);
end Raise_Wrong_Format;
-------------------------------------
-- Remove_Extraneous_Decimal_Digit --
-------------------------------------
procedure Remove_Extraneous_Decimal_Digit
(Textual_Rep : in out String;
First_Non_Blank : in out Positive)
is
Point_Position : constant Positive := Ada.Strings.Fixed.Index
(Textual_Rep,
".",
First_Non_Blank);
Integral_Part_Needs_Increment : constant Boolean :=
Textual_Rep (Point_Position + 1) in '5' .. '9';
begin
Ada.Strings.Fixed.Delete
(Textual_Rep,
Point_Position,
Point_Position + 1,
Ada.Strings.Right);
First_Non_Blank := First_Non_Blank + 2;
if Integral_Part_Needs_Increment then
Increment_Integral_Part
(Textual_Rep,
First_Non_Blank,
Last_Digit_Position => Point_Position + 1);
end if;
end Remove_Extraneous_Decimal_Digit;
--------------------------
-- Trim_Fractional_Part --
--------------------------
procedure Trim_Fractional_Part
(Textual_Rep : in out String;
First_Non_Blank : in out Positive)
is
Cursor : Positive :=
Ada.Strings.Fixed.Index (Textual_Rep, ".", First_Non_Blank);
First_To_Trim : Positive;
Fractional_Part_Last : Positive;
begin
while Cursor + 1 <= Textual_Rep'Last
and then Textual_Rep (Cursor + 1) in '0' .. '9' loop
Cursor := Cursor + 1;
end loop;
Fractional_Part_Last := Cursor;
while Textual_Rep (Cursor) = '0' loop
Cursor := Cursor - 1;
end loop;
if Textual_Rep (Cursor) = '.' then
Cursor := Cursor - 1;
end if;
First_To_Trim := Cursor + 1;
Ada.Strings.Fixed.Delete
(Textual_Rep, First_To_Trim, Fractional_Part_Last, Ada.Strings.Right);
First_Non_Blank :=
First_Non_Blank + (Fractional_Part_Last - First_To_Trim + 1);
end Trim_Fractional_Part;
end GNAT.Formatted_String;