diff options
author | Tristan Gingold | 2014-11-04 20:21:00 +0100 |
---|---|---|
committer | Tristan Gingold | 2014-11-04 20:21:00 +0100 |
commit | 0a088b311ed2fcebc542f8a2e42d09e2e3c9311c (patch) | |
tree | 8ec898f38ddff616e459a0df57b3f4112bd96ffc /src/scanner-scan_literal.adb | |
parent | 9c195bf5d86d67ea5eb419ccf6e48dc153e57c68 (diff) | |
download | ghdl-0a088b311ed2fcebc542f8a2e42d09e2e3c9311c.tar.gz ghdl-0a088b311ed2fcebc542f8a2e42d09e2e3c9311c.tar.bz2 ghdl-0a088b311ed2fcebc542f8a2e42d09e2e3c9311c.zip |
Create src/vhdl subdirectory.
Diffstat (limited to 'src/scanner-scan_literal.adb')
-rw-r--r-- | src/scanner-scan_literal.adb | 651 |
1 files changed, 0 insertions, 651 deletions
diff --git a/src/scanner-scan_literal.adb b/src/scanner-scan_literal.adb deleted file mode 100644 index 74acf44..0000000 --- a/src/scanner-scan_literal.adb +++ /dev/null @@ -1,651 +0,0 @@ --- Lexical analysis for numbers. --- Copyright (C) 2002 - 2014 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 GHDL; see the file COPYING. If not, write to the Free --- Software Foundation, 59 Temple Place - Suite 330, Boston, MA --- 02111-1307, USA. -with Ada.Unchecked_Conversion; - -separate (Scanner) - --- scan a decimal literal or a based literal. --- --- LRM93 13.4.1 --- DECIMAL_LITERAL ::= INTEGER [ . INTEGER ] [ EXPONENT ] --- EXPONENT ::= E [ + ] INTEGER | E - INTEGER --- --- LRM93 13.4.2 --- BASED_LITERAL ::= BASE # BASED_INTEGER [ . BASED_INTEGER ] # EXPONENT --- BASE ::= INTEGER -procedure Scan_Literal is - -- The base of an E_NUM is 2**16. - -- Type Uint16 is the type of a digit. - type Uint16 is mod 2 ** 16; - - type Uint32 is mod 2 ** 32; - - -- Type of the exponent. - type Sint16 is range -2 ** 15 .. 2 ** 15 - 1; - - -- Number of digits in a E_NUM. - -- We want at least 64bits of precision, so at least 5 digits of 16 bits - -- are required. - Nbr_Digits : constant Sint16 := 5; - subtype Digit_Range is Sint16 range 0 .. Nbr_Digits - 1; - - type Uint16_Array is array (Sint16 range <>) of Uint16; - - -- The value of an E_NUM is (S(N-1)|S(N-2) .. |S(0))* 2**(16*E) - -- where '|' is concatenation. - type E_Num is record - S : Uint16_Array (Digit_Range); - E : Sint16; - end record; - - E_Zero : constant E_Num := (S => (others => 0), E => 0); - E_One : constant E_Num := (S => (0 => 1, others => 0), E => 0); - - -- Compute RES = E * B + V. - -- RES and E can be the same object. - procedure Bmul (Res : out E_Num; E : E_Num; V : Uint16; B : Uint16); - - -- Convert to integer. - procedure Fix (Res : out Iir_Int64; Ok : out Boolean; E : E_Num); - - -- RES := A * B - -- RES can be A or B. - procedure Mul (Res : out E_Num; A, B : E_Num); - - -- RES := A / B. - -- RES can be A. - -- May raise constraint error. - procedure Div (Res : out E_Num; A, B: E_Num); - - -- Convert V to an E_Num. - function To_E_Num (V : Uint16) return E_Num; - - -- Convert E to RES. - procedure To_Float (Res : out Iir_Fp64; Ok : out Boolean; E : E_Num); - - procedure Bmul (Res : out E_Num; E : E_Num; V : Uint16; B : Uint16) - is - -- The carry. - C : Uint32; - begin - -- Only consider V if E is not scaled (otherwise V is not significant). - if E.E = 0 then - C := Uint32 (V); - else - C := 0; - end if; - - -- Multiply and propagate the carry. - for I in Digit_Range loop - C := Uint32 (E.S (I)) * Uint32 (B) + C; - Res.S (I) := Uint16 (C mod Uint16'Modulus); - C := C / Uint16'Modulus; - end loop; - - -- There is a carry, shift. - if C /= 0 then - -- ERR: Possible overflow. - Res.E := E.E + 1; - for I in 0 .. Nbr_Digits - 2 loop - Res.S (I) := Res.S (I + 1); - end loop; - Res.S (Nbr_Digits - 1) := Uint16 (C); - else - Res.E := E.E; - end if; - end Bmul; - - type Uint64 is mod 2 ** 64; - function Shift_Left (Value : Uint64; Amount: Natural) return Uint64; - function Shift_Left (Value : Uint16; Amount: Natural) return Uint16; - pragma Import (Intrinsic, Shift_Left); - - function Shift_Right (Value : Uint16; Amount: Natural) return Uint16; - pragma Import (Intrinsic, Shift_Right); - - function Unchecked_Conversion is new Ada.Unchecked_Conversion - (Source => Uint64, Target => Iir_Int64); - - procedure Fix (Res : out Iir_Int64; Ok : out Boolean; E : E_Num) - is - R : Uint64; - M : Sint16; - begin - -- Find the most significant digit. - M := -1; - for I in reverse Digit_Range loop - if E.S (I) /= 0 then - M := I; - exit; - end if; - end loop; - - -- Handle the easy 0 case. - -- The case M = -1 is handled below, in the normal flow. - if M + E.E < 0 then - Res := 0; - Ok := True; - return; - end if; - - -- Handle overflow. - -- 4 is the number of uint16 in a uint64. - if M + E.E >= 4 then - Ok := False; - return; - end if; - - -- Convert - R := 0; - for I in 0 .. M loop - R := R or Shift_Left (Uint64 (E.S (I)), 16 * Natural (E.E + I)); - end loop; - -- Check the sign bit is 0. - if (R and Shift_Left (1, 63)) /= 0 then - Ok := False; - else - Ok := True; - Res := Unchecked_Conversion (R); - end if; - end Fix; - - -- Return the position of the most non-null digit, -1 if V is 0. - function First_Digit (V : E_Num) return Sint16 is - begin - for I in reverse Digit_Range loop - if V.S (I) /= 0 then - return I; - end if; - end loop; - return -1; - end First_Digit; - - procedure Mul (Res : out E_Num; A, B : E_Num) - is - T : Uint16_Array (0 .. 2 * Nbr_Digits - 1); - V : Uint32; - Max : Sint16; - begin - V := 0; - for I in 0 .. Nbr_Digits - 1 loop - for J in 0 .. I loop - V := V + Uint32 (A.S (J)) * Uint32 (B.S (I - J)); - end loop; - T (I) := Uint16 (V mod Uint16'Modulus); - V := V / Uint16'Modulus; - end loop; - for I in Nbr_Digits .. 2 * Nbr_Digits - 2 loop - for J in I - Nbr_Digits + 1 .. Nbr_Digits - 1 loop - V := V + Uint32 (A.S (J)) * Uint32 (B.S (I - J)); - end loop; - T (I) := Uint16 (V mod Uint16'Modulus); - V := V / Uint16'Modulus; - end loop; - T (T'Last) := Uint16 (V); - -- Search the leading non-nul. - Max := -1; - for I in reverse T'Range loop - if T (I) /= 0 then - Max := I; - exit; - end if; - end loop; - if Max > Nbr_Digits - 1 then - -- Loss of precision. - -- Round. - if T (Max - Nbr_Digits) >= Uint16 (Uint16'Modulus / 2) then - V := 1; - for I in Max - (Nbr_Digits - 1) .. Max loop - V := V + Uint32 (T (I)); - T (I) := Uint16 (V mod Uint16'Modulus); - V := V / Uint16'Modulus; - exit when V = 0; - end loop; - if V /= 0 then - Max := Max + 1; - T (Max) := Uint16 (V); - end if; - end if; - Res.S := T (Max - (Nbr_Digits - 1) .. Max); - -- This may overflow. - Res.E := A.E + B.E + Max - (Nbr_Digits - 1); - else - Res.S (0 .. Max) := T (0 .. Max); - Res.S (Max + 1 .. Nbr_Digits - 1) := (others => 0); - -- This may overflow. - Res.E := A.E + B.E; - end if; - end Mul; - - procedure Div (Res : out E_Num; A, B: E_Num) - is - Dividend : Uint16_Array (0 .. Nbr_Digits); - A_F : constant Sint16 := First_Digit (A); - B_F : constant Sint16 := First_Digit (B); - - -- Digit corresponding to the first digit of B. - Doff : constant Sint16 := Dividend'Last - B_F; - Q : Uint16; - C, N_C : Uint16; - begin - -- Check for division by 0. - if B_F < 0 then - raise Constraint_Error; - end if; - - -- Copy and shift dividend. - -- Bit 15 of the most significant digit of A becomes bit 0 of the - -- most significant digit of DIVIDEND. Therefore we are sure - -- DIVIDEND < B (after realignment). - C := 0; - for K in 0 .. A_F loop - N_C := Shift_Right (A.S (K), 15); - Dividend (Dividend'Last - A_F - 1 + K) - := Shift_Left (A.S (K), 1) or C; - C := N_C; - end loop; - Dividend (Nbr_Digits) := C; - Dividend (0 .. Dividend'last - 2 - A_F) := (others => 0); - - -- Algorithm is the same as division by hand. - C := 0; - for I in reverse Digit_Range loop - Q := 0; - for J in 0 .. 15 loop - declare - Borrow : Uint32; - Tmp : Uint16_Array (0 .. B_F); - V : Uint32; - V16 : Uint16; - begin - -- Compute TMP := dividend - B; - Borrow := 0; - for K in 0 .. B_F loop - V := Uint32 (B.S (K)) + Borrow; - V16 := Uint16 (V mod Uint16'Modulus); - if V16 > Dividend (Doff + K) then - Borrow := 1; - else - Borrow := 0; - end if; - Tmp (K) := Dividend (Doff + K) - V16; - end loop; - - -- If the last shift creates a carry, we are sure Dividend > B - if C /= 0 then - Borrow := 0; - end if; - - Q := Q * 2; - -- Begin of : Dividend = Dividend * 2 - C := 0; - for K in 0 .. Doff - 1 loop - N_C := Shift_Right (Dividend (K), 15); - Dividend (K) := Shift_Left (Dividend (K), 1) or C; - C := N_C; - end loop; - - if Borrow = 0 then - -- Dividend > B - Q := Q + 1; - -- Dividend = Tmp * 2 - -- = (Dividend - B) * 2 - for K in Doff .. Nbr_Digits loop - N_C := Shift_Right (Tmp (K - Doff), 15); - Dividend (K) := Shift_Left (Tmp (K - Doff), 1) or C; - C := N_C; - end loop; - else - -- Dividend = Dividend * 2 - for K in Doff .. Nbr_Digits loop - N_C := Shift_Right (Dividend (K), 15); - Dividend (K) := Shift_Left (Dividend (K), 1) or C; - C := N_C; - end loop; - end if; - end; - end loop; - Res.S (I) := Q; - end loop; - Res.E := A.E - B.E + (A_F - B_F) - (Nbr_Digits - 1); - end Div; - - procedure To_Float (Res : out Iir_Fp64; Ok : out Boolean; E : E_Num) - is - V : Iir_Fp64; - P : Iir_Fp64; - begin - Res := 0.0; - P := Iir_Fp64'Scaling (1.0, 16 * E.E); - for I in Digit_Range loop - V := Iir_Fp64 (E.S (I)) * P; - P := Iir_Fp64'Scaling (P, 16); - Res := Res + V; - end loop; - Ok := True; - end To_Float; - - function To_E_Num (V : Uint16) return E_Num - is - Res : E_Num; - begin - Res.E := 0; - Res.S := (0 => V, others => 0); - return Res; - end To_E_Num; - - -- Numbers of digits. - Scale : Integer; - Res : E_Num; - - -- LRM 13.4.1 - -- INTEGER ::= DIGIT { [ UNDERLINE ] DIGIT } - -- - -- Update SCALE, RES. - -- The first character must be a digit. - procedure Scan_Integer - is - C : Character; - begin - C := Source (Pos); - loop - -- C is a digit. - Bmul (Res, Res, Character'Pos (C) - Character'Pos ('0'), 10); - Scale := Scale + 1; - - Pos := Pos + 1; - C := Source (Pos); - if C = '_' then - loop - Pos := Pos + 1; - C := Source (Pos); - exit when C /= '_'; - Error_Msg_Scan ("double underscore in number"); - end loop; - if C not in '0' .. '9' then - Error_Msg_Scan ("underscore must be followed by a digit"); - end if; - end if; - exit when C not in '0' .. '9'; - end loop; - end Scan_Integer; - - C : Character; - D : Uint16; - Ok : Boolean; - Has_Dot : Boolean; - Exp : Integer; - Exp_Neg : Boolean; - Base : Uint16; -begin - -- Start with a simple and fast conversion. - C := Source (Pos); - D := 0; - loop - D := D * 10 + Character'Pos (C) - Character'Pos ('0'); - - Pos := Pos + 1; - C := Source (Pos); - if C = '_' then - loop - Pos := Pos + 1; - C := Source (Pos); - exit when C /= '_'; - Error_Msg_Scan ("double underscore in number"); - end loop; - if C not in '0' .. '9' then - Error_Msg_Scan ("underscore must be followed by a digit"); - end if; - end if; - if C not in '0' .. '9' then - if C = '.' or else C = '#' or else (C = 'e' or C = 'E' or C = ':') - then - -- Continue scanning. - Res := To_E_Num (D); - exit; - end if; - - -- Finished. - -- a universal integer. - Current_Token := Tok_Integer; - -- No possible overflow. - Current_Context.Int64 := Iir_Int64 (D); - return; - elsif D >= 6552 then - -- Number may be greather than the uint16 limit. - Scale := 0; - Res := To_E_Num (D); - Scan_Integer; - exit; - end if; - end loop; - - Has_Dot := False; - Base := 10; - - C := Source (Pos); - if C = '.' then - -- Decimal integer. - Has_Dot := True; - Scale := 0; - Pos := Pos + 1; - C := Source (Pos); - if C not in '0' .. '9' then - Error_Msg_Scan ("a dot must be followed by a digit"); - return; - end if; - Scan_Integer; - elsif C = '#' - or else (C = ':' and then (Source (Pos + 1) in '0' .. '9' - or else Source (Pos + 1) in 'a' .. 'f' - or else Source (Pos + 1) in 'A' .. 'F')) - then - -- LRM 13.10 - -- The number sign (#) of a based literal can be replaced by colon (:), - -- provided that the replacement is done for both occurrences. - -- GHDL: correctly handle 'variable v : integer range 0 to 7:= 3'. - -- Is there any other places where a digit can be followed - -- by a colon ? (See IR 1093). - - -- Based integer. - declare - Number_Sign : constant Character := C; - Res_Int : Iir_Int64; - begin - Fix (Res_Int, Ok, Res); - if not Ok or else Res_Int > 16 then - -- LRM 13.4.2 - -- The base must be [...] at most sixteen. - Error_Msg_Scan ("base must be at most 16"); - -- Fallback. - Base := 16; - elsif Res_Int < 2 then - -- LRM 13.4.2 - -- The base must be at least two [...]. - Error_Msg_Scan ("base must be at least 2"); - -- Fallback. - Base := 2; - else - Base := Uint16 (Res_Int); - end if; - - Pos := Pos + 1; - Res := E_Zero; - C := Source (Pos); - loop - if C >= '0' and C <= '9' then - D := Character'Pos (C) - Character'Pos ('0'); - elsif C >= 'A' and C <= 'F' then - D := Character'Pos (C) - Character'Pos ('A') + 10; - elsif C >= 'a' and C <= 'f' then - D := Character'Pos (C) - Character'Pos ('a') + 10; - else - Error_Msg_Scan ("bad extended digit"); - exit; - end if; - - if D >= Base then - -- LRM 13.4.2 - -- The conventional meaning of base notation is - -- assumed; in particular the value of each extended - -- digit of a based literal must be less then the base. - Error_Msg_Scan ("digit beyond base"); - D := 1; - end if; - Pos := Pos + 1; - Bmul (Res, Res, D, Base); - Scale := Scale + 1; - - C := Source (Pos); - if C = '_' then - loop - Pos := Pos + 1; - C := Source (Pos); - exit when C /= '_'; - Error_Msg_Scan ("double underscore in based integer"); - end loop; - elsif C = '.' then - if Has_Dot then - Error_Msg_Scan ("double dot ignored"); - else - Has_Dot := True; - Scale := 0; - end if; - Pos := Pos + 1; - C := Source (Pos); - elsif C = Number_Sign then - Pos := Pos + 1; - exit; - elsif C = '#' or C = ':' then - Error_Msg_Scan ("bad number sign replacement character"); - exit; - end if; - end loop; - end; - end if; - C := Source (Pos); - Exp := 0; - if C = 'E' or else C = 'e' then - Pos := Pos + 1; - C := Source (Pos); - Exp_Neg := False; - if C = '+' then - Pos := Pos + 1; - C := Source (Pos); - elsif C = '-' then - if Has_Dot then - Exp_Neg := True; - else - -- LRM 13.4.1 - -- An exponent for an integer literal must not have a minus sign. - -- - -- LRM 13.4.2 - -- An exponent for a based integer literal must not have a minus - -- sign. - Error_Msg_Scan - ("negative exponent not allowed for integer literal"); - end if; - Pos := Pos + 1; - C := Source (Pos); - end if; - if C not in '0' .. '9' then - Error_Msg_Scan ("digit expected after exponent"); - else - loop - -- C is a digit. - Exp := Exp * 10 + (Character'Pos (C) - Character'Pos ('0')); - - Pos := Pos + 1; - C := Source (Pos); - if C = '_' then - loop - Pos := Pos + 1; - C := Source (Pos); - exit when C /= '_'; - Error_Msg_Scan ("double underscore not allowed in integer"); - end loop; - if C not in '0' .. '9' then - Error_Msg_Scan ("digit expected after underscore"); - exit; - end if; - elsif C not in '0' .. '9' then - exit; - end if; - end loop; - end if; - if Exp_Neg then - Exp := -Exp; - end if; - end if; - - if Has_Dot then - Scale := Scale - Exp; - else - Scale := -Exp; - end if; - if Scale /= 0 then - declare - Scale_Neg : Boolean; - Val_Exp : E_Num; - Val_Pow : E_Num; - begin - if Scale > 0 then - Scale_Neg := True; - else - Scale_Neg := False; - Scale := -Scale; - end if; - - Val_Pow := To_E_Num (Base); - Val_Exp := E_One; - while Scale /= 0 loop - if Scale mod 2 = 1 then - Mul (Val_Exp, Val_Exp, Val_Pow); - end if; - Scale := Scale / 2; - Mul (Val_Pow, Val_Pow, Val_Pow); - end loop; - if Scale_Neg then - Div (Res, Res, Val_Exp); - else - Mul (Res, Res, Val_Exp); - end if; - end; - end if; - - if Has_Dot then - -- a universal real. - Current_Token := Tok_Real; - -- Set to a valid literal, in case of constraint error. - To_Float (Current_Context.Fp64, Ok, Res); - if not Ok then - Error_Msg_Scan ("literal beyond real bounds"); - end if; - else - -- a universal integer. - Current_Token := Tok_Integer; - -- Set to a valid literal, in case of constraint error. - Fix (Current_Context.Int64, Ok, Res); - if not Ok then - Error_Msg_Scan ("literal beyond integer bounds"); - end if; - end if; -exception - when Constraint_Error => - Error_Msg_Scan ("literal overflow"); -end Scan_Literal; |