1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- A D A . T E X T _ I O . E D I T I N G --
9 -- Copyright (C) 1992-2007, 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 2, 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. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, --
20 -- Boston, MA 02110-1301, USA. --
22 -- As a special exception, if other files instantiate generics from this --
23 -- unit, or you link this unit with other files to produce an executable, --
24 -- this unit does not by itself cause the resulting executable to be --
25 -- covered by the GNU General Public License. This exception does not --
26 -- however invalidate any other reasons why the executable file might be --
27 -- covered by the GNU Public License. --
29 -- GNAT was originally developed by the GNAT team at New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc. --
32 ------------------------------------------------------------------------------
34 with Ada.Strings.Fixed;
35 package body Ada.Text_IO.Editing is
37 package Strings renames Ada.Strings;
38 package Strings_Fixed renames Ada.Strings.Fixed;
39 package Text_IO renames Ada.Text_IO;
45 function Blank_When_Zero (Pic : Picture) return Boolean is
47 return Pic.Contents.Original_BWZ;
54 function Expand (Picture : String) return String is
55 Result : String (1 .. MAX_PICSIZE);
56 Picture_Index : Integer := Picture'First;
57 Result_Index : Integer := Result'First;
61 package Int_IO is new Ada.Text_IO.Integer_IO (Integer);
64 if Picture'Length < 1 then
68 if Picture (Picture'First) = '(' then
73 case Picture (Picture_Index) is
76 Int_IO.Get (Picture (Picture_Index + 1 .. Picture'Last),
79 if Picture (Last + 1) /= ')' then
83 -- In what follows note that one copy of the repeated
84 -- character has already been made, so a count of one is a
85 -- no-op, and a count of zero erases a character.
87 if Result_Index + Count - 2 > Result'Last then
91 for J in 2 .. Count loop
92 Result (Result_Index + J - 2) := Picture (Picture_Index - 1);
95 Result_Index := Result_Index + Count - 1;
97 -- Last + 1 was a ')' throw it away too
99 Picture_Index := Last + 2;
105 if Result_Index > Result'Last then
109 Result (Result_Index) := Picture (Picture_Index);
110 Picture_Index := Picture_Index + 1;
111 Result_Index := Result_Index + 1;
115 exit when Picture_Index > Picture'Last;
118 return Result (1 .. Result_Index - 1);
129 function Format_Number
130 (Pic : Format_Record;
132 Currency_Symbol : String;
133 Fill_Character : Character;
134 Separator_Character : Character;
135 Radix_Point : Character) return String
137 Attrs : Number_Attributes := Parse_Number_String (Number);
139 Rounded : String := Number;
141 Sign_Position : Integer := Pic.Sign_Position; -- may float.
143 Answer : String (1 .. Pic.Picture.Length) := Pic.Picture.Expanded;
145 Currency_Pos : Integer := Pic.Start_Currency;
146 In_Currency : Boolean := False;
148 Dollar : Boolean := False;
149 -- Overridden immediately if necessary
151 Zero : Boolean := True;
152 -- Set to False when a non-zero digit is output
156 -- If the picture has fewer decimal places than the number, the image
157 -- must be rounded according to the usual rules.
159 if Attrs.Has_Fraction then
161 R : constant Integer :=
162 (Attrs.End_Of_Fraction - Attrs.Start_Of_Fraction + 1)
163 - Pic.Max_Trailing_Digits;
168 R_Pos := Attrs.End_Of_Fraction - R;
170 if Rounded (R_Pos + 1) > '4' then
172 if Rounded (R_Pos) = '.' then
176 if Rounded (R_Pos) /= '9' then
177 Rounded (R_Pos) := Character'Succ (Rounded (R_Pos));
179 Rounded (R_Pos) := '0';
183 if Rounded (R_Pos) = '.' then
187 if Rounded (R_Pos) /= '9' then
188 Rounded (R_Pos) := Character'Succ (Rounded (R_Pos));
191 Rounded (R_Pos) := '0';
196 -- The rounding may add a digit in front. Either the
197 -- leading blank or the sign (already captured) can
201 Rounded (R_Pos) := '1';
202 Attrs.Start_Of_Int := Attrs.Start_Of_Int - 1;
210 if Pic.Start_Currency /= Invalid_Position then
211 Dollar := Answer (Pic.Start_Currency) = '$';
214 -- Fix up "direct inserts" outside the playing field. Set up as one
215 -- loop to do the beginning, one (reverse) loop to do the end.
219 exit when Last = Pic.Start_Float;
220 exit when Last = Pic.Radix_Position;
221 exit when Answer (Last) = '9';
223 case Answer (Last) is
226 Answer (Last) := Separator_Character;
229 Answer (Last) := ' ';
236 exit when Last = Answer'Last;
241 -- Now for the end...
243 for J in reverse Last .. Answer'Last loop
244 exit when J = Pic.Radix_Position;
246 -- Do this test First, Separator_Character can equal Pic.Floater
248 if Answer (J) = Pic.Floater then
255 Answer (J) := Separator_Character;
271 if Pic.Start_Currency /= -1
272 and then Answer (Pic.Start_Currency) = '#'
273 and then Pic.Floater /= '#'
275 if Currency_Symbol'Length >
276 Pic.End_Currency - Pic.Start_Currency + 1
280 elsif Currency_Symbol'Length =
281 Pic.End_Currency - Pic.Start_Currency + 1
283 Answer (Pic.Start_Currency .. Pic.End_Currency) :=
286 elsif Pic.Radix_Position = Invalid_Position
287 or else Pic.Start_Currency < Pic.Radix_Position
289 Answer (Pic.Start_Currency .. Pic.End_Currency) :=
291 Answer (Pic.End_Currency - Currency_Symbol'Length + 1 ..
292 Pic.End_Currency) := Currency_Symbol;
295 Answer (Pic.Start_Currency .. Pic.End_Currency) :=
297 Answer (Pic.Start_Currency ..
298 Pic.Start_Currency + Currency_Symbol'Length - 1) :=
303 -- Fill in leading digits
305 if Attrs.End_Of_Int - Attrs.Start_Of_Int + 1 >
306 Pic.Max_Leading_Digits
308 raise Ada.Text_IO.Layout_Error;
311 if Pic.Radix_Position = Invalid_Position then
312 Position := Answer'Last;
314 Position := Pic.Radix_Position - 1;
317 for J in reverse Attrs.Start_Of_Int .. Attrs.End_Of_Int loop
319 while Answer (Position) /= '9'
320 and Answer (Position) /= Pic.Floater
322 if Answer (Position) = '_' then
323 Answer (Position) := Separator_Character;
325 elsif Answer (Position) = 'b' then
326 Answer (Position) := ' ';
329 Position := Position - 1;
332 Answer (Position) := Rounded (J);
334 if Rounded (J) /= '0' then
338 Position := Position - 1;
343 if Pic.Start_Float = Invalid_Position then
345 -- No leading floats, but need to change '9' to '0', '_' to
346 -- Separator_Character and 'b' to ' '.
348 for J in Last .. Position loop
350 -- Last set when fixing the "uninteresting" leaders above.
351 -- Don't duplicate the work.
353 if Answer (J) = '9' then
356 elsif Answer (J) = '_' then
357 Answer (J) := Separator_Character;
359 elsif Answer (J) = 'b' then
364 elsif Pic.Floater = '<'
370 for J in Pic.End_Float .. Position loop -- May be null range.
371 if Answer (J) = '9' then
374 elsif Answer (J) = '_' then
375 Answer (J) := Separator_Character;
377 elsif Answer (J) = 'b' then
382 if Position > Pic.End_Float then
383 Position := Pic.End_Float;
386 for J in Pic.Start_Float .. Position - 1 loop
390 Answer (Position) := Pic.Floater;
391 Sign_Position := Position;
393 elsif Pic.Floater = '$' then
395 for J in Pic.End_Float .. Position loop -- May be null range.
396 if Answer (J) = '9' then
399 elsif Answer (J) = '_' then
400 Answer (J) := ' '; -- no separators before leftmost digit.
402 elsif Answer (J) = 'b' then
407 if Position > Pic.End_Float then
408 Position := Pic.End_Float;
411 for J in Pic.Start_Float .. Position - 1 loop
415 Answer (Position) := Pic.Floater;
416 Currency_Pos := Position;
418 elsif Pic.Floater = '*' then
420 for J in Pic.End_Float .. Position loop -- May be null range.
421 if Answer (J) = '9' then
424 elsif Answer (J) = '_' then
425 Answer (J) := Separator_Character;
427 elsif Answer (J) = 'b' then
428 Answer (J) := Fill_Character;
432 if Position > Pic.End_Float then
433 Position := Pic.End_Float;
436 for J in Pic.Start_Float .. Position loop
437 Answer (J) := Fill_Character;
441 if Pic.Floater = '#' then
442 Currency_Pos := Currency_Symbol'Length;
446 for J in reverse Pic.Start_Float .. Position loop
450 Answer (J) := Fill_Character;
453 if In_Currency and then Currency_Pos > 0 then
454 Answer (J) := Currency_Symbol (Currency_Pos);
455 Currency_Pos := Currency_Pos - 1;
466 when '.' | 'V' | 'v' | '<' | '$' | '+' | '-' =>
470 if Currency_Pos = 0 then
473 Answer (J) := Currency_Symbol (Currency_Pos);
474 Currency_Pos := Currency_Pos - 1;
482 Answer (J) := Fill_Character;
488 if Currency_Pos = 0 then
492 Answer (J) := Currency_Symbol (Currency_Pos);
493 Currency_Pos := Currency_Pos - 1;
507 if Pic.Floater = '#' and then Currency_Pos /= 0 then
508 raise Ada.Text_IO.Layout_Error;
514 if Sign_Position = Invalid_Position then
515 if Attrs.Negative then
516 raise Ada.Text_IO.Layout_Error;
520 if Attrs.Negative then
521 case Answer (Sign_Position) is
522 when 'C' | 'D' | '-' =>
526 Answer (Sign_Position) := '-';
529 Answer (Sign_Position) := '(';
530 Answer (Pic.Second_Sign) := ')';
539 case Answer (Sign_Position) is
542 Answer (Sign_Position) := ' ';
544 when '<' | 'C' | 'D' =>
545 Answer (Sign_Position) := ' ';
546 Answer (Pic.Second_Sign) := ' ';
558 -- Fill in trailing digits
560 if Pic.Max_Trailing_Digits > 0 then
562 if Attrs.Has_Fraction then
563 Position := Attrs.Start_Of_Fraction;
564 Last := Pic.Radix_Position + 1;
566 for J in Last .. Answer'Last loop
568 if Answer (J) = '9' or Answer (J) = Pic.Floater then
569 Answer (J) := Rounded (Position);
571 if Rounded (Position) /= '0' then
575 Position := Position + 1;
578 -- Used up fraction but remember place in Answer
580 exit when Position > Attrs.End_Of_Fraction;
582 elsif Answer (J) = 'b' then
585 elsif Answer (J) = '_' then
586 Answer (J) := Separator_Character;
596 Position := Pic.Radix_Position + 1;
599 -- Now fill remaining 9's with zeros and _ with separators
603 for J in Position .. Last loop
604 if Answer (J) = '9' then
607 elsif Answer (J) = Pic.Floater then
610 elsif Answer (J) = '_' then
611 Answer (J) := Separator_Character;
613 elsif Answer (J) = 'b' then
619 Position := Last + 1;
622 if Pic.Floater = '#' and then Currency_Pos /= 0 then
623 raise Ada.Text_IO.Layout_Error;
626 -- No trailing digits, but now J may need to stick in a currency
629 if Pic.Start_Currency = Invalid_Position then
630 Position := Answer'Last + 1;
632 Position := Pic.Start_Currency;
636 for J in Position .. Answer'Last loop
638 if Pic.Start_Currency /= Invalid_Position and then
639 Answer (Pic.Start_Currency) = '#' then
645 Answer (J) := Fill_Character;
649 Answer (J) := Currency_Symbol (Currency_Pos);
650 Currency_Pos := Currency_Pos + 1;
652 if Currency_Pos > Currency_Symbol'Length then
653 In_Currency := False;
658 if Currency_Pos > Currency_Symbol'Length then
663 Answer (J) := Currency_Symbol (Currency_Pos);
664 Currency_Pos := Currency_Pos + 1;
666 if Currency_Pos > Currency_Symbol'Length then
667 In_Currency := False;
672 Answer (J) := Currency_Symbol (Currency_Pos);
673 Currency_Pos := Currency_Pos + 1;
678 Answer (J) := Fill_Character;
684 if Currency_Pos > Currency_Symbol'Length then
687 Answer (J) := Currency_Symbol (Currency_Pos);
688 Currency_Pos := Currency_Pos + 1;
702 -- Now get rid of Blank_when_Zero and complete Star fill
704 if Zero and Pic.Blank_When_Zero then
706 -- Value is zero, and blank it
711 Last := Last - 1 + Currency_Symbol'Length;
714 if Pic.Radix_Position /= Invalid_Position and then
715 Answer (Pic.Radix_Position) = 'V' then
719 return String'(1 .. Last => ' ');
721 elsif Zero and Pic.Star_Fill then
725 Last := Last - 1 + Currency_Symbol'Length;
728 if Pic.Radix_Position /= Invalid_Position then
730 if Answer (Pic.Radix_Position) = 'V' then
734 if Pic.Radix_Position > Pic.Start_Currency then
735 return String'(1 .. Pic.Radix_Position - 1 => '*') &
737 String'(Pic.Radix_Position + 1 .. Last => '*');
743 Pic.Radix_Position + Currency_Symbol'Length - 2 =>
746 (Pic.Radix_Position + Currency_Symbol'Length .. Last
751 return String'(1 .. Pic.Radix_Position - 1 => '*') &
753 String'(Pic.Radix_Position + 1 .. Last => '*');
757 return String'(1 .. Last => '*');
760 -- This was once a simple return statement, now there are nine
761 -- different return cases. Not to mention the five above to deal
762 -- with zeros. Why not split things out?
764 -- Processing the radix and sign expansion separately
765 -- would require lots of copying--the string and some of its
766 -- indicies--without really simplifying the logic. The cases are:
768 -- 1) Expand $, replace '.' with Radix_Point
769 -- 2) No currency expansion, replace '.' with Radix_Point
770 -- 3) Expand $, radix blanked
771 -- 4) No currency expansion, radix blanked
773 -- 6) Expand $, Elide V
774 -- 7) Elide V, Expand $ (Two cases depending on order.)
775 -- 8) No radix, expand $
776 -- 9) No radix, no currency expansion
778 if Pic.Radix_Position /= Invalid_Position then
780 if Answer (Pic.Radix_Position) = '.' then
781 Answer (Pic.Radix_Position) := Radix_Point;
785 -- 1) Expand $, replace '.' with Radix_Point
787 return Answer (1 .. Currency_Pos - 1) & Currency_Symbol &
788 Answer (Currency_Pos + 1 .. Answer'Last);
791 -- 2) No currency expansion, replace '.' with Radix_Point
796 elsif Answer (Pic.Radix_Position) = ' ' then -- blanked radix.
799 -- 3) Expand $, radix blanked
801 return Answer (1 .. Currency_Pos - 1) & Currency_Symbol &
802 Answer (Currency_Pos + 1 .. Answer'Last);
805 -- 4) No expansion, radix blanked
817 return Answer (1 .. Pic.Radix_Position - 1) &
818 Answer (Pic.Radix_Position + 1 .. Answer'Last);
820 elsif Currency_Pos < Pic.Radix_Position then
822 -- 6) Expand $, Elide V
824 return Answer (1 .. Currency_Pos - 1) & Currency_Symbol &
825 Answer (Currency_Pos + 1 .. Pic.Radix_Position - 1) &
826 Answer (Pic.Radix_Position + 1 .. Answer'Last);
829 -- 7) Elide V, Expand $
831 return Answer (1 .. Pic.Radix_Position - 1) &
832 Answer (Pic.Radix_Position + 1 .. Currency_Pos - 1) &
834 Answer (Currency_Pos + 1 .. Answer'Last);
840 -- 8) No radix, expand $
842 return Answer (1 .. Currency_Pos - 1) & Currency_Symbol &
843 Answer (Currency_Pos + 1 .. Answer'Last);
846 -- 9) No radix, no currency expansion
852 -------------------------
853 -- Parse_Number_String --
854 -------------------------
856 function Parse_Number_String (Str : String) return Number_Attributes is
857 Answer : Number_Attributes;
860 for J in Str'Range loop
868 -- Decide if this is the start of a number.
869 -- If so, figure out which one...
871 if Answer.Has_Fraction then
872 Answer.End_Of_Fraction := J;
874 if Answer.Start_Of_Int = Invalid_Position then
876 Answer.Start_Of_Int := J;
878 Answer.End_Of_Int := J;
883 -- Only count a zero before the decimal point if it follows a
884 -- non-zero digit. After the decimal point, zeros will be
885 -- counted if followed by a non-zero digit.
887 if not Answer.Has_Fraction then
888 if Answer.Start_Of_Int /= Invalid_Position then
889 Answer.End_Of_Int := J;
897 Answer.Negative := True;
901 -- Close integer, start fraction
903 if Answer.Has_Fraction then
907 -- Two decimal points is a no-no
909 Answer.Has_Fraction := True;
910 Answer.End_Of_Fraction := J;
912 -- Could leave this at Invalid_Position, but this seems the
913 -- right way to indicate a null range...
915 Answer.Start_Of_Fraction := J + 1;
916 Answer.End_Of_Int := J - 1;
919 raise Picture_Error; -- can this happen? probably not!
923 if Answer.Start_Of_Int = Invalid_Position then
924 Answer.Start_Of_Int := Answer.End_Of_Int + 1;
927 -- No significant (integer) digits needs a null range
930 end Parse_Number_String;
936 -- The following ensures that we return B and not b being careful not
937 -- to break things which expect lower case b for blank. See CXF3A02.
939 function Pic_String (Pic : Picture) return String is
940 Temp : String (1 .. Pic.Contents.Picture.Length) :=
941 Pic.Contents.Picture.Expanded;
943 for J in Temp'Range loop
944 if Temp (J) = 'b' then
956 procedure Precalculate (Pic : in out Format_Record) is
957 Debug : constant Boolean := False;
958 -- Set True to generate debug output
960 Computed_BWZ : Boolean := True;
962 type Legality is (Okay, Reject);
964 State : Legality := Reject;
965 -- Start in reject, which will reject null strings
967 Index : Pic_Index := Pic.Picture.Expanded'First;
969 function At_End return Boolean;
970 pragma Inline (At_End);
972 procedure Set_State (L : Legality);
973 pragma Inline (Set_State);
975 function Look return Character;
976 pragma Inline (Look);
978 function Is_Insert return Boolean;
979 pragma Inline (Is_Insert);
982 pragma Inline (Skip);
984 procedure Debug_Start (Name : String);
985 pragma Inline (Debug_Start);
987 procedure Debug_Integer (Value : Integer; S : String);
988 pragma Inline (Debug_Integer);
990 procedure Trailing_Currency;
991 procedure Trailing_Bracket;
992 procedure Number_Fraction;
993 procedure Number_Completion;
994 procedure Number_Fraction_Or_Bracket;
995 procedure Number_Fraction_Or_Z_Fill;
996 procedure Zero_Suppression;
997 procedure Floating_Bracket;
998 procedure Number_Fraction_Or_Star_Fill;
999 procedure Star_Suppression;
1000 procedure Number_Fraction_Or_Dollar;
1001 procedure Leading_Dollar;
1002 procedure Number_Fraction_Or_Pound;
1003 procedure Leading_Pound;
1005 procedure Floating_Plus;
1006 procedure Floating_Minus;
1007 procedure Picture_Plus;
1008 procedure Picture_Minus;
1009 procedure Picture_Bracket;
1011 procedure Optional_RHS_Sign;
1012 procedure Picture_String;
1013 procedure Set_Debug;
1019 function At_End return Boolean is
1021 Debug_Start ("At_End");
1022 return Index > Pic.Picture.Length;
1029 -- Needed to have a procedure to pass to pragma Debug
1031 procedure Set_Debug is
1033 -- Uncomment this line and make Debug a variable to enable debug
1044 procedure Debug_Integer (Value : Integer; S : String) is
1045 use Ada.Text_IO; -- needed for >
1048 if Debug and then Value > 0 then
1049 if Ada.Text_IO.Col > 70 - S'Length then
1050 Ada.Text_IO.New_Line;
1053 Ada.Text_IO.Put (' ' & S & Integer'Image (Value) & ',');
1061 procedure Debug_Start (Name : String) is
1064 Ada.Text_IO.Put_Line (" In " & Name & '.');
1068 ----------------------
1069 -- Floating_Bracket --
1070 ----------------------
1072 -- Note that Floating_Bracket is only called with an acceptable
1073 -- prefix. But we don't set Okay, because we must end with a '>'.
1075 procedure Floating_Bracket is
1077 Debug_Start ("Floating_Bracket");
1079 -- Two different floats not allowed
1081 if Pic.Floater /= '!' and then Pic.Floater /= '<' then
1082 raise Picture_Error;
1088 Pic.End_Float := Index;
1089 Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
1091 -- First bracket wasn't counted...
1102 when '_' | '0' | '/' =>
1103 Pic.End_Float := Index;
1107 Pic.End_Float := Index;
1108 Pic.Picture.Expanded (Index) := 'b';
1112 Pic.End_Float := Index;
1113 Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
1125 when 'V' | 'v' | '.' =>
1126 Pic.Radix_Position := Index;
1128 Number_Fraction_Or_Bracket;
1135 end Floating_Bracket;
1137 --------------------
1138 -- Floating_Minus --
1139 --------------------
1141 procedure Floating_Minus is
1143 Debug_Start ("Floating_Minus");
1151 when '_' | '0' | '/' =>
1152 Pic.End_Float := Index;
1156 Pic.End_Float := Index;
1157 Pic.Picture.Expanded (Index) := 'b';
1161 Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
1162 Pic.End_Float := Index;
1169 when '.' | 'V' | 'v' =>
1170 Pic.Radix_Position := Index;
1173 while Is_Insert loop
1190 Pic.Max_Trailing_Digits :=
1191 Pic.Max_Trailing_Digits + 1;
1192 Pic.End_Float := Index;
1195 when '_' | '0' | '/' =>
1199 Pic.Picture.Expanded (Index) := 'b';
1224 procedure Floating_Plus is
1226 Debug_Start ("Floating_Plus");
1234 when '_' | '0' | '/' =>
1235 Pic.End_Float := Index;
1239 Pic.End_Float := Index;
1240 Pic.Picture.Expanded (Index) := 'b';
1244 Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
1245 Pic.End_Float := Index;
1252 when '.' | 'V' | 'v' =>
1253 Pic.Radix_Position := Index;
1256 while Is_Insert loop
1273 Pic.Max_Trailing_Digits :=
1274 Pic.Max_Trailing_Digits + 1;
1275 Pic.End_Float := Index;
1278 when '_' | '0' | '/' =>
1282 Pic.Picture.Expanded (Index) := 'b';
1308 function Is_Insert return Boolean is
1314 case Pic.Picture.Expanded (Index) is
1316 when '_' | '0' | '/' => return True;
1319 Pic.Picture.Expanded (Index) := 'b'; -- canonical
1322 when others => return False;
1326 --------------------
1327 -- Leading_Dollar --
1328 --------------------
1330 -- Note that Leading_Dollar can be called in either State.
1331 -- It will set state to Okay only if a 9 or (second) $
1334 -- Also notice the tricky bit with State and Zero_Suppression.
1335 -- Zero_Suppression is Picture_Error if a '$' or a '9' has been
1336 -- encountered, exactly the cases where State has been set.
1338 procedure Leading_Dollar is
1340 Debug_Start ("Leading_Dollar");
1342 -- Treat as a floating dollar, and unwind otherwise
1344 if Pic.Floater /= '!' and then Pic.Floater /= '$' then
1346 -- Two floats not allowed
1348 raise Picture_Error;
1354 Pic.Start_Currency := Index;
1355 Pic.End_Currency := Index;
1356 Pic.Start_Float := Index;
1357 Pic.End_Float := Index;
1359 -- Don't increment Pic.Max_Leading_Digits, we need one "real"
1371 when '_' | '0' | '/' =>
1372 Pic.End_Float := Index;
1375 -- A trailing insertion character is not part of the
1376 -- floating currency, so need to look ahead.
1379 Pic.End_Float := Pic.End_Float - 1;
1383 Pic.End_Float := Index;
1384 Pic.Picture.Expanded (Index) := 'b';
1388 Pic.Picture.Expanded (Index) := 'Z'; -- consistency
1390 if State = Okay then
1391 raise Picture_Error;
1393 -- Overwrite Floater and Start_Float
1396 Pic.Start_Float := Index;
1401 if State = Okay then
1402 raise Picture_Error;
1404 -- Overwrite Floater and Start_Float
1407 Pic.Start_Float := Index;
1412 Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
1413 Pic.End_Float := Index;
1414 Pic.End_Currency := Index;
1415 Set_State (Okay); Skip;
1418 if State /= Okay then
1420 Pic.Start_Float := Invalid_Position;
1421 Pic.End_Float := Invalid_Position;
1424 -- A single dollar does not a floating make
1429 when 'V' | 'v' | '.' =>
1430 if State /= Okay then
1432 Pic.Start_Float := Invalid_Position;
1433 Pic.End_Float := Invalid_Position;
1436 -- Only one dollar before the sign is okay, but doesn't
1439 Pic.Radix_Position := Index;
1441 Number_Fraction_Or_Dollar;
1455 -- This one is complex! A Leading_Pound can be fixed or floating,
1456 -- but in some cases the decision has to be deferred until we leave
1457 -- this procedure. Also note that Leading_Pound can be called in
1460 -- It will set state to Okay only if a 9 or (second) # is
1463 -- One Last note: In ambiguous cases, the currency is treated as
1464 -- floating unless there is only one '#'.
1466 procedure Leading_Pound is
1468 Inserts : Boolean := False;
1469 -- Set to True if a '_', '0', '/', 'B', or 'b' is encountered
1471 Must_Float : Boolean := False;
1472 -- Set to true if a '#' occurs after an insert
1475 Debug_Start ("Leading_Pound");
1477 -- Treat as a floating currency. If it isn't, this will be
1478 -- overwritten later.
1480 if Pic.Floater /= '!' and then Pic.Floater /= '#' then
1482 -- Two floats not allowed
1484 raise Picture_Error;
1490 Pic.Start_Currency := Index;
1491 Pic.End_Currency := Index;
1492 Pic.Start_Float := Index;
1493 Pic.End_Float := Index;
1495 -- Don't increment Pic.Max_Leading_Digits, we need one "real"
1498 Pic.Max_Currency_Digits := 1; -- we've seen one.
1509 when '_' | '0' | '/' =>
1510 Pic.End_Float := Index;
1515 Pic.Picture.Expanded (Index) := 'b';
1516 Pic.End_Float := Index;
1521 Pic.Picture.Expanded (Index) := 'Z'; -- consistency
1524 raise Picture_Error;
1526 Pic.Max_Leading_Digits := 0;
1528 -- Overwrite Floater and Start_Float
1531 Pic.Start_Float := Index;
1537 raise Picture_Error;
1539 Pic.Max_Leading_Digits := 0;
1541 -- Overwrite Floater and Start_Float
1543 Pic.Start_Float := Index;
1552 Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
1553 Pic.End_Float := Index;
1554 Pic.End_Currency := Index;
1559 if State /= Okay then
1561 -- A single '#' doesn't float
1564 Pic.Start_Float := Invalid_Position;
1565 Pic.End_Float := Invalid_Position;
1571 when 'V' | 'v' | '.' =>
1572 if State /= Okay then
1574 Pic.Start_Float := Invalid_Position;
1575 Pic.End_Float := Invalid_Position;
1578 -- Only one pound before the sign is okay, but doesn't
1581 Pic.Radix_Position := Index;
1583 Number_Fraction_Or_Pound;
1596 function Look return Character is
1599 raise Picture_Error;
1602 return Pic.Picture.Expanded (Index);
1611 Debug_Start ("Number");
1616 when '_' | '0' | '/' =>
1620 Pic.Picture.Expanded (Index) := 'b';
1624 Computed_BWZ := False;
1625 Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
1629 when '.' | 'V' | 'v' =>
1630 Pic.Radix_Position := Index;
1644 -- Will return in Okay state if a '9' was seen
1649 -----------------------
1650 -- Number_Completion --
1651 -----------------------
1653 procedure Number_Completion is
1655 Debug_Start ("Number_Completion");
1657 while not At_End loop
1660 when '_' | '0' | '/' =>
1664 Pic.Picture.Expanded (Index) := 'b';
1668 Computed_BWZ := False;
1669 Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
1673 when 'V' | 'v' | '.' =>
1674 Pic.Radix_Position := Index;
1683 end Number_Completion;
1685 ---------------------
1686 -- Number_Fraction --
1687 ---------------------
1689 procedure Number_Fraction is
1691 -- Note that number fraction can be called in either State.
1692 -- It will set state to Valid only if a 9 is encountered.
1694 Debug_Start ("Number_Fraction");
1702 when '_' | '0' | '/' =>
1706 Pic.Picture.Expanded (Index) := 'b';
1710 Computed_BWZ := False;
1711 Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1;
1712 Set_State (Okay); Skip;
1718 end Number_Fraction;
1720 --------------------------------
1721 -- Number_Fraction_Or_Bracket --
1722 --------------------------------
1724 procedure Number_Fraction_Or_Bracket is
1726 Debug_Start ("Number_Fraction_Or_Bracket");
1735 when '_' | '0' | '/' => Skip;
1738 Pic.Picture.Expanded (Index) := 'b';
1742 Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1;
1743 Pic.End_Float := Index;
1752 when '_' | '0' | '/' =>
1756 Pic.Picture.Expanded (Index) := 'b';
1760 Pic.Max_Trailing_Digits :=
1761 Pic.Max_Trailing_Digits + 1;
1762 Pic.End_Float := Index;
1775 end Number_Fraction_Or_Bracket;
1777 -------------------------------
1778 -- Number_Fraction_Or_Dollar --
1779 -------------------------------
1781 procedure Number_Fraction_Or_Dollar is
1783 Debug_Start ("Number_Fraction_Or_Dollar");
1791 when '_' | '0' | '/' =>
1795 Pic.Picture.Expanded (Index) := 'b';
1799 Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1;
1800 Pic.End_Float := Index;
1809 when '_' | '0' | '/' =>
1813 Pic.Picture.Expanded (Index) := 'b';
1817 Pic.Max_Trailing_Digits :=
1818 Pic.Max_Trailing_Digits + 1;
1819 Pic.End_Float := Index;
1832 end Number_Fraction_Or_Dollar;
1834 ------------------------------
1835 -- Number_Fraction_Or_Pound --
1836 ------------------------------
1838 procedure Number_Fraction_Or_Pound is
1847 when '_' | '0' | '/' =>
1851 Pic.Picture.Expanded (Index) := 'b';
1855 Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1;
1856 Pic.End_Float := Index;
1866 when '_' | '0' | '/' =>
1870 Pic.Picture.Expanded (Index) := 'b';
1874 Pic.Max_Trailing_Digits :=
1875 Pic.Max_Trailing_Digits + 1;
1876 Pic.End_Float := Index;
1891 end Number_Fraction_Or_Pound;
1893 ----------------------------------
1894 -- Number_Fraction_Or_Star_Fill --
1895 ----------------------------------
1897 procedure Number_Fraction_Or_Star_Fill is
1899 Debug_Start ("Number_Fraction_Or_Star_Fill");
1908 when '_' | '0' | '/' =>
1912 Pic.Picture.Expanded (Index) := 'b';
1916 Pic.Star_Fill := True;
1917 Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1;
1918 Pic.End_Float := Index;
1928 when '_' | '0' | '/' =>
1932 Pic.Picture.Expanded (Index) := 'b';
1936 Pic.Star_Fill := True;
1937 Pic.Max_Trailing_Digits :=
1938 Pic.Max_Trailing_Digits + 1;
1939 Pic.End_Float := Index;
1953 end Number_Fraction_Or_Star_Fill;
1955 -------------------------------
1956 -- Number_Fraction_Or_Z_Fill --
1957 -------------------------------
1959 procedure Number_Fraction_Or_Z_Fill is
1961 Debug_Start ("Number_Fraction_Or_Z_Fill");
1970 when '_' | '0' | '/' =>
1974 Pic.Picture.Expanded (Index) := 'b';
1978 Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1;
1979 Pic.End_Float := Index;
1980 Pic.Picture.Expanded (Index) := 'Z'; -- consistency
1991 when '_' | '0' | '/' =>
1995 Pic.Picture.Expanded (Index) := 'b';
1999 Pic.Picture.Expanded (Index) := 'Z'; -- consistency
2001 Pic.Max_Trailing_Digits :=
2002 Pic.Max_Trailing_Digits + 1;
2003 Pic.End_Float := Index;
2016 end Number_Fraction_Or_Z_Fill;
2018 -----------------------
2019 -- Optional_RHS_Sign --
2020 -----------------------
2022 procedure Optional_RHS_Sign is
2024 Debug_Start ("Optional_RHS_Sign");
2033 Pic.Sign_Position := Index;
2038 Pic.Sign_Position := Index;
2039 Pic.Picture.Expanded (Index) := 'C';
2042 if Look = 'R' or Look = 'r' then
2043 Pic.Second_Sign := Index;
2044 Pic.Picture.Expanded (Index) := 'R';
2048 raise Picture_Error;
2054 Pic.Sign_Position := Index;
2055 Pic.Picture.Expanded (Index) := 'D';
2058 if Look = 'B' or Look = 'b' then
2059 Pic.Second_Sign := Index;
2060 Pic.Picture.Expanded (Index) := 'B';
2064 raise Picture_Error;
2070 if Pic.Picture.Expanded (Pic.Sign_Position) = '<' then
2071 Pic.Second_Sign := Index;
2075 raise Picture_Error;
2082 end Optional_RHS_Sign;
2088 -- Note that Picture can be called in either State
2090 -- It will set state to Valid only if a 9 is encountered or floating
2091 -- currency is called.
2093 procedure Picture is
2095 Debug_Start ("Picture");
2104 when '_' | '0' | '/' =>
2108 Pic.Picture.Expanded (Index) := 'b';
2120 Computed_BWZ := False;
2122 Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
2125 when 'V' | 'v' | '.' =>
2126 Pic.Radix_Position := Index;
2139 ---------------------
2140 -- Picture_Bracket --
2141 ---------------------
2143 procedure Picture_Bracket is
2145 Pic.Sign_Position := Index;
2146 Debug_Start ("Picture_Bracket");
2147 Pic.Sign_Position := Index;
2149 -- Treat as a floating sign, and unwind otherwise
2152 Pic.Start_Float := Index;
2153 Pic.End_Float := Index;
2155 -- Don't increment Pic.Max_Leading_Digits, we need one "real"
2158 Skip; -- Known Bracket
2163 when '_' | '0' | '/' =>
2164 Pic.End_Float := Index;
2168 Pic.End_Float := Index;
2169 Pic.Picture.Expanded (Index) := 'b';
2173 Set_State (Okay); -- "<<>" is enough.
2179 when '$' | '#' | '9' | '*' =>
2180 if State /= Okay then
2182 Pic.Start_Float := Invalid_Position;
2183 Pic.End_Float := Invalid_Position;
2191 when '.' | 'V' | 'v' =>
2192 if State /= Okay then
2194 Pic.Start_Float := Invalid_Position;
2195 Pic.End_Float := Invalid_Position;
2198 -- Don't assume that state is okay, haven't seen a digit
2205 raise Picture_Error;
2209 end Picture_Bracket;
2215 procedure Picture_Minus is
2217 Debug_Start ("Picture_Minus");
2219 Pic.Sign_Position := Index;
2221 -- Treat as a floating sign, and unwind otherwise
2224 Pic.Start_Float := Index;
2225 Pic.End_Float := Index;
2227 -- Don't increment Pic.Max_Leading_Digits, we need one "real"
2230 Skip; -- Known Minus
2235 when '_' | '0' | '/' =>
2236 Pic.End_Float := Index;
2240 Pic.End_Float := Index;
2241 Pic.Picture.Expanded (Index) := 'b';
2245 Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
2246 Pic.End_Float := Index;
2248 Set_State (Okay); -- "-- " is enough.
2253 when '$' | '#' | '9' | '*' =>
2254 if State /= Okay then
2256 Pic.Start_Float := Invalid_Position;
2257 Pic.End_Float := Invalid_Position;
2266 -- Can't have Z and a floating sign
2268 if State = Okay then
2272 Pic.Picture.Expanded (Index) := 'Z'; -- consistency
2278 when '.' | 'V' | 'v' =>
2279 if State /= Okay then
2281 Pic.Start_Float := Invalid_Position;
2282 Pic.End_Float := Invalid_Position;
2285 -- Don't assume that state is okay, haven't seen a digit
2301 procedure Picture_Plus is
2303 Debug_Start ("Picture_Plus");
2304 Pic.Sign_Position := Index;
2306 -- Treat as a floating sign, and unwind otherwise
2309 Pic.Start_Float := Index;
2310 Pic.End_Float := Index;
2312 -- Don't increment Pic.Max_Leading_Digits, we need one "real"
2320 when '_' | '0' | '/' =>
2321 Pic.End_Float := Index;
2325 Pic.End_Float := Index;
2326 Pic.Picture.Expanded (Index) := 'b';
2330 Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
2331 Pic.End_Float := Index;
2333 Set_State (Okay); -- "++" is enough
2338 when '$' | '#' | '9' | '*' =>
2339 if State /= Okay then
2341 Pic.Start_Float := Invalid_Position;
2342 Pic.End_Float := Invalid_Position;
2350 if State = Okay then
2354 -- Can't have Z and a floating sign
2356 Pic.Picture.Expanded (Index) := 'Z'; -- consistency
2358 -- '+Z' is acceptable
2362 -- Overwrite Floater and Start_Float
2365 Pic.Start_Float := Index;
2372 when '.' | 'V' | 'v' =>
2373 if State /= Okay then
2375 Pic.Start_Float := Invalid_Position;
2376 Pic.End_Float := Invalid_Position;
2379 -- Don't assume that state is okay, haven't seen a digit
2391 --------------------
2392 -- Picture_String --
2393 --------------------
2395 procedure Picture_String is
2397 Debug_Start ("Picture_String");
2399 while Is_Insert loop
2419 Pic.Picture.Expanded (Index) := 'Z'; -- consistency
2429 when '9' | '.' | 'V' | 'v' =>
2435 raise Picture_Error;
2439 -- Blank when zero either if the PIC does not contain a '9' or if
2440 -- requested by the user and no '*'.
2442 Pic.Blank_When_Zero :=
2443 (Computed_BWZ or Pic.Blank_When_Zero) and not Pic.Star_Fill;
2445 -- Star fill if '*' and no '9'
2447 Pic.Star_Fill := Pic.Star_Fill and Computed_BWZ;
2459 procedure Set_State (L : Legality) is
2462 Ada.Text_IO.Put_Line
2463 (" Set state from " & Legality'Image (State)
2464 & " to " & Legality'Image (L));
2477 Ada.Text_IO.Put_Line (" Skip " & Pic.Picture.Expanded (Index));
2483 ----------------------
2484 -- Star_Suppression --
2485 ----------------------
2487 procedure Star_Suppression is
2489 Debug_Start ("Star_Suppression");
2491 if Pic.Floater /= '!' and then Pic.Floater /= '*' then
2493 -- Two floats not allowed
2495 raise Picture_Error;
2501 Pic.Start_Float := Index;
2502 Pic.End_Float := Index;
2503 Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
2506 -- Even a single * is a valid picture
2508 Pic.Star_Fill := True;
2518 when '_' | '0' | '/' =>
2519 Pic.End_Float := Index;
2523 Pic.End_Float := Index;
2524 Pic.Picture.Expanded (Index) := 'b';
2528 Pic.End_Float := Index;
2529 Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
2530 Set_State (Okay); Skip;
2537 when '.' | 'V' | 'v' =>
2538 Pic.Radix_Position := Index;
2540 Number_Fraction_Or_Star_Fill;
2544 if Pic.Max_Currency_Digits > 0 then
2545 raise Picture_Error;
2548 -- Cannot have leading and trailing currency
2554 when others => raise Picture_Error;
2557 end Star_Suppression;
2559 ----------------------
2560 -- Trailing_Bracket --
2561 ----------------------
2563 procedure Trailing_Bracket is
2565 Debug_Start ("Trailing_Bracket");
2568 Pic.Second_Sign := Index;
2571 raise Picture_Error;
2573 end Trailing_Bracket;
2575 -----------------------
2576 -- Trailing_Currency --
2577 -----------------------
2579 procedure Trailing_Currency is
2581 Debug_Start ("Trailing_Currency");
2588 Pic.Start_Currency := Index;
2589 Pic.End_Currency := Index;
2593 while not At_End and then Look = '#' loop
2594 if Pic.Start_Currency = Invalid_Position then
2595 Pic.Start_Currency := Index;
2598 Pic.End_Currency := Index;
2609 when '_' | '0' | '/' => Skip;
2612 Pic.Picture.Expanded (Index) := 'b';
2615 when others => return;
2618 end Trailing_Currency;
2620 ----------------------
2621 -- Zero_Suppression --
2622 ----------------------
2624 procedure Zero_Suppression is
2626 Debug_Start ("Zero_Suppression");
2629 Pic.Start_Float := Index;
2630 Pic.End_Float := Index;
2631 Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
2632 Pic.Picture.Expanded (Index) := 'Z'; -- consistency
2637 -- Even a single Z is a valid picture
2645 when '_' | '0' | '/' =>
2646 Pic.End_Float := Index;
2650 Pic.End_Float := Index;
2651 Pic.Picture.Expanded (Index) := 'b';
2655 Pic.Picture.Expanded (Index) := 'Z'; -- consistency
2657 Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
2658 Pic.End_Float := Index;
2667 when '.' | 'V' | 'v' =>
2668 Pic.Radix_Position := Index;
2670 Number_Fraction_Or_Z_Fill;
2682 end Zero_Suppression;
2684 -- Start of processing for Precalculate
2687 pragma Debug (Set_Debug);
2692 Ada.Text_IO.New_Line;
2693 Ada.Text_IO.Put (" Picture : """ &
2694 Pic.Picture.Expanded (1 .. Pic.Picture.Length) & """,");
2695 Ada.Text_IO.Put (" Floater : '" & Pic.Floater & "',");
2698 if State = Reject then
2699 raise Picture_Error;
2702 Debug_Integer (Pic.Radix_Position, "Radix Positon : ");
2703 Debug_Integer (Pic.Sign_Position, "Sign Positon : ");
2704 Debug_Integer (Pic.Second_Sign, "Second Sign : ");
2705 Debug_Integer (Pic.Start_Float, "Start Float : ");
2706 Debug_Integer (Pic.End_Float, "End Float : ");
2707 Debug_Integer (Pic.Start_Currency, "Start Currency : ");
2708 Debug_Integer (Pic.End_Currency, "End Currency : ");
2709 Debug_Integer (Pic.Max_Leading_Digits, "Max Leading Digits : ");
2710 Debug_Integer (Pic.Max_Trailing_Digits, "Max Trailing Digits : ");
2713 Ada.Text_IO.New_Line;
2718 when Constraint_Error =>
2720 -- To deal with special cases like null strings
2722 raise Picture_Error;
2730 (Pic_String : String;
2731 Blank_When_Zero : Boolean := False) return Picture
2737 Item : constant String := Expand (Pic_String);
2740 Result.Contents.Picture := (Item'Length, Item);
2741 Result.Contents.Original_BWZ := Blank_When_Zero;
2742 Result.Contents.Blank_When_Zero := Blank_When_Zero;
2743 Precalculate (Result.Contents);
2749 raise Picture_Error;
2757 (Pic_String : String;
2758 Blank_When_Zero : Boolean := False) return Boolean
2762 Expanded_Pic : constant String := Expand (Pic_String);
2763 -- Raises Picture_Error if Item not well-formed
2765 Format_Rec : Format_Record;
2768 Format_Rec.Picture := (Expanded_Pic'Length, Expanded_Pic);
2769 Format_Rec.Blank_When_Zero := Blank_When_Zero;
2770 Format_Rec.Original_BWZ := Blank_When_Zero;
2771 Precalculate (Format_Rec);
2773 -- False only if Blank_When_Zero is True but the pic string has a '*'
2775 return not Blank_When_Zero
2776 or else Strings_Fixed.Index (Expanded_Pic, "*") = 0;
2780 when others => return False;
2783 --------------------
2784 -- Decimal_Output --
2785 --------------------
2787 package body Decimal_Output is
2796 Currency : String := Default_Currency;
2797 Fill : Character := Default_Fill;
2798 Separator : Character := Default_Separator;
2799 Radix_Mark : Character := Default_Radix_Mark) return String
2802 return Format_Number
2803 (Pic.Contents, Num'Image (Item),
2804 Currency, Fill, Separator, Radix_Mark);
2813 Currency : String := Default_Currency) return Natural
2815 Picstr : constant String := Pic_String (Pic);
2816 V_Adjust : Integer := 0;
2817 Cur_Adjust : Integer := 0;
2820 -- Check if Picstr has 'V' or '$'
2822 -- If 'V', then length is 1 less than otherwise
2824 -- If '$', then length is Currency'Length-1 more than otherwise
2826 -- This should use the string handling package ???
2828 for J in Picstr'Range loop
2829 if Picstr (J) = 'V' then
2832 elsif Picstr (J) = '$' then
2833 Cur_Adjust := Currency'Length - 1;
2837 return Picstr'Length - V_Adjust + Cur_Adjust;
2845 (File : Text_IO.File_Type;
2848 Currency : String := Default_Currency;
2849 Fill : Character := Default_Fill;
2850 Separator : Character := Default_Separator;
2851 Radix_Mark : Character := Default_Radix_Mark)
2854 Text_IO.Put (File, Image (Item, Pic,
2855 Currency, Fill, Separator, Radix_Mark));
2861 Currency : String := Default_Currency;
2862 Fill : Character := Default_Fill;
2863 Separator : Character := Default_Separator;
2864 Radix_Mark : Character := Default_Radix_Mark)
2867 Text_IO.Put (Image (Item, Pic,
2868 Currency, Fill, Separator, Radix_Mark));
2875 Currency : String := Default_Currency;
2876 Fill : Character := Default_Fill;
2877 Separator : Character := Default_Separator;
2878 Radix_Mark : Character := Default_Radix_Mark)
2880 Result : constant String :=
2881 Image (Item, Pic, Currency, Fill, Separator, Radix_Mark);
2884 if Result'Length > To'Length then
2885 raise Ada.Text_IO.Layout_Error;
2887 Strings_Fixed.Move (Source => Result, Target => To,
2888 Justify => Strings.Right);
2899 Currency : String := Default_Currency) return Boolean
2903 Temp : constant String := Image (Item, Pic, Currency);
2904 pragma Warnings (Off, Temp);
2910 when Ada.Text_IO.Layout_Error => return False;
2915 end Ada.Text_IO.Editing;