blob: 459e7e7940c095ed44b5fb5940aff93cc64488c5 [file] [log] [blame]
------------------------------------------------------------------------------
-- --
-- GNAT RUN-TIME COMPONENTS --
-- --
-- A D A . W I D E _ W I D E _ T E X T _ I O . E D I T I N G --
-- --
-- B o d y --
-- --
-- Copyright (C) 1992-2022, Free Software Foundation, Inc. --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
-- ware Foundation; either version 3, or (at your option) any later ver- --
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
-- GNAT was originally developed by the GNAT team at New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc. --
-- --
------------------------------------------------------------------------------
with Ada.Strings.Fixed;
with Ada.Strings.Wide_Wide_Fixed;
package body Ada.Wide_Wide_Text_IO.Editing is
package Strings renames Ada.Strings;
package Strings_Fixed renames Ada.Strings.Fixed;
package Strings_Wide_Wide_Fixed renames Ada.Strings.Wide_Wide_Fixed;
package Wide_Wide_Text_IO renames Ada.Wide_Wide_Text_IO;
-----------------------
-- Local_Subprograms --
-----------------------
function To_Wide (C : Character) return Wide_Wide_Character;
pragma Inline (To_Wide);
-- Convert Character to corresponding Wide_Wide_Character
---------------------
-- Blank_When_Zero --
---------------------
function Blank_When_Zero (Pic : Picture) return Boolean is
begin
return Pic.Contents.Original_BWZ;
end Blank_When_Zero;
--------------------
-- Decimal_Output --
--------------------
package body Decimal_Output is
-----------
-- Image --
-----------
function Image
(Item : Num;
Pic : Picture;
Currency : Wide_Wide_String := Default_Currency;
Fill : Wide_Wide_Character := Default_Fill;
Separator : Wide_Wide_Character := Default_Separator;
Radix_Mark : Wide_Wide_Character := Default_Radix_Mark)
return Wide_Wide_String
is
begin
return Format_Number
(Pic.Contents, Num'Image (Item),
Currency, Fill, Separator, Radix_Mark);
end Image;
------------
-- Length --
------------
function Length
(Pic : Picture;
Currency : Wide_Wide_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 : Wide_Wide_Text_IO.File_Type;
Item : Num;
Pic : Picture;
Currency : Wide_Wide_String := Default_Currency;
Fill : Wide_Wide_Character := Default_Fill;
Separator : Wide_Wide_Character := Default_Separator;
Radix_Mark : Wide_Wide_Character := Default_Radix_Mark)
is
begin
Wide_Wide_Text_IO.Put (File, Image (Item, Pic,
Currency, Fill, Separator, Radix_Mark));
end Put;
procedure Put
(Item : Num;
Pic : Picture;
Currency : Wide_Wide_String := Default_Currency;
Fill : Wide_Wide_Character := Default_Fill;
Separator : Wide_Wide_Character := Default_Separator;
Radix_Mark : Wide_Wide_Character := Default_Radix_Mark)
is
begin
Wide_Wide_Text_IO.Put (Image (Item, Pic,
Currency, Fill, Separator, Radix_Mark));
end Put;
procedure Put
(To : out Wide_Wide_String;
Item : Num;
Pic : Picture;
Currency : Wide_Wide_String := Default_Currency;
Fill : Wide_Wide_Character := Default_Fill;
Separator : Wide_Wide_Character := Default_Separator;
Radix_Mark : Wide_Wide_Character := Default_Radix_Mark)
is
Result : constant Wide_Wide_String :=
Image (Item, Pic, Currency, Fill, Separator, Radix_Mark);
begin
if Result'Length > To'Length then
raise Wide_Wide_Text_IO.Layout_Error;
else
Strings_Wide_Wide_Fixed.Move (Source => Result, Target => To,
Justify => Strings.Right);
end if;
end Put;
-----------
-- Valid --
-----------
function Valid
(Item : Num;
Pic : Picture;
Currency : Wide_Wide_String := Default_Currency) return Boolean
is
begin
declare
Temp : constant Wide_Wide_String := Image (Item, Pic, Currency);
pragma Warnings (Off, Temp);
begin
return True;
end;
exception
when Layout_Error => return False;
end Valid;
end Decimal_Output;
------------
-- 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;
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 '(' =>
-- We now need to scan out the count after a left paren. In
-- the non-wide version we used Integer_IO.Get, but that is
-- not convenient here, since we don't want to drag in normal
-- Text_IO just for this purpose. So we do the scan ourselves,
-- with the normal validity checks.
Last := Picture_Index + 1;
Count := 0;
if Picture (Last) not in '0' .. '9' then
raise Picture_Error;
end if;
Count := Character'Pos (Picture (Last)) - Character'Pos ('0');
Last := Last + 1;
loop
if Last > Picture'Last then
raise Picture_Error;
end if;
if Picture (Last) = '_' then
if Picture (Last - 1) = '_' then
raise Picture_Error;
end if;
elsif Picture (Last) = ')' then
exit;
elsif Picture (Last) not in '0' .. '9' then
raise Picture_Error;
else
Count := Count * 10
+ Character'Pos (Picture (Last)) -
Character'Pos ('0');
end if;
Last := Last + 1;
end loop;
-- In what follows note that one copy of the repeated
-- character has already been made, so a count of one is
-- no-op, and a count of zero erases a character.
for J in 2 .. Count loop
Result (Result_Index + J - 2) := Picture (Picture_Index - 1);
end loop;
Result_Index := Result_Index + Count - 1;
-- Last was a ')' throw it away too
Picture_Index := Last + 1;
when ')' =>
raise Picture_Error;
when others =>
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 : Wide_Wide_String;
Fill_Character : Wide_Wide_Character;
Separator_Character : Wide_Wide_Character;
Radix_Point : Wide_Wide_Character) return Wide_Wide_String
is
Attrs : Number_Attributes := Parse_Number_String (Number);
Position : Integer;
Rounded : String := Number;
Sign_Position : Integer := Pic.Sign_Position; -- may float.
Answer : Wide_Wide_String (1 .. Pic.Picture.Length);
Last : Integer;
Currency_Pos : Integer := Pic.Start_Currency;
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 := Rounded'Length - 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;
for J in Answer'Range loop
Answer (J) := To_Wide (Pic.Picture.Expanded (J));
end loop;
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 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) := To_Wide (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 separator 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) := '*';
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) := '*';
end loop;
else
if Pic.Floater = '#' then
Currency_Pos := Currency_Symbol'Length;
end if;
for J in reverse Pic.Start_Float .. Position loop
case Answer (J) is
when '*' =>
Answer (J) := Fill_Character;
when 'Z' | 'b' | '/' | '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 Layout_Error;
end if;
end if;
-- Do sign
if Sign_Position = Invalid_Position then
if Attrs.Negative then
raise 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) := To_Wide (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 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;
-- Note: There are some weird cases J can imagine with 'b' or '#'
-- in currency strings where the following code will cause
-- glitches. The trick is to tell when the character in the
-- answer should be checked, and when to look at the original
-- string. Some other time. RIE 11/26/96 ???
case Answer (J) is
when '*' =>
Answer (J) := Fill_Character;
when 'b' =>
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 '_' =>
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 Wide_Wide_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
Wide_Wide_String'(1 .. Pic.Radix_Position - 1 => '*') &
Radix_Point &
Wide_Wide_String'(Pic.Radix_Position + 1 .. Last => '*');
else
return
Wide_Wide_String'
(1 ..
Pic.Radix_Position + Currency_Symbol'Length - 2
=> '*') &
Radix_Point &
Wide_Wide_String'
(Pic.Radix_Position + Currency_Symbol'Length .. Last
=> '*');
end if;
else
return
Wide_Wide_String'(1 .. Pic.Radix_Position - 1 => '*') &
Radix_Point &
Wide_Wide_String'(Pic.Radix_Position + 1 .. Last => '*');
end if;
end if;
return Wide_Wide_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 (intger) 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
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 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;
------------
-- At_End --
------------
function At_End return Boolean is
begin
return Index > Pic.Picture.Length;
end At_End;
----------------------
-- 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
Pic.Floater := '<';
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
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
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
-- Treat as a floating dollar, and unwind otherwise
Pic.Floater := '$';
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
-- Will overwrite Floater and Start_Float
Zero_Suppression;
end if;
when '*' =>
if State = Okay then
raise Picture_Error;
else
-- Will overwrite Floater and Start_Float
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
-- Treat as a floating currency. If it isn't, this will be
-- overwritten later.
Pic.Floater := '#';
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;
-- Will overwrite Floater and Start_Float
Zero_Suppression;
end if;
when '*' =>
if Must_Float then
raise Picture_Error;
else
Pic.Max_Leading_Digits := 0;
-- Will overwrite Floater and Start_Float
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
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
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.
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
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
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
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
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
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
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;
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
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
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);
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
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
State := L;
end Set_State;
----------
-- Skip --
----------
procedure Skip is
begin
Index := Index + 1;
end Skip;
----------------------
-- Star_Suppression --
----------------------
procedure Star_Suppression is
begin
Pic.Floater := '*';
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 '#' | '$' =>
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
if Look = '>' then
Pic.Second_Sign := Index;
Skip;
else
raise Picture_Error;
end if;
end Trailing_Bracket;
-----------------------
-- Trailing_Currency --
-----------------------
procedure Trailing_Currency is
begin
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
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
Picture_String;
if State = Reject then
raise Picture_Error;
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;
-------------
-- To_Wide --
-------------
function To_Wide (C : Character) return Wide_Wide_Character is
begin
return Wide_Wide_Character'Val (Character'Pos (C));
end To_Wide;
-----------
-- 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_0 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;
end Ada.Wide_Wide_Text_IO.Editing;