-- GHDL Run Time (GRT) - 'value subprograms. -- Copyright (C) 2002, 2003, 2004, 2005 Tristan Gingold -- -- GHDL is free software; you can redistribute it and/or modify it under -- the terms of the GNU General Public License as published by the Free -- Software Foundation; either version 2, or (at your option) any later -- version. -- -- GHDL is distributed in the hope that it will be useful, but WITHOUT 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 Public License -- along with GCC; see the file COPYING. If not, write to the Free -- Software Foundation, 59 Temple Place - Suite 330, Boston, MA -- 02111-1307, USA. with Grt.Errors; use Grt.Errors; with System; with Ada.Unchecked_Conversion; package body Grt.Values is NBSP : constant Character := Character'Val (160); HT : constant Character := Character'Val (9); function White (C : in Character) return Boolean is begin return C = ' ' or C = NBSP or C = HT; end White; procedure Remove_Whitespace(S : in Std_String_Basep; Pos : in out Ghdl_Index_Type; Len : in Ghdl_Index_Type; Chars : out Ghdl_B2) is begin Chars := False; -- GHDL: allow several leading whitespace. while Pos < Len loop if White (S (Pos)) then Pos := Pos + 1; else Chars := True; exit; end if; end loop; end Remove_Whitespace; function LC(C : in Character) return Character is begin if C >= 'A' and then C <= 'Z' then return Character'val(Character'pos(C) + Character'pos('a') - Character'pos('A')); else return C; end if; end LC; procedure Make_LC_String(S : Std_String_Basep; Pos : in out Ghdl_Index_Type; Str : out String) is begin for i in Str'range loop Str(i) := LC(S(Pos)); Pos := Pos + 1; end loop; end Make_LC_String; function StringMatch(Str : String; EnumStr : Ghdl_C_String) return boolean is EnumLen : constant Natural := strlen(EnumStr); begin for j in Str'range loop if j > EnumLen or else Str(j) /= EnumStr(j) then return false; end if; end loop; if Str'last = EnumLen then return true; else return false; end if; end StringMatch; function Ghdl_Value_Enum (Str : Std_String_Ptr; Rti : Ghdl_Rti_Access) return Ghdl_Index_Type is Val : Ghdl_Index_Type := 0; S : constant Std_String_Basep := Str.Base; Len : constant Ghdl_Index_Type := Str.Bounds.Dim_1.Length; Pos : Ghdl_Index_Type := 0; Chars : Ghdl_B2; Enum_Rti : Ghdl_Rtin_Type_Enum_Acc; begin Remove_Whitespace(S, Pos, Len, Chars); if Pos = Len then Error_E ("'value: empty string"); end if; Enum_Rti := To_Ghdl_Rtin_Type_Enum_Acc (Rti); declare Str : String(1..Natural(Len - Pos)); Found : Boolean := False; begin Make_LC_String(S, Pos, Str); for i in 0 .. Enum_Rti.Nbr - 1 loop if StringMatch(Str, Enum_Rti.Names.all(i)) then Found := True; Val := i; exit; end if; end loop; if not Found then Error_E ("'value: " & Str & " not in enumeration " & Enum_Rti.Name.all(1..strlen(Enum_Rti.Name))); end if; end; Remove_Whitespace(S, Pos, Len, Chars); if Chars then Error_E ("'value: trailing characters after blank"); end if; return Val; end Ghdl_Value_Enum; function Ghdl_Value_B2 (Str : Std_String_Ptr; Rti : Ghdl_Rti_Access) return Ghdl_B2 is begin return Ghdl_B2'Val(Ghdl_Value_Enum (Str , Rti )); end Ghdl_Value_B2; function Ghdl_Value_E8 (Str : Std_String_Ptr; Rti : Ghdl_Rti_Access) return Ghdl_E8 is begin return Ghdl_E8'Val(Ghdl_Value_Enum (Str , Rti )); end Ghdl_Value_E8; function Ghdl_Value_E32 (Str : Std_String_Ptr; Rti : Ghdl_Rti_Access) return Ghdl_E32 is begin return Ghdl_E32'Val(Ghdl_Value_Enum (Str , Rti )); end Ghdl_Value_E32; function Ghdl_Value_I64 (Str : Std_String_Ptr) return Ghdl_I64 is S : constant Std_String_Basep := Str.Base; Len : constant Ghdl_Index_Type := Str.Bounds.Dim_1.Length; Pos : Ghdl_Index_Type := 0; C : Character; Sep : Character; Val, D, Base : Ghdl_I64; Exp : Integer; Chars : Ghdl_B2; begin -- LRM 14.1 -- Leading [and trailing] whitespace is allowed and ignored. -- -- GHDL: allow several leading whitespace. Remove_Whitespace(S, Pos, Len, Chars); if Pos = Len then Error_E ("'value: empty string"); end if; C := S (Pos); -- Be user friendly. if C = '-' or C = '+' then Error_E ("'value: leading sign +/- not allowed"); end if; Val := 0; loop if C in '0' .. '9' then Val := Val * 10 + Character'Pos (C) - Character'Pos ('0'); Pos := Pos + 1; exit when Pos >= Len; C := S (Pos); else Error_E ("'value: decimal digit expected"); end if; case C is when '_' => Pos := Pos + 1; if Pos >= Len then Error_E ("'value: trailing underscore"); end if; C := S (Pos); when '#' | ':' | 'E' | 'e' => exit; when ' ' | NBSP | HT => Pos := Pos + 1; exit; when others => null; end case; end loop; if Pos >= Len then return Val; end if; if C = '#' or C = ':' then Base := Val; Val := 0; Sep := C; Pos := Pos + 1; if Base < 2 or Base > 16 then Error_E ("'value: bad base"); end if; if Pos >= Len then Error_E ("'value: missing based integer"); end if; C := S (Pos); loop case C is when '0' .. '9' => D := Character'Pos (C) - Character'Pos ('0'); when 'a' .. 'f' => D := Character'Pos (C) - Character'Pos ('a') + 10; when 'A' .. 'F' => D := Character'Pos (C) - Character'Pos ('A') + 10; when others => Error_E ("'value: digit expected"); end case; if D >= Base then Error_E ("'value: digit >= base"); end if; Val := Val * Base + D; Pos := Pos + 1; if Pos >= Len then Error_E ("'value: missing end sign number"); end if; C := S (Pos); if C = '#' or C = ':' then if C /= Sep then Error_E ("'value: sign number mismatch"); end if; Pos := Pos + 1; exit; elsif C = '_' then Pos := Pos + 1; if Pos >= Len then Error_E ("'value: no character after underscore"); end if; C := S (Pos); end if; end loop; else Base := 10; end if; -- Handle exponent. if C = 'e' or C = 'E' then Pos := Pos + 1; if Pos >= Len then Error_E ("'value: no character after exponent"); end if; C := S (Pos); if C = '+' then Pos := Pos + 1; if Pos >= Len then Error_E ("'value: no character after sign"); end if; C := S (Pos); elsif C = '-' then Error_E ("'value: negativ exponent not allowed"); end if; Exp := 0; loop if C in '0' .. '9' then Exp := Exp * 10 + Character'Pos (C) - Character'Pos ('0'); Pos := Pos + 1; exit when Pos >= Len; C := S (Pos); else Error_E ("'value: decimal digit expected"); end if; case C is when '_' => Pos := Pos + 1; if Pos >= Len then Error_E ("'value: trailing underscore"); end if; C := S (Pos); when ' ' | NBSP | HT => Pos := Pos + 1; exit; when others => null; end case; end loop; while Exp > 0 loop if Exp mod 2 = 1 then Val := Val * Base; end if; Exp := Exp / 2; Base := Base * Base; end loop; end if; -- LRM 14.1 -- [Leading] and trailing whitespace is allowed and ignored. -- -- GHDL: allow several trailing whitespace. Remove_Whitespace(S, Pos, Len, Chars); if Chars then Error_E ("integer'value: trailing characters after blank"); end if; return Val; end Ghdl_Value_I64; function Ghdl_Value_I32 (Str : Std_String_Ptr) return Ghdl_I32 is begin return Ghdl_I32 (Ghdl_Value_I64 (Str)); end Ghdl_Value_I32; function Ghdl_Value_Physical_Type (Str : Std_String_Ptr; Rti : Ghdl_Rti_Access) return Ghdl_I64 is function To_Std_String_Ptr is new Ada.Unchecked_Conversion (Source => System.Address, Target => Std_String_Ptr); function To_Std_String_Boundp is new Ada.Unchecked_Conversion (Source => System.Address, Target => Std_String_Boundp); S : aliased Std_String := Str.all; Bound : aliased Std_String_Bound := Str.Bounds.all; Start, Finish : Ghdl_Index_Type; Found_Real : Boolean := false; Phys_Rti : Ghdl_Rtin_Type_Physical_Acc; Unit : Ghdl_Rtin_Unit_Acc; Multiple : Ghdl_Rti_Unit_Val; Mult : Ghdl_I64; begin Phys_Rti := To_Ghdl_Rtin_Type_Physical_Acc (Rti); S.Bounds := To_Std_String_Boundp(Bound'Address); -- find characters at the end... Finish := Bound.Dim_1.Length - 1; while White(S.Base.all(Finish)) loop Finish := Finish - 1; end loop; Start := Finish; while not White(S.Base.all(Start - 1)) loop Start := Start - 1; end loop; -- shorten Bounds to exclude non-numeric part Bound.Dim_1.Right := Bound.Dim_1.Right - Std_Integer(Bound.Dim_1.Length - Start); Bound.Dim_1.Length := Start; -- does the string represent a Real? for i in 0 .. Start loop if S.Base.all(i) = '.' then Found_Real := true; end if; end loop; declare Unit_Str : String(1 .. Natural(1 + Finish - Start)); Found : Boolean := False; begin Make_LC_String(Str.Base, Start, Unit_Str); for i in 0 .. Phys_Rti.Nbr - 1 loop Unit := To_Ghdl_Rtin_Unit_Acc(Phys_Rti.Units(i)); if StringMatch(Unit_Str, Unit.Name) then Found := True; Multiple := To_Ghdl_Rtin_Unit_Acc (Phys_Rti.Units (i)).Value; exit; end if; end loop; if not Found then Error_E ("'value: Unit " & Unit_Str & " not in physical type" & Phys_Rti.Name.all(1..strlen(Phys_Rti.Name))); end if; end; if Rti.Kind = Ghdl_Rtik_Type_P64 then Mult := Multiple.Unit_64; else Mult := Ghdl_I64(Multiple.Unit_32); end if; if Found_Real then return Ghdl_I64 (Ghdl_Value_F64 (To_Std_String_Ptr(S'Address)) * Ghdl_F64 (Mult)); else return Ghdl_Value_I64 (To_Std_String_Ptr(S'Address)) * Mult; end if; end Ghdl_Value_Physical_Type; function Ghdl_Value_P64 (Str : Std_String_Ptr; Rti : Ghdl_Rti_Access) return Ghdl_I64 is begin if Rti.Kind /= Ghdl_Rtik_Type_P64 then Error_E ("Physical_Type_64'value: incorrect RTI"); end if; return Ghdl_Value_Physical_Type(Str, Rti); end Ghdl_Value_P64; function Ghdl_Value_P32 (Str : Std_String_Ptr; Rti : Ghdl_Rti_Access) return Ghdl_I32 is begin if Rti.Kind /= Ghdl_Rtik_Type_P32 then Error_E ("Physical_Type_32'value: incorrect RTI"); end if; return Ghdl_I32(Ghdl_Value_Physical_Type(Str, Rti)); end Ghdl_Value_P32; -- From patch attached to https://gna.org/bugs/index.php?18352 -- thanks to Christophe Curis https://gna.org/users/lobotomy function Ghdl_Value_F64 (Str : Std_String_Ptr) return Ghdl_F64 is S : constant Std_String_Basep := Str.Base; Len : constant Ghdl_Index_Type := Str.Bounds.Dim_1.Length; Pos : Ghdl_Index_Type := 0; C : Character; Chars : Ghdl_B2; Is_Negative, Is_Neg_Exp : Boolean := False; Base : Ghdl_F64; Intg : Ghdl_I32; Val, Df : Ghdl_F64; Sep : Character; FrcExp : Ghdl_F64; begin -- LRM 14.1 -- Leading [and trailing] whitespace is allowed and ignored. -- -- GHDL: allow several leading whitespace. Remove_Whitespace(S, Pos, Len, Chars); if Pos = Len then Error_E ("'value: empty string"); end if; C := S (Pos); if C = '-' then Is_Negative := True; Pos := Pos + 1; elsif C = '+' then Pos := Pos + 1; end if; if Pos >= Len then Error_E ("'value: decimal digit expected"); end if; -- Read Integer-or-Base part (may be optional) Intg := 0; while Pos < Len loop C := S (Pos); if C in '0' .. '9' then Intg := Intg * 10 + Character'Pos (C) - Character'Pos ('0'); elsif C /= '_' then exit; end if; Pos := Pos + 1; end loop; if Pos = Len then return Ghdl_F64 (Intg); end if; -- Special case: base was specified if C = '#' or C = ':' then if Intg < 2 or Intg > 16 then Error_E ("'value: bad base"); end if; Base := Ghdl_F64 (Intg); Val := 0.0; Sep := C; Pos := Pos + 1; if Pos >= Len then Error_E ("'value: missing based decimal"); end if; -- Get the Integer part of the Value while Pos < Len loop C := S (Pos); case C is when '0' .. '9' => Df := Ghdl_F64 (Character'Pos (C) - Character'Pos('0') ); when 'A' .. 'F' => Df := Ghdl_F64 (Character'Pos (C) - Character'Pos('A') + 10); when 'a' .. 'f' => Df := Ghdl_F64 (Character'Pos (C) - Character'Pos('a') + 10); when others => exit; end case; if C /= '_' then if Df >= Base then Error_E ("'value: digit greater than base"); end if; Val := Val * Base + Df; end if; Pos := Pos + 1; end loop; if Pos >= Len then Error_E ("'value: missing end sign number"); end if; else Base := 10.0; Sep := ' '; Val := Ghdl_F64 (Intg); end if; -- Handle the Fractional part if C = '.' then Pos := Pos + 1; FrcExp := 1.0; while Pos < Len loop C := S (Pos); case C is when '0' .. '9' => Df := Ghdl_F64 (Character'Pos (C) - Character'Pos('0')); when 'A' .. 'F' => exit when Sep = ' '; Df := Ghdl_F64 (Character'Pos (C) - Character'Pos('A') + 10); when 'a' .. 'f' => exit when Sep = ' '; Df := Ghdl_F64 (Character'Pos (C) - Character'Pos('a') + 10); when others => exit; end case; if C /= '_' then FrcExp := FrcExp / Base; if Df > Base then Error_E ("'value: digit greater than base"); end if; Val := Val + Df * FrcExp; end if; Pos := Pos + 1; end loop; end if; -- If base was specified, we must find here the end marker if Sep /= ' ' then if Pos >= Len then Error_E ("'value: missing end sign number"); end if; if C /= Sep then Error_E ("'value: sign number mismatch"); end if; Pos := Pos + 1; end if; -- Handle exponent if Pos < Len then C := S (Pos); if C = 'e' or C = 'E' then Pos := Pos + 1; if Pos >= Len then Error_E ("'value: no character after exponent"); end if; C := S (Pos); if C = '-' then Is_Neg_Exp := True; Pos := Pos + 1; elsif C = '+' then Pos := Pos + 1; end if; Intg := 0; while Pos < Len loop C := S (Pos); if C in '0' .. '9' then Intg := Intg * 10 + Character'Pos (C) - Character'Pos ('0'); else exit; end if; Pos := Pos + 1; end loop; -- This Exponentiation method is sub-optimal, -- but it does not depend on any library FrcExp := 1.0; if Is_Neg_Exp then while Intg > 0 loop FrcExp := FrcExp / 10.0; Intg := Intg - 1; end loop; else while Intg > 0 loop FrcExp := FrcExp * 10.0; Intg := Intg - 1; end loop; end if; Val := Val * FrcExp; end if; end if; -- LRM 14.1 -- [Leading] and trailing whitespace is allowed and ignored. -- -- GHDL: allow several leading whitespace. Remove_Whitespace(S, Pos, Len, Chars); if Chars then Error_E ("'value: trailing characters after blank"); end if; if Is_Negative then Val := -Val; end if; return Val; end Ghdl_Value_F64; end Grt.Values;