File : s-valllu.adb


     ------------------------------------------------------------------------------
     --                                                                          --
     --                         GNAT COMPILER COMPONENTS                         --
     --                                                                          --
   5 --                       S Y S T E M . V A L _ L L U                        --
     --                                                                          --
     --                                 S p e c                                  --
     --                                                                          --
     --                            $Revision: 1.12 $                             --
  10 --                                                                          --
     --          Copyright (C) 1992-1997 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- --
  15 -- ware  Foundation;  either version 2,  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.  See the GNU General Public License --
     -- for  more details.  You should have  received  a copy of the GNU General --
  20 -- Public License  distributed with GNAT;  see file COPYING.  If not, write --
     -- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
     -- MA 02111-1307, USA.                                                      --
     --                                                                          --
     -- As a special exception,  if other files  instantiate  generics from this --
  25 -- unit, or you link  this unit with other files  to produce an executable, --
     -- this  unit  does not  by itself cause  the resulting  executable  to  be --
     -- covered  by the  GNU  General  Public  License.  This exception does not --
     -- however invalidate  any other reasons why  the executable file  might be --
     -- covered by the  GNU Public License.                                      --
  30 --                                                                          --
     -- GNAT was originally developed  by the GNAT team at  New York University. --
     -- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). --
     --                                                                          --
     ------------------------------------------------------------------------------
  35 
     with System.Unsigned_Types; use System.Unsigned_Types;
     with System.Val_Util;       use System.Val_Util;
     
     package body System.Val_LLU is
  40 
        -----------------------------
        -- Scan_Long_Long_Unsigned --
        -----------------------------
     
  45    function Scan_Long_Long_Unsigned
          (Str  : String;
           Ptr  : access Integer;
           Max  : Integer)
           return Long_Long_Unsigned
  50    is
           P : Integer;
           --  Local copy of the pointer
     
           Uval : Long_Long_Unsigned;
  55       --  Accumulated unsigned integer result
     
           Expon : Integer;
           --  Exponent value
     
  60       Minus : Boolean := False;
           --  Set to True if minus sign is present, otherwise to False. Note that
           --  a minus sign is permissible for the singular case of -0, and in any
           --  case the pointer is left pointing past a negative integer literal.
     
  65       Overflow : Boolean := False;
           --  Set True if overflow is detected at any point
     
           Start : Positive;
           --  Save location of first non-blank character
  70 
           Base_Char : Character;
           --  Base character (# or :) in based case
     
           Base : Long_Long_Unsigned := 10;
  75       --  Base value (reset in based case)
     
           Digit : Long_Long_Unsigned;
           --  Digit value
     
  80    begin
           Scan_Sign (Str, Ptr, Max, Minus, Start);
     
           if Str (Ptr.all) not in '0' .. '9' then
              Ptr.all := Start;
  85          raise Constraint_Error;
           end if;
     
           P := Ptr.all;
           Uval := Character'Pos (Str (P)) - Character'Pos ('0');
  90       P := P + 1;
     
           --  Scan out digits of what is either the number or the base.
           --  In either case, we are definitely scanning out in base 10.
     
  95       declare
              Umax : constant := (Long_Long_Unsigned'Last - 9) / 10;
              --  Max value which cannot overflow on accumulating next digit
     
              Umax10 : constant := Long_Long_Unsigned'Last / 10;
 100          --  Numbers bigger than Umax10 overflow if multiplied by 10
     
           begin
              --  Loop through decimal digits
              loop
 105             exit when P > Max;
     
                 Digit := Character'Pos (Str (P)) - Character'Pos ('0');
     
                 --  Non-digit encountered
 110 
                 if Digit > 9 then
                    if Str (P) = '_' then
                       Scan_Underscore (Str, P, Ptr, Max, False);
                    else
 115                   exit;
                    end if;
     
                 --  Accumulate result, checking for overflow
     
 120             else
                    if Uval <= Umax then
                       Uval := 10 * Uval + Digit;
     
                    elsif Uval > Umax10 then
 125                   Overflow := True;
     
                    else
                       Uval := 10 * Uval + Digit;
     
 130                   if Uval < Umax10 then
                          Overflow := True;
                       end if;
                    end if;
     
 135                P := P + 1;
                 end if;
              end loop;
           end;
     
 140       Ptr.all := P;
     
           --  Deal with based case
     
           if P < Max and then (Str (P) = ':' or else Str (P) = '#') then
 145          Base_Char := Str (P);
              P := P + 1;
              Base := Uval;
              Uval := 0;
     
 150          --  Check base value. Overflow is set True if we find a bad base, or
              --  a digit that is out of range of the base. That way, we scan out
              --  the numeral that is still syntactically correct, though illegal.
              --  We use a safe base of 16 for this scan, to avoid zero divide.
     
 155          if Base not in 2 .. 16 then
                 Overflow := True;
                 Base :=  16;
              end if;
     
 160          --  Scan out based integer
     
              declare
                 Umax : constant Long_Long_Unsigned :=
                          (Long_Long_Unsigned'Last - Base + 1) / Base;
 165             --  Max value which cannot overflow on accumulating next digit
     
                 UmaxB : constant Long_Long_Unsigned :=
                           Long_Long_Unsigned'Last / Base;
                 --  Numbers bigger than UmaxB overflow if multiplied by base
 170 
              begin
                 --  Loop to scan out based integer value
     
                 loop
 175                --  We require a digit at this stage
     
                    if Str (P) in '0' .. '9' then
                       Digit := Character'Pos (Str (P)) - Character'Pos ('0');
     
 180                elsif Str (P) in 'A' .. 'F' then
                       Digit :=
                         Character'Pos (Str (P)) - (Character'Pos ('A') - 10);
     
                    elsif Str (P) in 'a' .. 'f' then
 185                   Digit :=
                         Character'Pos (Str (P)) - (Character'Pos ('a') - 10);
     
                    --  If we don't have a digit, then this is not a based number
                    --  after all, so we use the value we scanned out as the base
 190                --  (now in Base), and the pointer to the base character was
                    --  already stored in Ptr.all.
     
                    else
                       Uval := Base;
 195                   exit;
                    end if;
     
                    --  If digit is too large, just signal overflow and continue.
                    --  The idea here is to keep scanning as long as the input is
 200                --  syntactically valid, even if we have detected overflow
     
                    if Digit >= Base then
                       Overflow := True;
     
 205                --  Here we accumulate the value, checking overflow
     
                    elsif Uval <= Umax then
                       Uval := Base * Uval + Digit;
     
 210                elsif Uval > UmaxB then
                       Overflow := True;
     
                    else
                       Uval := Base * Uval + Digit;
 215 
                       if Uval < UmaxB then
                          Overflow := True;
                       end if;
                    end if;
 220 
                    --  If at end of string with no base char, not a based number
                    --  but we signal Constraint_Error and set the pointer past
                    --  the end of the field, since this is what the ACVC tests
                    --  seem to require, see CE3704N, line 204.
 225 
                    P := P + 1;
     
                    if P > Max then
                       Ptr.all := P;
 230                   raise Constraint_Error;
                    end if;
     
                    --  If terminating base character, we are done with loop
     
 235                if Str (P) = Base_Char then
                       Ptr.all := P + 1;
                       exit;
     
                    --  Deal with underscore
 240 
                    elsif Str (P) = '_' then
                       Scan_Underscore (Str, P, Ptr, Max, True);
                    end if;
     
 245             end loop;
              end;
           end if;
     
           --  Come here with scanned unsigned value in Uval. The only remaining
 250       --  required step is to deal with exponent if one is present.
     
           Expon := Scan_Exponent (Str, Ptr, Max);
     
           if Expon /= 0 and then Uval /= 0 then
 255 
              --  For non-zero value, scale by exponent value. No need to do this
              --  efficiently, since use of exponent in integer literals is rare,
              --  and in any case the exponent cannot be very large.
     
 260          declare
                 UmaxB : constant Long_Long_Unsigned :=
                           Long_Long_Unsigned'Last / Base;
                 --  Numbers bigger than UmaxB overflow if multiplied by base
     
 265          begin
                 for J in 1 .. Expon loop
                    if Uval > UmaxB then
                       Overflow := True;
                       exit;
 270                end if;
     
                    Uval := Uval * Base;
                 end loop;
              end;
 275       end if;
     
           --  Return result, dealing with sign and overflow
     
           if Overflow or else (Minus and then Uval /= 0) then
 280          raise Constraint_Error;
           else
              return Uval;
           end if;
        end Scan_Long_Long_Unsigned;
 285 
        ------------------------------
        -- Value_Long_Long_Unsigned --
        ------------------------------
     
 290    function Value_Long_Long_Unsigned
          (Str : String)
          return Long_Long_Unsigned
        is
           V : Long_Long_Unsigned;
 295       P : aliased Integer := Str'First;
     
        begin
           V := Scan_Long_Long_Unsigned (Str, P'Access, Str'Last);
           Scan_Trailing_Blanks (Str, P);
 300       return V;
     
        end Value_Long_Long_Unsigned;
     
     end System.Val_LLU;