1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
5 -- G N A T . F O R M A T T E D _ S T R I N G --
9 -- Copyright (C) 2014, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 with Ada
.Characters
.Handling
;
33 with Ada
.Float_Text_IO
;
34 with Ada
.Integer_Text_IO
;
35 with Ada
.Long_Float_Text_IO
;
36 with Ada
.Long_Integer_Text_IO
;
37 with Ada
.Strings
.Fixed
;
38 with Ada
.Unchecked_Deallocation
;
40 with System
.Address_Image
;
42 package body GNAT
.Formatted_String
is
44 type F_Kind
is (Decimal_Int
, -- %d %i
45 Unsigned_Decimal_Int
, -- %u
47 Unsigned_Hexadecimal_Int
, -- %x
48 Unsigned_Hexadecimal_Int_Up
, -- %X
49 Decimal_Float
, -- %f %F
50 Decimal_Scientific_Float
, -- %e
51 Decimal_Scientific_Float_Up
, -- %E
52 Shortest_Decimal_Float
, -- %g
53 Shortest_Decimal_Float_Up
, -- %G
59 type Sign_Kind
is (Neg
, Zero
, Pos
);
61 subtype Is_Number
is F_Kind
range Decimal_Int
.. Decimal_Float
;
63 type F_Sign
is (If_Neg
, Forced
, Space
) with Default_Value
=> If_Neg
;
65 type F_Base
is (None
, C_Style
, Ada_Style
) with Default_Value
=> None
;
67 Unset
: constant Integer := -1;
72 Precision
: Integer := Unset
;
73 Left_Justify
: Boolean := False;
76 Zero_Pad
: Boolean := False;
77 Value_Needed
: Natural range 0 .. 2 := 0;
81 (Format
: Formatted_String
;
83 Start
: out Positive);
84 -- Parse the next format specifier, a format specifier has the following
85 -- syntax: %[flags][width][.precision][length]specifier
87 function Get_Formatted
90 Len
: Positive) return String;
91 -- Returns Value formatted given the information in F_Spec
93 procedure Raise_Wrong_Format
(Format
: Formatted_String
) with No_Return
;
94 -- Raise the Format_Error exception which information about the context
103 Exp
: Text_IO
.Field
);
104 function P_Flt_Format
105 (Format
: Formatted_String
;
106 Var
: Flt
) return Formatted_String
;
107 -- Generic routine which handles all floating point numbers
112 with function To_Integer
(Item
: Int
) return Integer;
114 with function Sign
(Item
: Int
) return Sign_Kind
;
119 Base
: Text_IO
.Number_Base
);
120 function P_Int_Format
121 (Format
: Formatted_String
;
122 Var
: Int
) return Formatted_String
;
123 -- Generic routine which handles all the integer numbers
129 function "+" (Format
: String) return Formatted_String
is
131 return Formatted_String
'
132 (Finalization.Controlled with
133 D => new Data'(Format
'Length, 1, Format
, 1,
134 Null_Unbounded_String
, 0, 0, (0, 0)));
141 function "-" (Format
: Formatted_String
) return String is
142 F
: String renames Format
.D
.Format
;
143 J
: Natural renames Format
.D
.Index
;
144 R
: Unbounded_String
:= Format
.D
.Result
;
147 -- Make sure we get the remaining character up to the next unhandled
150 while (J
<= F
'Length and then F
(J
) /= '%')
151 or else (J
< F
'Length - 1 and then F
(J
+ 1) = '%')
155 -- If we have two consecutive %, skip the second one
157 if F
(J
) = '%' and then J
< F
'Length - 1 and then F
(J
+ 1) = '%' then
164 return To_String
(R
);
172 (Format
: Formatted_String
;
173 Var
: Character) return Formatted_String
179 Next_Format
(Format
, F
, Start
);
181 if F
.Value_Needed
> 0 then
182 Raise_Wrong_Format
(Format
);
187 Append
(Format
.D
.Result
, Get_Formatted
(F
, String'(1 => Var), 1));
189 Raise_Wrong_Format (Format);
196 (Format : Formatted_String;
197 Var : String) return Formatted_String
203 Next_Format (Format, F, Start);
205 if F.Value_Needed > 0 then
206 Raise_Wrong_Format (Format);
212 S : constant String := Get_Formatted (F, Var, Var'Length);
214 if F.Precision = Unset then
215 Append (Format.D.Result, S);
219 S (S'First .. S'First + F.Precision - 1));
224 Raise_Wrong_Format (Format);
231 (Format : Formatted_String;
232 Var : Boolean) return Formatted_String is
234 return Format & Boolean'Image (Var);
238 (Format : Formatted_String;
239 Var : Float) return Formatted_String
241 function Float_Format is new Flt_Format (Float, Float_Text_IO.Put);
243 return Float_Format (Format, Var);
247 (Format : Formatted_String;
248 Var : Long_Float) return Formatted_String
250 function Float_Format is
251 new Flt_Format (Long_Float, Long_Float_Text_IO.Put);
253 return Float_Format (Format, Var);
257 (Format : Formatted_String;
258 Var : Duration) return Formatted_String
260 package Duration_Text_IO is new Text_IO.Fixed_IO (Duration);
261 function Duration_Format is
262 new P_Flt_Format (Duration, Duration_Text_IO.Put);
264 return Duration_Format (Format, Var);
268 (Format : Formatted_String;
269 Var : Integer) return Formatted_String
271 function Integer_Format is
272 new Int_Format (Integer, Integer_Text_IO.Put);
274 return Integer_Format (Format, Var);
278 (Format : Formatted_String;
279 Var : Long_Integer) return Formatted_String
281 function Integer_Format is
282 new Int_Format (Long_Integer, Long_Integer_Text_IO.Put);
284 return Integer_Format (Format, Var);
288 (Format : Formatted_String;
289 Var : System.Address) return Formatted_String
291 A_Img : constant String := System.Address_Image (Var);
296 Next_Format (Format, F, Start);
298 if F.Value_Needed > 0 then
299 Raise_Wrong_Format (Format);
304 Append (Format.D.Result, Get_Formatted (F, A_Img, A_Img'Length));
306 Raise_Wrong_Format (Format);
316 overriding procedure Adjust (F : in out Formatted_String) is
318 F.D.Ref_Count := F.D.Ref_Count + 1;
325 function Decimal_Format
326 (Format : Formatted_String;
327 Var : Flt) return Formatted_String
329 function Flt_Format is new P_Flt_Format (Flt, Put);
331 return Flt_Format (Format, Var);
339 (Format : Formatted_String;
340 Var : Enum) return Formatted_String is
342 return Format & Enum'Image (Var);
349 overriding procedure Finalize (F : in out Formatted_String) is
350 procedure Unchecked_Free is
351 new Unchecked_Deallocation (Data, Data_Access);
353 D : Data_Access := F.D;
358 D.Ref_Count := D.Ref_Count - 1;
360 if D.Ref_Count = 0 then
369 function Fixed_Format
370 (Format : Formatted_String;
371 Var : Flt) return Formatted_String
373 function Flt_Format is new P_Flt_Format (Flt, Put);
375 return Flt_Format (Format, Var);
383 (Format : Formatted_String;
384 Var : Flt) return Formatted_String
386 function Flt_Format is new P_Flt_Format (Flt, Put);
388 return Flt_Format (Format, Var);
395 function Get_Formatted
398 Len : Positive) return String
400 use Ada.Strings.Fixed;
402 Res : Unbounded_String;
403 S : Positive := Value'First;
408 if F_Spec.Kind in Is_Number then
409 if F_Spec.Sign = Forced and then Value (Value'First) /= '-' then
411 elsif F_Spec.Sign = Space and then Value (Value'First) /= '-' then
415 if Value (Value'First) = '-' then
421 -- Zero padding if required and possible
423 if F_Spec.Left_Justify = False
424 and then F_Spec.Zero_Pad
425 and then F_Spec.Width > Len + Value'First - S
427 Append (Res, String'((F_Spec
.Width
- Len
+ Value
'First - S
) * '0'));
432 Append
(Res
, Value
(S
.. Value
'Last));
435 R
: String (1 .. Natural'Max (Natural'Max (F_Spec
.Width
, Len
),
436 Length
(Res
))) := (others => ' ');
438 if F_Spec
.Left_Justify
then
439 R
(1 .. Length
(Res
)) := To_String
(Res
);
441 R
(R
'Last - Length
(Res
) + 1 .. R
'Last) := To_String
(Res
);
453 (Format
: Formatted_String
;
454 Var
: Int
) return Formatted_String
456 function Sign
(Var
: Int
) return Sign_Kind
is
457 (if Var
< 0 then Neg
elsif Var
= 0 then Zero
else Pos
);
459 function To_Integer
(Var
: Int
) return Integer is
462 function Int_Format
is new P_Int_Format
(Int
, To_Integer
, Sign
, Put
);
465 return Int_Format
(Format
, Var
);
473 (Format
: Formatted_String
;
474 Var
: Int
) return Formatted_String
476 function Sign
(Var
: Int
) return Sign_Kind
is
477 (if Var
< 0 then Neg
elsif Var
= 0 then Zero
else Pos
);
479 function To_Integer
(Var
: Int
) return Integer is
482 function Int_Format
is new P_Int_Format
(Int
, To_Integer
, Sign
, Put
);
485 return Int_Format
(Format
, Var
);
492 procedure Next_Format
493 (Format
: Formatted_String
;
495 Start
: out Positive)
497 F
: String renames Format
.D
.Format
;
498 J
: Natural renames Format
.D
.Index
;
500 Width_From_Var
: Boolean := False;
503 Format
.D
.Current
:= Format
.D
.Current
+ 1;
504 F_Spec
.Value_Needed
:= 0;
508 while (J
<= F
'Last and then F
(J
) /= '%')
509 or else (J
< F
'Last - 1 and then F
(J
+ 1) = '%')
511 Append
(Format
.D
.Result
, F
(J
));
513 -- If we have two consecutive %, skip the second one
515 if F
(J
) = '%' and then J
< F
'Last - 1 and then F
(J
+ 1) = '%' then
522 if F
(J
) /= '%' or else J
= F
'Last then
523 raise Format_Error
with "no format specifier found for parameter"
524 & Positive'Image (Format
.D
.Current
);
531 -- Check for any flags
533 Flags_Check
: while J
< F
'Last loop
535 F_Spec
.Left_Justify
:= True;
536 elsif F
(J
) = '+' then
537 F_Spec
.Sign
:= Forced
;
538 elsif F
(J
) = ' ' then
539 F_Spec
.Sign
:= Space
;
540 elsif F
(J
) = '#' then
541 F_Spec
.Base
:= C_Style
;
542 elsif F
(J
) = '~' then
543 F_Spec
.Base
:= Ada_Style
;
544 elsif F
(J
) = '0' then
545 F_Spec
.Zero_Pad
:= True;
551 end loop Flags_Check
;
553 -- Check width if any
555 if F
(J
) in '0' .. '9' then
557 -- We have a width parameter
561 while J
< F
'Last and then F
(J
+ 1) in '0' .. '9' loop
565 F_Spec
.Width
:= Natural'Value (F
(S
.. J
));
569 elsif F
(J
) = '*' then
571 -- The width will be taken from the integer parameter
573 F_Spec
.Value_Needed
:= 1;
574 Width_From_Var
:= True;
581 -- We have a precision parameter
585 if F
(J
) in '0' .. '9' then
588 while J
< F
'Length and then F
(J
+ 1) in '0' .. '9' loop
594 -- No precision, 0 is assumed
596 F_Spec
.Precision
:= 0;
599 F_Spec
.Precision
:= Natural'Value (F
(S
.. J
));
604 elsif F
(J
) = '*' then
606 -- The prevision will be taken from the integer parameter
608 F_Spec
.Value_Needed
:= F_Spec
.Value_Needed
+ 1;
613 -- Skip the length specifier, this is not needed for this implementation
614 -- but yet for compatibility reason it is handled.
618 and then F
(J
) in 'h' |
'l' |
'j' |
'z' |
't' |
'L'
621 end loop Length_Check
;
624 Raise_Wrong_Format
(Format
);
627 -- Read next character which should be the expected type
630 when 'c' => F_Spec
.Kind
:= Char
;
631 when 's' => F_Spec
.Kind
:= Str
;
632 when 'd' |
'i' => F_Spec
.Kind
:= Decimal_Int
;
633 when 'u' => F_Spec
.Kind
:= Unsigned_Decimal_Int
;
634 when 'f' |
'F' => F_Spec
.Kind
:= Decimal_Float
;
635 when 'e' => F_Spec
.Kind
:= Decimal_Scientific_Float
;
636 when 'E' => F_Spec
.Kind
:= Decimal_Scientific_Float_Up
;
637 when 'g' => F_Spec
.Kind
:= Shortest_Decimal_Float
;
638 when 'G' => F_Spec
.Kind
:= Shortest_Decimal_Float_Up
;
639 when 'o' => F_Spec
.Kind
:= Unsigned_Octal
;
640 when 'x' => F_Spec
.Kind
:= Unsigned_Hexadecimal_Int
;
641 when 'X' => F_Spec
.Kind
:= Unsigned_Hexadecimal_Int_Up
;
644 raise Format_Error
with "unknown format specified for parameter"
645 & Positive'Image (Format
.D
.Current
);
650 if F_Spec
.Value_Needed
> 0
651 and then F_Spec
.Value_Needed
= Format
.D
.Stored_Value
653 if F_Spec
.Value_Needed
= 1 then
654 if Width_From_Var
then
655 F_Spec
.Width
:= Format
.D
.Stack
(1);
657 F_Spec
.Precision
:= Format
.D
.Stack
(1);
661 F_Spec
.Width
:= Format
.D
.Stack
(1);
662 F_Spec
.Precision
:= Format
.D
.Stack
(2);
671 function P_Flt_Format
672 (Format
: Formatted_String
;
673 Var
: Flt
) return Formatted_String
676 Buffer
: String (1 .. 50);
677 S
, E
: Positive := 1;
682 Next_Format
(Format
, F
, Start
);
684 if F
.Value_Needed
> 0 then
685 Raise_Wrong_Format
(Format
);
688 if F
.Precision
= Unset
then
695 when Decimal_Float
=>
697 Put
(Buffer
, Var
, Aft
, Exp
=> 0);
698 S
:= Strings
.Fixed
.Index_Non_Blank
(Buffer
);
701 when Decimal_Scientific_Float | Decimal_Scientific_Float_Up
=>
703 Put
(Buffer
, Var
, Aft
, Exp
=> 3);
704 S
:= Strings
.Fixed
.Index_Non_Blank
(Buffer
);
707 if F
.Kind
= Decimal_Scientific_Float
then
709 Characters
.Handling
.To_Lower
(Buffer
(S
.. E
));
712 when Shortest_Decimal_Float | Shortest_Decimal_Float_Up
=>
716 Put
(Buffer
, Var
, Aft
, Exp
=> 0);
717 S
:= Strings
.Fixed
.Index_Non_Blank
(Buffer
);
720 -- Check with exponent
723 Buffer2
: String (1 .. 50);
727 Put
(Buffer2
, Var
, Aft
, Exp
=> 3);
728 S2
:= Strings
.Fixed
.Index_Non_Blank
(Buffer2
);
731 -- If with exponent it is shorter, use it
733 if (E2
- S2
) < (E
- S
) then
740 if F
.Kind
= Shortest_Decimal_Float
then
742 Characters
.Handling
.To_Lower
(Buffer
(S
.. E
));
746 Raise_Wrong_Format
(Format
);
749 Append
(Format
.D
.Result
,
750 Get_Formatted
(F
, Buffer
(S
.. E
), Buffer
(S
.. E
)'Length));
759 function P_Int_Format
760 (Format
: Formatted_String
;
761 Var
: Int
) return Formatted_String
763 function Handle_Precision
return Boolean;
764 -- Return True if nothing else to do
767 Buffer
: String (1 .. 50);
768 S
, E
: Positive := 1;
772 ----------------------
773 -- Handle_Precision --
774 ----------------------
776 function Handle_Precision
return Boolean is
778 if F
.Precision
= 0 and then Sign
(Var
) = Zero
then
781 elsif F
.Precision
= Natural'Last then
784 elsif F
.Precision
> E
- S
+ 1 then
785 Len
:= F
.Precision
- (E
- S
+ 1);
786 Buffer
(S
- Len
.. S
- 1) := (others => '0');
791 end Handle_Precision
;
793 -- Start of processing for P_Int_Format
796 Next_Format
(Format
, F
, Start
);
798 if Format
.D
.Stored_Value
< F
.Value_Needed
then
799 Format
.D
.Stored_Value
:= Format
.D
.Stored_Value
+ 1;
800 Format
.D
.Stack
(Format
.D
.Stored_Value
) := To_Integer
(Var
);
801 Format
.D
.Index
:= Start
;
806 when Unsigned_Octal
=>
807 if Sign
(Var
) = Neg
then
808 Raise_Wrong_Format
(Format
);
811 Put
(Buffer
, Var
, Base
=> 8);
812 S
:= Strings
.Fixed
.Index
(Buffer
, "8#") + 2;
813 E
:= Strings
.Fixed
.Index
(Buffer
(S
.. Buffer
'Last), "#") - 1;
815 if Handle_Precision
then
821 when C_Style
=> Len
:= 1;
822 when Ada_Style
=> Len
:= 3;
825 when Unsigned_Hexadecimal_Int
=>
826 if Sign
(Var
) = Neg
then
827 Raise_Wrong_Format
(Format
);
830 Put
(Buffer
, Var
, Base
=> 16);
831 S
:= Strings
.Fixed
.Index
(Buffer
, "16#") + 3;
832 E
:= Strings
.Fixed
.Index
(Buffer
(S
.. Buffer
'Last), "#") - 1;
833 Buffer
(S
.. E
) := Characters
.Handling
.To_Lower
(Buffer
(S
.. E
));
835 if Handle_Precision
then
841 when C_Style
=> Len
:= 2;
842 when Ada_Style
=> Len
:= 4;
845 when Unsigned_Hexadecimal_Int_Up
=>
846 if Sign
(Var
) = Neg
then
847 Raise_Wrong_Format
(Format
);
850 Put
(Buffer
, Var
, Base
=> 16);
851 S
:= Strings
.Fixed
.Index
(Buffer
, "16#") + 3;
852 E
:= Strings
.Fixed
.Index
(Buffer
(S
.. Buffer
'Last), "#") - 1;
854 if Handle_Precision
then
860 when C_Style
=> Len
:= 2;
861 when Ada_Style
=> Len
:= 4;
864 when Unsigned_Decimal_Int
=>
865 if Sign
(Var
) = Neg
then
866 Raise_Wrong_Format
(Format
);
869 Put
(Buffer
, Var
, Base
=> 10);
870 S
:= Strings
.Fixed
.Index_Non_Blank
(Buffer
);
873 if Handle_Precision
then
878 Put
(Buffer
, Var
, Base
=> 10);
879 S
:= Strings
.Fixed
.Index_Non_Blank
(Buffer
);
882 if Handle_Precision
then
889 Buffer
(S
) := Character'Val (To_Integer
(Var
));
891 if Handle_Precision
then
896 Raise_Wrong_Format
(Format
);
899 -- Then add base if needed
902 N
: String := Get_Formatted
(F
, Buffer
(S
.. E
), E
- S
+ 1 + Len
);
903 P
: constant Positive :=
906 else Natural'Max (Strings
.Fixed
.Index_Non_Blank
(N
) - 1,
915 when Unsigned_Octal
=>
918 when Unsigned_Hexadecimal_Int
=>
919 if F
.Left_Justify
then
920 N
(P
.. P
+ 1) := "Ox";
922 N
(P
- 1 .. P
) := "0x";
925 when Unsigned_Hexadecimal_Int_Up
=>
926 if F
.Left_Justify
then
927 N
(P
.. P
+ 1) := "OX";
929 N
(P
- 1 .. P
) := "0X";
938 when Unsigned_Octal
=>
939 if F
.Left_Justify
then
940 N
(N
'First + 2 .. N
'Last) := N
(N
'First .. N
'Last - 2);
942 N
(P
.. N
'Last - 1) := N
(P
+ 1 .. N
'Last);
945 N
(N
'First .. N
'First + 1) := "8#";
948 when Unsigned_Hexadecimal_Int |
949 Unsigned_Hexadecimal_Int_Up
=>
950 if F
.Left_Justify
then
951 N
(N
'First + 3 .. N
'Last) := N
(N
'First .. N
'Last - 3);
953 N
(P
.. N
'Last - 1) := N
(P
+ 1 .. N
'Last);
956 N
(N
'First .. N
'First + 2) := "16#";
964 Append
(Format
.D
.Result
, N
);
970 ------------------------
971 -- Raise_Wrong_Format --
972 ------------------------
974 procedure Raise_Wrong_Format
(Format
: Formatted_String
) is
976 raise Format_Error
with
977 "wrong format specified for parameter"
978 & Positive'Image (Format
.D
.Current
);
979 end Raise_Wrong_Format
;
981 end GNAT
.Formatted_String
;