------------------------------------------------------------------------------ -- -- -- GNAT RUN-TIME COMPONENTS -- -- -- -- A D A . T E X T _ I O . E D I T I N G -- -- -- -- B o d y -- -- -- -- Copyright (C) 1992-2016, 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 -- -- . -- -- -- -- GNAT was originally developed by the GNAT team at New York University. -- -- Extensive contributions were provided by Ada Core Technologies Inc. -- -- -- ------------------------------------------------------------------------------ with Ada.Strings.Fixed; package body Ada.Text_IO.Editing is package Strings renames Ada.Strings; package Strings_Fixed renames Ada.Strings.Fixed; package Text_IO renames Ada.Text_IO; --------------------- -- Blank_When_Zero -- --------------------- function Blank_When_Zero (Pic : Picture) return Boolean is begin return Pic.Contents.Original_BWZ; end Blank_When_Zero; ------------ -- Expand -- ------------ function Expand (Picture : String) return String is Result : String (1 .. MAX_PICSIZE); Picture_Index : Integer := Picture'First; Result_Index : Integer := Result'First; Count : Natural; Last : Integer; package Int_IO is new Ada.Text_IO.Integer_IO (Integer); begin if Picture'Length < 1 then raise Picture_Error; end if; if Picture (Picture'First) = '(' then raise Picture_Error; end if; loop case Picture (Picture_Index) is when '(' => Int_IO.Get (Picture (Picture_Index + 1 .. Picture'Last), Count, Last); if Picture (Last + 1) /= ')' then raise Picture_Error; end if; -- In what follows note that one copy of the repeated character -- has already been made, so a count of one is a no-op, and a -- count of zero erases a character. if Result_Index + Count - 2 > Result'Last then raise Picture_Error; end if; for J in 2 .. Count loop Result (Result_Index + J - 2) := Picture (Picture_Index - 1); end loop; Result_Index := Result_Index + Count - 1; -- Last + 1 was a ')' throw it away too Picture_Index := Last + 2; when ')' => raise Picture_Error; when others => if Result_Index > Result'Last then raise Picture_Error; end if; Result (Result_Index) := Picture (Picture_Index); Picture_Index := Picture_Index + 1; Result_Index := Result_Index + 1; end case; exit when Picture_Index > Picture'Last; end loop; return Result (1 .. Result_Index - 1); exception when others => raise Picture_Error; end Expand; ------------------- -- Format_Number -- ------------------- function Format_Number (Pic : Format_Record; Number : String; Currency_Symbol : String; Fill_Character : Character; Separator_Character : Character; Radix_Point : Character) return String is Attrs : Number_Attributes := Parse_Number_String (Number); Position : Integer; Rounded : String := Number; Sign_Position : Integer := Pic.Sign_Position; -- may float. Answer : String (1 .. Pic.Picture.Length) := Pic.Picture.Expanded; Last : Integer; Currency_Pos : Integer := Pic.Start_Currency; In_Currency : Boolean := False; Dollar : Boolean := False; -- Overridden immediately if necessary Zero : Boolean := True; -- Set to False when a non-zero digit is output begin -- If the picture has fewer decimal places than the number, the image -- must be rounded according to the usual rules. if Attrs.Has_Fraction then declare R : constant Integer := (Attrs.End_Of_Fraction - Attrs.Start_Of_Fraction + 1) - Pic.Max_Trailing_Digits; R_Pos : Integer; begin if R > 0 then R_Pos := Attrs.End_Of_Fraction - R; if Rounded (R_Pos + 1) > '4' then if Rounded (R_Pos) = '.' then R_Pos := R_Pos - 1; end if; if Rounded (R_Pos) /= '9' then Rounded (R_Pos) := Character'Succ (Rounded (R_Pos)); else Rounded (R_Pos) := '0'; R_Pos := R_Pos - 1; while R_Pos > 1 loop if Rounded (R_Pos) = '.' then R_Pos := R_Pos - 1; end if; if Rounded (R_Pos) /= '9' then Rounded (R_Pos) := Character'Succ (Rounded (R_Pos)); exit; else Rounded (R_Pos) := '0'; R_Pos := R_Pos - 1; end if; end loop; -- The rounding may add a digit in front. Either the -- leading blank or the sign (already captured) can -- be overwritten. if R_Pos = 1 then Rounded (R_Pos) := '1'; Attrs.Start_Of_Int := Attrs.Start_Of_Int - 1; end if; end if; end if; end if; end; end if; if Pic.Start_Currency /= Invalid_Position then Dollar := Answer (Pic.Start_Currency) = '$'; end if; -- Fix up "direct inserts" outside the playing field. Set up as one -- loop to do the beginning, one (reverse) loop to do the end. Last := 1; loop exit when Last = Pic.Start_Float; exit when Last = Pic.Radix_Position; exit when Answer (Last) = '9'; case Answer (Last) is when '_' => Answer (Last) := Separator_Character; when 'b' => Answer (Last) := ' '; when others => null; end case; exit when Last = Answer'Last; Last := Last + 1; end loop; -- Now for the end... for J in reverse Last .. Answer'Last loop exit when J = Pic.Radix_Position; -- Do this test First, Separator_Character can equal Pic.Floater if Answer (J) = Pic.Floater then exit; end if; case Answer (J) is when '_' => Answer (J) := Separator_Character; when 'b' => Answer (J) := ' '; when '9' => exit; when others => null; end case; end loop; -- Non-floating sign if Pic.Start_Currency /= -1 and then Answer (Pic.Start_Currency) = '#' and then Pic.Floater /= '#' then if Currency_Symbol'Length > Pic.End_Currency - Pic.Start_Currency + 1 then raise Picture_Error; elsif Currency_Symbol'Length = Pic.End_Currency - Pic.Start_Currency + 1 then Answer (Pic.Start_Currency .. Pic.End_Currency) := Currency_Symbol; elsif Pic.Radix_Position = Invalid_Position or else Pic.Start_Currency < Pic.Radix_Position then Answer (Pic.Start_Currency .. Pic.End_Currency) := (others => ' '); Answer (Pic.End_Currency - Currency_Symbol'Length + 1 .. Pic.End_Currency) := Currency_Symbol; else Answer (Pic.Start_Currency .. Pic.End_Currency) := (others => ' '); Answer (Pic.Start_Currency .. Pic.Start_Currency + Currency_Symbol'Length - 1) := Currency_Symbol; end if; end if; -- Fill in leading digits if Attrs.End_Of_Int - Attrs.Start_Of_Int + 1 > Pic.Max_Leading_Digits then raise Ada.Text_IO.Layout_Error; end if; Position := (if Pic.Radix_Position = Invalid_Position then Answer'Last else Pic.Radix_Position - 1); for J in reverse Attrs.Start_Of_Int .. Attrs.End_Of_Int loop while Answer (Position) /= '9' and then Answer (Position) /= Pic.Floater loop if Answer (Position) = '_' then Answer (Position) := Separator_Character; elsif Answer (Position) = 'b' then Answer (Position) := ' '; end if; Position := Position - 1; end loop; Answer (Position) := Rounded (J); if Rounded (J) /= '0' then Zero := False; end if; Position := Position - 1; end loop; -- Do lead float if Pic.Start_Float = Invalid_Position then -- No leading floats, but need to change '9' to '0', '_' to -- Separator_Character and 'b' to ' '. for J in Last .. Position loop -- Last set when fixing the "uninteresting" leaders above. -- Don't duplicate the work. if Answer (J) = '9' then Answer (J) := '0'; elsif Answer (J) = '_' then Answer (J) := Separator_Character; elsif Answer (J) = 'b' then Answer (J) := ' '; end if; end loop; elsif Pic.Floater = '<' or else Pic.Floater = '+' or else Pic.Floater = '-' then for J in Pic.End_Float .. Position loop -- May be null range. if Answer (J) = '9' then Answer (J) := '0'; elsif Answer (J) = '_' then Answer (J) := Separator_Character; elsif Answer (J) = 'b' then Answer (J) := ' '; end if; end loop; if Position > Pic.End_Float then Position := Pic.End_Float; end if; for J in Pic.Start_Float .. Position - 1 loop Answer (J) := ' '; end loop; Answer (Position) := Pic.Floater; Sign_Position := Position; elsif Pic.Floater = '$' then for J in Pic.End_Float .. Position loop -- May be null range. if Answer (J) = '9' then Answer (J) := '0'; elsif Answer (J) = '_' then Answer (J) := ' '; -- no separators before leftmost digit. elsif Answer (J) = 'b' then Answer (J) := ' '; end if; end loop; if Position > Pic.End_Float then Position := Pic.End_Float; end if; for J in Pic.Start_Float .. Position - 1 loop Answer (J) := ' '; end loop; Answer (Position) := Pic.Floater; Currency_Pos := Position; elsif Pic.Floater = '*' then for J in Pic.End_Float .. Position loop -- May be null range. if Answer (J) = '9' then Answer (J) := '0'; elsif Answer (J) = '_' then Answer (J) := Separator_Character; elsif Answer (J) = 'b' then Answer (J) := Fill_Character; end if; end loop; if Position > Pic.End_Float then Position := Pic.End_Float; end if; for J in Pic.Start_Float .. Position loop Answer (J) := Fill_Character; end loop; else if Pic.Floater = '#' then Currency_Pos := Currency_Symbol'Length; In_Currency := True; end if; for J in reverse Pic.Start_Float .. Position loop case Answer (J) is when '*' => Answer (J) := Fill_Character; when 'b' | '/' => if In_Currency and then Currency_Pos > 0 then Answer (J) := Currency_Symbol (Currency_Pos); Currency_Pos := Currency_Pos - 1; else Answer (J) := ' '; end if; when 'Z' | '0' => Answer (J) := ' '; when '9' => Answer (J) := '0'; when '.' | 'V' | 'v' | '<' | '$' | '+' | '-' => null; when '#' => if Currency_Pos = 0 then Answer (J) := ' '; else Answer (J) := Currency_Symbol (Currency_Pos); Currency_Pos := Currency_Pos - 1; end if; when '_' => case Pic.Floater is when '*' => Answer (J) := Fill_Character; when 'Z' | 'b' => Answer (J) := ' '; when '#' => if Currency_Pos = 0 then Answer (J) := ' '; else Answer (J) := Currency_Symbol (Currency_Pos); Currency_Pos := Currency_Pos - 1; end if; when others => null; end case; when others => null; end case; end loop; if Pic.Floater = '#' and then Currency_Pos /= 0 then raise Ada.Text_IO.Layout_Error; end if; end if; -- Do sign if Sign_Position = Invalid_Position then if Attrs.Negative then raise Ada.Text_IO.Layout_Error; end if; else if Attrs.Negative then case Answer (Sign_Position) is when 'C' | 'D' | '-' => null; when '+' => Answer (Sign_Position) := '-'; when '<' => Answer (Sign_Position) := '('; Answer (Pic.Second_Sign) := ')'; when others => raise Picture_Error; end case; else -- positive case Answer (Sign_Position) is when '-' => Answer (Sign_Position) := ' '; when '<' | 'C' | 'D' => Answer (Sign_Position) := ' '; Answer (Pic.Second_Sign) := ' '; when '+' => null; when others => raise Picture_Error; end case; end if; end if; -- Fill in trailing digits if Pic.Max_Trailing_Digits > 0 then if Attrs.Has_Fraction then Position := Attrs.Start_Of_Fraction; Last := Pic.Radix_Position + 1; for J in Last .. Answer'Last loop if Answer (J) = '9' or else Answer (J) = Pic.Floater then Answer (J) := Rounded (Position); if Rounded (Position) /= '0' then Zero := False; end if; Position := Position + 1; Last := J + 1; -- Used up fraction but remember place in Answer exit when Position > Attrs.End_Of_Fraction; elsif Answer (J) = 'b' then Answer (J) := ' '; elsif Answer (J) = '_' then Answer (J) := Separator_Character; end if; Last := J + 1; end loop; Position := Last; else Position := Pic.Radix_Position + 1; end if; -- Now fill remaining 9's with zeros and _ with separators Last := Answer'Last; for J in Position .. Last loop if Answer (J) = '9' then Answer (J) := '0'; elsif Answer (J) = Pic.Floater then Answer (J) := '0'; elsif Answer (J) = '_' then Answer (J) := Separator_Character; elsif Answer (J) = 'b' then Answer (J) := ' '; end if; end loop; Position := Last + 1; else if Pic.Floater = '#' and then Currency_Pos /= 0 then raise Ada.Text_IO.Layout_Error; end if; -- No trailing digits, but now J may need to stick in a currency -- symbol or sign. Position := (if Pic.Start_Currency = Invalid_Position then Answer'Last + 1 else Pic.Start_Currency); end if; for J in Position .. Answer'Last loop if Pic.Start_Currency /= Invalid_Position and then Answer (Pic.Start_Currency) = '#' then Currency_Pos := 1; end if; case Answer (J) is when '*' => Answer (J) := Fill_Character; when 'b' => if In_Currency then Answer (J) := Currency_Symbol (Currency_Pos); Currency_Pos := Currency_Pos + 1; if Currency_Pos > Currency_Symbol'Length then In_Currency := False; end if; end if; when '#' => if Currency_Pos > Currency_Symbol'Length then Answer (J) := ' '; else In_Currency := True; Answer (J) := Currency_Symbol (Currency_Pos); Currency_Pos := Currency_Pos + 1; if Currency_Pos > Currency_Symbol'Length then In_Currency := False; end if; end if; when '_' => Answer (J) := Currency_Symbol (Currency_Pos); Currency_Pos := Currency_Pos + 1; case Pic.Floater is when '*' => Answer (J) := Fill_Character; when 'Z' | 'z' => Answer (J) := ' '; when '#' => if Currency_Pos > Currency_Symbol'Length then Answer (J) := ' '; else Answer (J) := Currency_Symbol (Currency_Pos); Currency_Pos := Currency_Pos + 1; end if; when others => null; end case; when others => exit; end case; end loop; -- Now get rid of Blank_when_Zero and complete Star fill if Zero and then Pic.Blank_When_Zero then -- Value is zero, and blank it Last := Answer'Last; if Dollar then Last := Last - 1 + Currency_Symbol'Length; end if; if Pic.Radix_Position /= Invalid_Position and then Answer (Pic.Radix_Position) = 'V' then Last := Last - 1; end if; return String'(1 .. Last => ' '); elsif Zero and then Pic.Star_Fill then Last := Answer'Last; if Dollar then Last := Last - 1 + Currency_Symbol'Length; end if; if Pic.Radix_Position /= Invalid_Position then if Answer (Pic.Radix_Position) = 'V' then Last := Last - 1; elsif Dollar then if Pic.Radix_Position > Pic.Start_Currency then return String'(1 .. Pic.Radix_Position - 1 => '*') & Radix_Point & String'(Pic.Radix_Position + 1 .. Last => '*'); else return String' (1 .. Pic.Radix_Position + Currency_Symbol'Length - 2 => '*') & Radix_Point & String' (Pic.Radix_Position + Currency_Symbol'Length .. Last => '*'); end if; else return String'(1 .. Pic.Radix_Position - 1 => '*') & Radix_Point & String'(Pic.Radix_Position + 1 .. Last => '*'); end if; end if; return String'(1 .. Last => '*'); end if; -- This was once a simple return statement, now there are nine different -- return cases. Not to mention the five above to deal with zeros. Why -- not split things out? -- Processing the radix and sign expansion separately would require -- lots of copying--the string and some of its indexes--without -- really simplifying the logic. The cases are: -- 1) Expand $, replace '.' with Radix_Point -- 2) No currency expansion, replace '.' with Radix_Point -- 3) Expand $, radix blanked -- 4) No currency expansion, radix blanked -- 5) Elide V -- 6) Expand $, Elide V -- 7) Elide V, Expand $ (Two cases depending on order.) -- 8) No radix, expand $ -- 9) No radix, no currency expansion if Pic.Radix_Position /= Invalid_Position then if Answer (Pic.Radix_Position) = '.' then Answer (Pic.Radix_Position) := Radix_Point; if Dollar then -- 1) Expand $, replace '.' with Radix_Point return Answer (1 .. Currency_Pos - 1) & Currency_Symbol & Answer (Currency_Pos + 1 .. Answer'Last); else -- 2) No currency expansion, replace '.' with Radix_Point return Answer; end if; elsif Answer (Pic.Radix_Position) = ' ' then -- blanked radix. if Dollar then -- 3) Expand $, radix blanked return Answer (1 .. Currency_Pos - 1) & Currency_Symbol & Answer (Currency_Pos + 1 .. Answer'Last); else -- 4) No expansion, radix blanked return Answer; end if; -- V cases else if not Dollar then -- 5) Elide V return Answer (1 .. Pic.Radix_Position - 1) & Answer (Pic.Radix_Position + 1 .. Answer'Last); elsif Currency_Pos < Pic.Radix_Position then -- 6) Expand $, Elide V return Answer (1 .. Currency_Pos - 1) & Currency_Symbol & Answer (Currency_Pos + 1 .. Pic.Radix_Position - 1) & Answer (Pic.Radix_Position + 1 .. Answer'Last); else -- 7) Elide V, Expand $ return Answer (1 .. Pic.Radix_Position - 1) & Answer (Pic.Radix_Position + 1 .. Currency_Pos - 1) & Currency_Symbol & Answer (Currency_Pos + 1 .. Answer'Last); end if; end if; elsif Dollar then -- 8) No radix, expand $ return Answer (1 .. Currency_Pos - 1) & Currency_Symbol & Answer (Currency_Pos + 1 .. Answer'Last); else -- 9) No radix, no currency expansion return Answer; end if; end Format_Number; ------------------------- -- Parse_Number_String -- ------------------------- function Parse_Number_String (Str : String) return Number_Attributes is Answer : Number_Attributes; begin for J in Str'Range loop case Str (J) is when ' ' => null; -- ignore when '1' .. '9' => -- Decide if this is the start of a number. -- If so, figure out which one... if Answer.Has_Fraction then Answer.End_Of_Fraction := J; else if Answer.Start_Of_Int = Invalid_Position then -- start integer Answer.Start_Of_Int := J; end if; Answer.End_Of_Int := J; end if; when '0' => -- Only count a zero before the decimal point if it follows a -- non-zero digit. After the decimal point, zeros will be -- counted if followed by a non-zero digit. if not Answer.Has_Fraction then if Answer.Start_Of_Int /= Invalid_Position then Answer.End_Of_Int := J; end if; end if; when '-' => -- Set negative Answer.Negative := True; when '.' => -- Close integer, start fraction if Answer.Has_Fraction then raise Picture_Error; end if; -- Two decimal points is a no-no Answer.Has_Fraction := True; Answer.End_Of_Fraction := J; -- Could leave this at Invalid_Position, but this seems the -- right way to indicate a null range... Answer.Start_Of_Fraction := J + 1; Answer.End_Of_Int := J - 1; when others => raise Picture_Error; -- can this happen? probably not end case; end loop; if Answer.Start_Of_Int = Invalid_Position then Answer.Start_Of_Int := Answer.End_Of_Int + 1; end if; -- No significant (integer) digits needs a null range return Answer; end Parse_Number_String; ---------------- -- Pic_String -- ---------------- -- The following ensures that we return B and not b being careful not -- to break things which expect lower case b for blank. See CXF3A02. function Pic_String (Pic : Picture) return String is Temp : String (1 .. Pic.Contents.Picture.Length) := Pic.Contents.Picture.Expanded; begin for J in Temp'Range loop if Temp (J) = 'b' then Temp (J) := 'B'; end if; end loop; return Temp; end Pic_String; ------------------ -- Precalculate -- ------------------ procedure Precalculate (Pic : in out Format_Record) is Debug : constant Boolean := False; -- Set True to generate debug output Computed_BWZ : Boolean := True; type Legality is (Okay, Reject); State : Legality := Reject; -- Start in reject, which will reject null strings Index : Pic_Index := Pic.Picture.Expanded'First; function At_End return Boolean; pragma Inline (At_End); procedure Set_State (L : Legality); pragma Inline (Set_State); function Look return Character; pragma Inline (Look); function Is_Insert return Boolean; pragma Inline (Is_Insert); procedure Skip; pragma Inline (Skip); procedure Debug_Start (Name : String); pragma Inline (Debug_Start); procedure Debug_Integer (Value : Integer; S : String); pragma Inline (Debug_Integer); procedure Trailing_Currency; procedure Trailing_Bracket; procedure Number_Fraction; procedure Number_Completion; procedure Number_Fraction_Or_Bracket; procedure Number_Fraction_Or_Z_Fill; procedure Zero_Suppression; procedure Floating_Bracket; procedure Number_Fraction_Or_Star_Fill; procedure Star_Suppression; procedure Number_Fraction_Or_Dollar; procedure Leading_Dollar; procedure Number_Fraction_Or_Pound; procedure Leading_Pound; procedure Picture; procedure Floating_Plus; procedure Floating_Minus; procedure Picture_Plus; procedure Picture_Minus; procedure Picture_Bracket; procedure Number; procedure Optional_RHS_Sign; procedure Picture_String; procedure Set_Debug; ------------ -- At_End -- ------------ function At_End return Boolean is begin Debug_Start ("At_End"); return Index > Pic.Picture.Length; end At_End; -------------- -- Set_Debug-- -------------- -- Needed to have a procedure to pass to pragma Debug procedure Set_Debug is begin -- Uncomment this line and make Debug a variable to enable debug -- Debug := True; null; end Set_Debug; ------------------- -- Debug_Integer -- ------------------- procedure Debug_Integer (Value : Integer; S : String) is use Ada.Text_IO; -- needed for > begin if Debug and then Value > 0 then if Ada.Text_IO.Col > 70 - S'Length then Ada.Text_IO.New_Line; end if; Ada.Text_IO.Put (' ' & S & Integer'Image (Value) & ','); end if; end Debug_Integer; ----------------- -- Debug_Start -- ----------------- procedure Debug_Start (Name : String) is begin if Debug then Ada.Text_IO.Put_Line (" In " & Name & '.'); end if; end Debug_Start; ---------------------- -- Floating_Bracket -- ---------------------- -- Note that Floating_Bracket is only called with an acceptable -- prefix. But we don't set Okay, because we must end with a '>'. procedure Floating_Bracket is begin Debug_Start ("Floating_Bracket"); -- Two different floats not allowed if Pic.Floater /= '!' and then Pic.Floater /= '<' then raise Picture_Error; else Pic.Floater := '<'; end if; Pic.End_Float := Index; Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1; -- First bracket wasn't counted... Skip; -- known '<' loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Pic.End_Float := Index; Skip; when 'B' | 'b' => Pic.End_Float := Index; Pic.Picture.Expanded (Index) := 'b'; Skip; when '<' => Pic.End_Float := Index; Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1; Skip; when '9' => Number_Completion; when '$' => Leading_Dollar; when '#' => Leading_Pound; when 'V' | 'v' | '.' => Pic.Radix_Position := Index; Skip; Number_Fraction_Or_Bracket; return; when others => return; end case; end loop; end Floating_Bracket; -------------------- -- Floating_Minus -- -------------------- procedure Floating_Minus is begin Debug_Start ("Floating_Minus"); loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Pic.End_Float := Index; Skip; when 'B' | 'b' => Pic.End_Float := Index; Pic.Picture.Expanded (Index) := 'b'; Skip; when '-' => Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1; Pic.End_Float := Index; Skip; when '9' => Number_Completion; return; when '.' | 'V' | 'v' => Pic.Radix_Position := Index; Skip; -- Radix while Is_Insert loop Skip; end loop; if At_End then return; end if; if Look = '-' then loop if At_End then return; end if; case Look is when '-' => Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1; Pic.End_Float := Index; Skip; when '_' | '0' | '/' => Skip; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; Skip; when others => return; end case; end loop; else Number_Completion; end if; return; when others => return; end case; end loop; end Floating_Minus; ------------------- -- Floating_Plus -- ------------------- procedure Floating_Plus is begin Debug_Start ("Floating_Plus"); loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Pic.End_Float := Index; Skip; when 'B' | 'b' => Pic.End_Float := Index; Pic.Picture.Expanded (Index) := 'b'; Skip; when '+' => Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1; Pic.End_Float := Index; Skip; when '9' => Number_Completion; return; when '.' | 'V' | 'v' => Pic.Radix_Position := Index; Skip; -- Radix while Is_Insert loop Skip; end loop; if At_End then return; end if; if Look = '+' then loop if At_End then return; end if; case Look is when '+' => Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1; Pic.End_Float := Index; Skip; when '_' | '0' | '/' => Skip; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; Skip; when others => return; end case; end loop; else Number_Completion; end if; return; when others => return; end case; end loop; end Floating_Plus; --------------- -- Is_Insert -- --------------- function Is_Insert return Boolean is begin if At_End then return False; end if; case Pic.Picture.Expanded (Index) is when '_' | '0' | '/' => return True; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; -- canonical return True; when others => return False; end case; end Is_Insert; -------------------- -- Leading_Dollar -- -------------------- -- Note that Leading_Dollar can be called in either State. It will set -- state to Okay only if a 9 or (second) $ is encountered. -- Also notice the tricky bit with State and Zero_Suppression. -- Zero_Suppression is Picture_Error if a '$' or a '9' has been -- encountered, exactly the cases where State has been set. procedure Leading_Dollar is begin Debug_Start ("Leading_Dollar"); -- Treat as a floating dollar, and unwind otherwise if Pic.Floater /= '!' and then Pic.Floater /= '$' then -- Two floats not allowed raise Picture_Error; else Pic.Floater := '$'; end if; Pic.Start_Currency := Index; Pic.End_Currency := Index; Pic.Start_Float := Index; Pic.End_Float := Index; -- Don't increment Pic.Max_Leading_Digits, we need one "real" -- currency place. Skip; -- known '$' loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Pic.End_Float := Index; Skip; -- A trailing insertion character is not part of the -- floating currency, so need to look ahead. if Look /= '$' then Pic.End_Float := Pic.End_Float - 1; end if; when 'B' | 'b' => Pic.End_Float := Index; Pic.Picture.Expanded (Index) := 'b'; Skip; when 'Z' | 'z' => Pic.Picture.Expanded (Index) := 'Z'; -- consistency if State = Okay then raise Picture_Error; else -- Overwrite Floater and Start_Float Pic.Floater := 'Z'; Pic.Start_Float := Index; Zero_Suppression; end if; when '*' => if State = Okay then raise Picture_Error; else -- Overwrite Floater and Start_Float Pic.Floater := '*'; Pic.Start_Float := Index; Star_Suppression; end if; when '$' => Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1; Pic.End_Float := Index; Pic.End_Currency := Index; Set_State (Okay); Skip; when '9' => if State /= Okay then Pic.Floater := '!'; Pic.Start_Float := Invalid_Position; Pic.End_Float := Invalid_Position; end if; -- A single dollar does not a floating make Number_Completion; return; when 'V' | 'v' | '.' => if State /= Okay then Pic.Floater := '!'; Pic.Start_Float := Invalid_Position; Pic.End_Float := Invalid_Position; end if; -- Only one dollar before the sign is okay, but doesn't -- float. Pic.Radix_Position := Index; Skip; Number_Fraction_Or_Dollar; return; when others => return; end case; end loop; end Leading_Dollar; ------------------- -- Leading_Pound -- ------------------- -- This one is complex. A Leading_Pound can be fixed or floating, -- but in some cases the decision has to be deferred until we leave -- this procedure. Also note that Leading_Pound can be called in -- either State. -- It will set state to Okay only if a 9 or (second) # is encountered -- One Last note: In ambiguous cases, the currency is treated as -- floating unless there is only one '#'. procedure Leading_Pound is Inserts : Boolean := False; -- Set to True if a '_', '0', '/', 'B', or 'b' is encountered Must_Float : Boolean := False; -- Set to true if a '#' occurs after an insert begin Debug_Start ("Leading_Pound"); -- Treat as a floating currency. If it isn't, this will be -- overwritten later. if Pic.Floater /= '!' and then Pic.Floater /= '#' then -- Two floats not allowed raise Picture_Error; else Pic.Floater := '#'; end if; Pic.Start_Currency := Index; Pic.End_Currency := Index; Pic.Start_Float := Index; Pic.End_Float := Index; -- Don't increment Pic.Max_Leading_Digits, we need one "real" -- currency place. Pic.Max_Currency_Digits := 1; -- we've seen one. Skip; -- known '#' loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Pic.End_Float := Index; Inserts := True; Skip; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; Pic.End_Float := Index; Inserts := True; Skip; when 'Z' | 'z' => Pic.Picture.Expanded (Index) := 'Z'; -- consistency if Must_Float then raise Picture_Error; else Pic.Max_Leading_Digits := 0; -- Overwrite Floater and Start_Float Pic.Floater := 'Z'; Pic.Start_Float := Index; Zero_Suppression; end if; when '*' => if Must_Float then raise Picture_Error; else Pic.Max_Leading_Digits := 0; -- Overwrite Floater and Start_Float Pic.Floater := '*'; Pic.Start_Float := Index; Star_Suppression; end if; when '#' => if Inserts then Must_Float := True; end if; Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1; Pic.End_Float := Index; Pic.End_Currency := Index; Set_State (Okay); Skip; when '9' => if State /= Okay then -- A single '#' doesn't float Pic.Floater := '!'; Pic.Start_Float := Invalid_Position; Pic.End_Float := Invalid_Position; end if; Number_Completion; return; when 'V' | 'v' | '.' => if State /= Okay then Pic.Floater := '!'; Pic.Start_Float := Invalid_Position; Pic.End_Float := Invalid_Position; end if; -- Only one pound before the sign is okay, but doesn't -- float. Pic.Radix_Position := Index; Skip; Number_Fraction_Or_Pound; return; when others => return; end case; end loop; end Leading_Pound; ---------- -- Look -- ---------- function Look return Character is begin if At_End then raise Picture_Error; end if; return Pic.Picture.Expanded (Index); end Look; ------------ -- Number -- ------------ procedure Number is begin Debug_Start ("Number"); loop case Look is when '_' | '0' | '/' => Skip; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; Skip; when '9' => Computed_BWZ := False; Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1; Set_State (Okay); Skip; when '.' | 'V' | 'v' => Pic.Radix_Position := Index; Skip; Number_Fraction; return; when others => return; end case; if At_End then return; end if; -- Will return in Okay state if a '9' was seen end loop; end Number; ----------------------- -- Number_Completion -- ----------------------- procedure Number_Completion is begin Debug_Start ("Number_Completion"); while not At_End loop case Look is when '_' | '0' | '/' => Skip; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; Skip; when '9' => Computed_BWZ := False; Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1; Set_State (Okay); Skip; when 'V' | 'v' | '.' => Pic.Radix_Position := Index; Skip; Number_Fraction; return; when others => return; end case; end loop; end Number_Completion; --------------------- -- Number_Fraction -- --------------------- procedure Number_Fraction is begin -- Note that number fraction can be called in either State. -- It will set state to Valid only if a 9 is encountered. Debug_Start ("Number_Fraction"); loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Skip; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; Skip; when '9' => Computed_BWZ := False; Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1; Set_State (Okay); Skip; when others => return; end case; end loop; end Number_Fraction; -------------------------------- -- Number_Fraction_Or_Bracket -- -------------------------------- procedure Number_Fraction_Or_Bracket is begin Debug_Start ("Number_Fraction_Or_Bracket"); loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Skip; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; Skip; when '<' => Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1; Pic.End_Float := Index; Skip; loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Skip; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; Skip; when '<' => Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1; Pic.End_Float := Index; Skip; when others => return; end case; end loop; when others => Number_Fraction; return; end case; end loop; end Number_Fraction_Or_Bracket; ------------------------------- -- Number_Fraction_Or_Dollar -- ------------------------------- procedure Number_Fraction_Or_Dollar is begin Debug_Start ("Number_Fraction_Or_Dollar"); loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Skip; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; Skip; when '$' => Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1; Pic.End_Float := Index; Skip; loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Skip; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; Skip; when '$' => Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1; Pic.End_Float := Index; Skip; when others => return; end case; end loop; when others => Number_Fraction; return; end case; end loop; end Number_Fraction_Or_Dollar; ------------------------------ -- Number_Fraction_Or_Pound -- ------------------------------ procedure Number_Fraction_Or_Pound is begin loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Skip; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; Skip; when '#' => Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1; Pic.End_Float := Index; Skip; loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Skip; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; Skip; when '#' => Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1; Pic.End_Float := Index; Skip; when others => return; end case; end loop; when others => Number_Fraction; return; end case; end loop; end Number_Fraction_Or_Pound; ---------------------------------- -- Number_Fraction_Or_Star_Fill -- ---------------------------------- procedure Number_Fraction_Or_Star_Fill is begin Debug_Start ("Number_Fraction_Or_Star_Fill"); loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Skip; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; Skip; when '*' => Pic.Star_Fill := True; Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1; Pic.End_Float := Index; Skip; loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Skip; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; Skip; when '*' => Pic.Star_Fill := True; Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1; Pic.End_Float := Index; Skip; when others => return; end case; end loop; when others => Number_Fraction; return; end case; end loop; end Number_Fraction_Or_Star_Fill; ------------------------------- -- Number_Fraction_Or_Z_Fill -- ------------------------------- procedure Number_Fraction_Or_Z_Fill is begin Debug_Start ("Number_Fraction_Or_Z_Fill"); loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Skip; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; Skip; when 'Z' | 'z' => Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1; Pic.End_Float := Index; Pic.Picture.Expanded (Index) := 'Z'; -- consistency Skip; loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Skip; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; Skip; when 'Z' | 'z' => Pic.Picture.Expanded (Index) := 'Z'; -- consistency Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1; Pic.End_Float := Index; Skip; when others => return; end case; end loop; when others => Number_Fraction; return; end case; end loop; end Number_Fraction_Or_Z_Fill; ----------------------- -- Optional_RHS_Sign -- ----------------------- procedure Optional_RHS_Sign is begin Debug_Start ("Optional_RHS_Sign"); if At_End then return; end if; case Look is when '+' | '-' => Pic.Sign_Position := Index; Skip; return; when 'C' | 'c' => Pic.Sign_Position := Index; Pic.Picture.Expanded (Index) := 'C'; Skip; if Look = 'R' or else Look = 'r' then Pic.Second_Sign := Index; Pic.Picture.Expanded (Index) := 'R'; Skip; else raise Picture_Error; end if; return; when 'D' | 'd' => Pic.Sign_Position := Index; Pic.Picture.Expanded (Index) := 'D'; Skip; if Look = 'B' or else Look = 'b' then Pic.Second_Sign := Index; Pic.Picture.Expanded (Index) := 'B'; Skip; else raise Picture_Error; end if; return; when '>' => if Pic.Picture.Expanded (Pic.Sign_Position) = '<' then Pic.Second_Sign := Index; Skip; else raise Picture_Error; end if; when others => return; end case; end Optional_RHS_Sign; ------------- -- Picture -- ------------- -- Note that Picture can be called in either State -- It will set state to Valid only if a 9 is encountered or floating -- currency is called. procedure Picture is begin Debug_Start ("Picture"); loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Skip; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; Skip; when '$' => Leading_Dollar; return; when '#' => Leading_Pound; return; when '9' => Computed_BWZ := False; Set_State (Okay); Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1; Skip; when 'V' | 'v' | '.' => Pic.Radix_Position := Index; Skip; Number_Fraction; Trailing_Currency; return; when others => return; end case; end loop; end Picture; --------------------- -- Picture_Bracket -- --------------------- procedure Picture_Bracket is begin Pic.Sign_Position := Index; Debug_Start ("Picture_Bracket"); Pic.Sign_Position := Index; -- Treat as a floating sign, and unwind otherwise Pic.Floater := '<'; Pic.Start_Float := Index; Pic.End_Float := Index; -- Don't increment Pic.Max_Leading_Digits, we need one "real" -- sign place. Skip; -- Known Bracket loop case Look is when '_' | '0' | '/' => Pic.End_Float := Index; Skip; when 'B' | 'b' => Pic.End_Float := Index; Pic.Picture.Expanded (Index) := 'b'; Skip; when '<' => Set_State (Okay); -- "<<>" is enough. Floating_Bracket; Trailing_Currency; Trailing_Bracket; return; when '$' | '#' | '9' | '*' => if State /= Okay then Pic.Floater := '!'; Pic.Start_Float := Invalid_Position; Pic.End_Float := Invalid_Position; end if; Picture; Trailing_Bracket; Set_State (Okay); return; when '.' | 'V' | 'v' => if State /= Okay then Pic.Floater := '!'; Pic.Start_Float := Invalid_Position; Pic.End_Float := Invalid_Position; end if; -- Don't assume that state is okay, haven't seen a digit Picture; Trailing_Bracket; return; when others => raise Picture_Error; end case; end loop; end Picture_Bracket; ------------------- -- Picture_Minus -- ------------------- procedure Picture_Minus is begin Debug_Start ("Picture_Minus"); Pic.Sign_Position := Index; -- Treat as a floating sign, and unwind otherwise Pic.Floater := '-'; Pic.Start_Float := Index; Pic.End_Float := Index; -- Don't increment Pic.Max_Leading_Digits, we need one "real" -- sign place. Skip; -- Known Minus loop case Look is when '_' | '0' | '/' => Pic.End_Float := Index; Skip; when 'B' | 'b' => Pic.End_Float := Index; Pic.Picture.Expanded (Index) := 'b'; Skip; when '-' => Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1; Pic.End_Float := Index; Skip; Set_State (Okay); -- "-- " is enough. Floating_Minus; Trailing_Currency; return; when '$' | '#' | '9' | '*' => if State /= Okay then Pic.Floater := '!'; Pic.Start_Float := Invalid_Position; Pic.End_Float := Invalid_Position; end if; Picture; Set_State (Okay); return; when 'Z' | 'z' => -- Can't have Z and a floating sign if State = Okay then Set_State (Reject); end if; Pic.Picture.Expanded (Index) := 'Z'; -- consistency Zero_Suppression; Trailing_Currency; Optional_RHS_Sign; return; when '.' | 'V' | 'v' => if State /= Okay then Pic.Floater := '!'; Pic.Start_Float := Invalid_Position; Pic.End_Float := Invalid_Position; end if; -- Don't assume that state is okay, haven't seen a digit Picture; return; when others => return; end case; end loop; end Picture_Minus; ------------------ -- Picture_Plus -- ------------------ procedure Picture_Plus is begin Debug_Start ("Picture_Plus"); Pic.Sign_Position := Index; -- Treat as a floating sign, and unwind otherwise Pic.Floater := '+'; Pic.Start_Float := Index; Pic.End_Float := Index; -- Don't increment Pic.Max_Leading_Digits, we need one "real" -- sign place. Skip; -- Known Plus loop case Look is when '_' | '0' | '/' => Pic.End_Float := Index; Skip; when 'B' | 'b' => Pic.End_Float := Index; Pic.Picture.Expanded (Index) := 'b'; Skip; when '+' => Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1; Pic.End_Float := Index; Skip; Set_State (Okay); -- "++" is enough Floating_Plus; Trailing_Currency; return; when '$' | '#' | '9' | '*' => if State /= Okay then Pic.Floater := '!'; Pic.Start_Float := Invalid_Position; Pic.End_Float := Invalid_Position; end if; Picture; Set_State (Okay); return; when 'Z' | 'z' => if State = Okay then Set_State (Reject); end if; -- Can't have Z and a floating sign Pic.Picture.Expanded (Index) := 'Z'; -- consistency -- '+Z' is acceptable Set_State (Okay); -- Overwrite Floater and Start_Float Pic.Floater := 'Z'; Pic.Start_Float := Index; Zero_Suppression; Trailing_Currency; Optional_RHS_Sign; return; when '.' | 'V' | 'v' => if State /= Okay then Pic.Floater := '!'; Pic.Start_Float := Invalid_Position; Pic.End_Float := Invalid_Position; end if; -- Don't assume that state is okay, haven't seen a digit Picture; return; when others => return; end case; end loop; end Picture_Plus; -------------------- -- Picture_String -- -------------------- procedure Picture_String is begin Debug_Start ("Picture_String"); while Is_Insert loop Skip; end loop; case Look is when '$' | '#' => Picture; Optional_RHS_Sign; when '+' => Picture_Plus; when '-' => Picture_Minus; when '<' => Picture_Bracket; when 'Z' | 'z' => Pic.Picture.Expanded (Index) := 'Z'; -- consistency Zero_Suppression; Trailing_Currency; Optional_RHS_Sign; when '*' => Star_Suppression; Trailing_Currency; Optional_RHS_Sign; when '9' | '.' | 'V' | 'v' => Number; Trailing_Currency; Optional_RHS_Sign; when others => raise Picture_Error; end case; -- Blank when zero either if the PIC does not contain a '9' or if -- requested by the user and no '*'. Pic.Blank_When_Zero := (Computed_BWZ or else Pic.Blank_When_Zero) and then not Pic.Star_Fill; -- Star fill if '*' and no '9' Pic.Star_Fill := Pic.Star_Fill and then Computed_BWZ; if not At_End then Set_State (Reject); end if; end Picture_String; --------------- -- Set_State -- --------------- procedure Set_State (L : Legality) is begin if Debug then Ada.Text_IO.Put_Line (" Set state from " & Legality'Image (State) & " to " & Legality'Image (L)); end if; State := L; end Set_State; ---------- -- Skip -- ---------- procedure Skip is begin if Debug then Ada.Text_IO.Put_Line (" Skip " & Pic.Picture.Expanded (Index)); end if; Index := Index + 1; end Skip; ---------------------- -- Star_Suppression -- ---------------------- procedure Star_Suppression is begin Debug_Start ("Star_Suppression"); if Pic.Floater /= '!' and then Pic.Floater /= '*' then -- Two floats not allowed raise Picture_Error; else Pic.Floater := '*'; end if; Pic.Start_Float := Index; Pic.End_Float := Index; Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1; Set_State (Okay); -- Even a single * is a valid picture Pic.Star_Fill := True; Skip; -- Known * loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Pic.End_Float := Index; Skip; when 'B' | 'b' => Pic.End_Float := Index; Pic.Picture.Expanded (Index) := 'b'; Skip; when '*' => Pic.End_Float := Index; Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1; Set_State (Okay); Skip; when '9' => Set_State (Okay); Number_Completion; return; when '.' | 'V' | 'v' => Pic.Radix_Position := Index; Skip; Number_Fraction_Or_Star_Fill; return; when '#' | '$' => if Pic.Max_Currency_Digits > 0 then raise Picture_Error; end if; -- Cannot have leading and trailing currency Trailing_Currency; Set_State (Okay); return; when others => raise Picture_Error; end case; end loop; end Star_Suppression; ---------------------- -- Trailing_Bracket -- ---------------------- procedure Trailing_Bracket is begin Debug_Start ("Trailing_Bracket"); if Look = '>' then Pic.Second_Sign := Index; Skip; else raise Picture_Error; end if; end Trailing_Bracket; ----------------------- -- Trailing_Currency -- ----------------------- procedure Trailing_Currency is begin Debug_Start ("Trailing_Currency"); if At_End then return; end if; if Look = '$' then Pic.Start_Currency := Index; Pic.End_Currency := Index; Skip; else while not At_End and then Look = '#' loop if Pic.Start_Currency = Invalid_Position then Pic.Start_Currency := Index; end if; Pic.End_Currency := Index; Skip; end loop; end if; loop if At_End then return; end if; case Look is when '_' | '0' | '/' => Skip; when 'B' | 'b' => Pic.Picture.Expanded (Index) := 'b'; Skip; when others => return; end case; end loop; end Trailing_Currency; ---------------------- -- Zero_Suppression -- ---------------------- procedure Zero_Suppression is begin Debug_Start ("Zero_Suppression"); Pic.Floater := 'Z'; Pic.Start_Float := Index; Pic.End_Float := Index; Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1; Pic.Picture.Expanded (Index) := 'Z'; -- consistency Skip; -- Known Z loop -- Even a single Z is a valid picture if At_End then Set_State (Okay); return; end if; case Look is when '_' | '0' | '/' => Pic.End_Float := Index; Skip; when 'B' | 'b' => Pic.End_Float := Index; Pic.Picture.Expanded (Index) := 'b'; Skip; when 'Z' | 'z' => Pic.Picture.Expanded (Index) := 'Z'; -- consistency Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1; Pic.End_Float := Index; Set_State (Okay); Skip; when '9' => Set_State (Okay); Number_Completion; return; when '.' | 'V' | 'v' => Pic.Radix_Position := Index; Skip; Number_Fraction_Or_Z_Fill; return; when '#' | '$' => Trailing_Currency; Set_State (Okay); return; when others => return; end case; end loop; end Zero_Suppression; -- Start of processing for Precalculate begin pragma Debug (Set_Debug); Picture_String; if Debug then Ada.Text_IO.New_Line; Ada.Text_IO.Put (" Picture : """ & Pic.Picture.Expanded (1 .. Pic.Picture.Length) & ""","); Ada.Text_IO.Put (" Floater : '" & Pic.Floater & "',"); end if; if State = Reject then raise Picture_Error; end if; Debug_Integer (Pic.Radix_Position, "Radix Positon : "); Debug_Integer (Pic.Sign_Position, "Sign Positon : "); Debug_Integer (Pic.Second_Sign, "Second Sign : "); Debug_Integer (Pic.Start_Float, "Start Float : "); Debug_Integer (Pic.End_Float, "End Float : "); Debug_Integer (Pic.Start_Currency, "Start Currency : "); Debug_Integer (Pic.End_Currency, "End Currency : "); Debug_Integer (Pic.Max_Leading_Digits, "Max Leading Digits : "); Debug_Integer (Pic.Max_Trailing_Digits, "Max Trailing Digits : "); if Debug then Ada.Text_IO.New_Line; end if; exception when Constraint_Error => -- To deal with special cases like null strings raise Picture_Error; end Precalculate; ---------------- -- To_Picture -- ---------------- function To_Picture (Pic_String : String; Blank_When_Zero : Boolean := False) return Picture is Result : Picture; begin declare Item : constant String := Expand (Pic_String); begin Result.Contents.Picture := (Item'Length, Item); Result.Contents.Original_BWZ := Blank_When_Zero; Result.Contents.Blank_When_Zero := Blank_When_Zero; Precalculate (Result.Contents); return Result; end; exception when others => raise Picture_Error; end To_Picture; ----------- -- Valid -- ----------- function Valid (Pic_String : String; Blank_When_Zero : Boolean := False) return Boolean is begin declare Expanded_Pic : constant String := Expand (Pic_String); -- Raises Picture_Error if Item not well-formed Format_Rec : Format_Record; begin Format_Rec.Picture := (Expanded_Pic'Length, Expanded_Pic); Format_Rec.Blank_When_Zero := Blank_When_Zero; Format_Rec.Original_BWZ := Blank_When_Zero; Precalculate (Format_Rec); -- False only if Blank_When_Zero is True but the pic string has a '*' return not Blank_When_Zero or else Strings_Fixed.Index (Expanded_Pic, "*") = 0; end; exception when others => return False; end Valid; -------------------- -- Decimal_Output -- -------------------- package body Decimal_Output is ----------- -- Image -- ----------- function Image (Item : Num; Pic : Picture; Currency : String := Default_Currency; Fill : Character := Default_Fill; Separator : Character := Default_Separator; Radix_Mark : Character := Default_Radix_Mark) return String is begin return Format_Number (Pic.Contents, Num'Image (Item), Currency, Fill, Separator, Radix_Mark); end Image; ------------ -- Length -- ------------ function Length (Pic : Picture; Currency : String := Default_Currency) return Natural is Picstr : constant String := Pic_String (Pic); V_Adjust : Integer := 0; Cur_Adjust : Integer := 0; begin -- Check if Picstr has 'V' or '$' -- If 'V', then length is 1 less than otherwise -- If '$', then length is Currency'Length-1 more than otherwise -- This should use the string handling package ??? for J in Picstr'Range loop if Picstr (J) = 'V' then V_Adjust := -1; elsif Picstr (J) = '$' then Cur_Adjust := Currency'Length - 1; end if; end loop; return Picstr'Length - V_Adjust + Cur_Adjust; end Length; --------- -- Put -- --------- procedure Put (File : Text_IO.File_Type; Item : Num; Pic : Picture; Currency : String := Default_Currency; Fill : Character := Default_Fill; Separator : Character := Default_Separator; Radix_Mark : Character := Default_Radix_Mark) is begin Text_IO.Put (File, Image (Item, Pic, Currency, Fill, Separator, Radix_Mark)); end Put; procedure Put (Item : Num; Pic : Picture; Currency : String := Default_Currency; Fill : Character := Default_Fill; Separator : Character := Default_Separator; Radix_Mark : Character := Default_Radix_Mark) is begin Text_IO.Put (Image (Item, Pic, Currency, Fill, Separator, Radix_Mark)); end Put; procedure Put (To : out String; Item : Num; Pic : Picture; Currency : String := Default_Currency; Fill : Character := Default_Fill; Separator : Character := Default_Separator; Radix_Mark : Character := Default_Radix_Mark) is Result : constant String := Image (Item, Pic, Currency, Fill, Separator, Radix_Mark); begin if Result'Length > To'Length then raise Ada.Text_IO.Layout_Error; else Strings_Fixed.Move (Source => Result, Target => To, Justify => Strings.Right); end if; end Put; ----------- -- Valid -- ----------- function Valid (Item : Num; Pic : Picture; Currency : String := Default_Currency) return Boolean is begin declare Temp : constant String := Image (Item, Pic, Currency); pragma Warnings (Off, Temp); begin return True; end; exception when Ada.Text_IO.Layout_Error => return False; end Valid; end Decimal_Output; end Ada.Text_IO.Editing;