------------------------------------------------------------------------------ -- -- -- GNAT COMPILER COMPONENTS -- -- -- -- N A M E T -- -- -- -- B o d y -- -- -- -- Copyright (C) 1992-2024, 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. See the GNU General Public License -- -- for more details. You should have received a copy of the GNU General -- -- Public License distributed with GNAT; see file COPYING3. If not, go to -- -- http://www.gnu.org/licenses for a complete copy of the license. -- -- -- -- GNAT was originally developed by the GNAT team at New York University. -- -- Extensive contributions were provided by Ada Core Technologies Inc. -- -- -- ------------------------------------------------------------------------------ -- WARNING: There is a C version of this package. Any changes to this -- source file must be properly reflected in the C header file namet.h -- which is created manually from namet.ads and namet.adb. with Debug; use Debug; with Opt; use Opt; with Output; use Output; with Widechar; with Interfaces; use Interfaces; package body Namet is Name_Chars_Reserve : constant := 5000; Name_Entries_Reserve : constant := 100; -- The names table is locked during gigi processing, since gigi assumes -- that the table does not move. After returning from gigi, the names -- table is unlocked again, since writing library file information needs -- to generate some extra names. To avoid the inefficiency of always -- reallocating during this second unlocked phase, we reserve a bit of -- extra space before doing the release call. Hash_Num : constant Int := 2**16; -- Number of headers in the hash table. Current hash algorithm is closely -- tailored to this choice, so it can only be changed if a corresponding -- change is made to the hash algorithm. Hash_Max : constant Int := Hash_Num - 1; -- Indexes in the hash header table run from 0 to Hash_Num - 1 subtype Hash_Index_Type is Int range 0 .. Hash_Max; -- Range of hash index values Hash_Table : array (Hash_Index_Type) of Name_Id; -- The hash table is used to locate existing entries in the names table. -- The entries point to the first names table entry whose hash value -- matches the hash code. Then subsequent names table entries with the -- same hash code value are linked through the Hash_Link fields. ----------------------- -- Local Subprograms -- ----------------------- function Hash (Buf : Bounded_String) return Hash_Index_Type; pragma Inline (Hash); -- Compute hash code for name stored in Buf procedure Strip_Qualification_And_Suffixes (Buf : in out Bounded_String); -- Given an encoded entity name in Buf, remove package body -- suffix as described for Strip_Package_Body_Suffix, and also remove -- all qualification, i.e. names followed by two underscores. ----------------------------- -- Add_Char_To_Name_Buffer -- ----------------------------- procedure Add_Char_To_Name_Buffer (C : Character) is begin Append (Global_Name_Buffer, C); end Add_Char_To_Name_Buffer; ---------------------------- -- Add_Nat_To_Name_Buffer -- ---------------------------- procedure Add_Nat_To_Name_Buffer (V : Nat) is begin Append (Global_Name_Buffer, V); end Add_Nat_To_Name_Buffer; ---------------------------- -- Add_Str_To_Name_Buffer -- ---------------------------- procedure Add_Str_To_Name_Buffer (S : String) is begin Append (Global_Name_Buffer, S); end Add_Str_To_Name_Buffer; ------------ -- Append -- ------------ procedure Append (Buf : in out Bounded_String; C : Character) is begin Buf.Length := Buf.Length + 1; if Buf.Length > Buf.Chars'Last then Write_Str ("Name buffer overflow; Max_Length = "); Write_Int (Int (Buf.Max_Length)); Write_Line (""); raise Program_Error; end if; Buf.Chars (Buf.Length) := C; end Append; procedure Append (Buf : in out Bounded_String; V : Nat) is begin if V >= 10 then Append (Buf, V / 10); end if; Append (Buf, Character'Val (Character'Pos ('0') + V rem 10)); end Append; procedure Append (Buf : in out Bounded_String; S : String) is First : constant Natural := Buf.Length + 1; begin Buf.Length := Buf.Length + S'Length; if Buf.Length > Buf.Chars'Last then Write_Str ("Name buffer overflow; Max_Length = "); Write_Int (Int (Buf.Max_Length)); Write_Line (""); raise Program_Error; end if; Buf.Chars (First .. Buf.Length) := S; -- A loop calling Append(Character) would be cleaner, but this slice -- assignment is substantially faster. end Append; procedure Append (Buf : in out Bounded_String; Buf2 : Bounded_String) is begin Append (Buf, Buf2.Chars (1 .. Buf2.Length)); end Append; procedure Append (Buf : in out Bounded_String; Id : Valid_Name_Id) is pragma Assert (Is_Valid_Name (Id)); Index : constant Int := Name_Entries.Table (Id).Name_Chars_Index; Len : constant Short := Name_Entries.Table (Id).Name_Len; Chars : Name_Chars.Table_Type renames Name_Chars.Table (Index + 1 .. Index + Int (Len)); begin Append (Buf, String (Chars)); end Append; -------------------- -- Append_Decoded -- -------------------- procedure Append_Decoded (Buf : in out Bounded_String; Id : Valid_Name_Id) is Temp : Bounded_String; function Has_Encodings (Temp : Bounded_String) return Boolean; -- True if Temp contains encoded characters. If not, we can set -- Name_Has_No_Encodings to True below, and never call this again -- on the same Name_Id. function Has_Encodings (Temp : Bounded_String) return Boolean is begin for J in 1 .. Temp.Length loop if Temp.Chars (J) in 'U' | 'W' | 'Q' | 'O' then return True; end if; end loop; return False; end Has_Encodings; begin Append (Temp, Id); -- Skip scan if we already know there are no encodings (i.e. the first -- time this was called on Id, the Has_Encodings call below returned -- False). if Name_Entries.Table (Id).Name_Has_No_Encodings then goto Done; end if; if not Has_Encodings (Temp) then Name_Entries.Table (Id).Name_Has_No_Encodings := True; goto Done; end if; -- Here we have at least some encoding that we must decode Decode : declare New_Len : Natural; Old : Positive; New_Buf : String (1 .. Temp.Chars'Last); procedure Copy_One_Character; -- Copy a character from Temp.Chars to New_Buf. Includes case -- of copying a Uhh,Whhhh,WWhhhhhhhh sequence and decoding it. function Hex (N : Natural) return Word; -- Scans past N digits using Old pointer and returns hex value procedure Insert_Character (C : Character); -- Insert a new character into output decoded name ------------------------ -- Copy_One_Character -- ------------------------ procedure Copy_One_Character is C : Character; begin C := Temp.Chars (Old); -- U (upper half insertion case) if C = 'U' and then Old < Temp.Length and then Temp.Chars (Old + 1) not in 'A' .. 'Z' | '_' then Old := Old + 1; -- If we have upper half encoding, then we have to set an -- appropriate wide character sequence for this character. if Upper_Half_Encoding then Widechar.Set_Wide (Char_Code (Hex (2)), New_Buf, New_Len); -- For other encoding methods, upper half characters can -- simply use their normal representation. else declare W2 : constant Word := Hex (2); begin pragma Assert (W2 <= 255); -- Add assumption to facilitate static analysis. Note -- that we cannot use pragma Assume for bootstrap -- reasons. Insert_Character (Character'Val (W2)); end; end if; -- WW (wide wide character insertion) elsif C = 'W' and then Old < Temp.Length and then Temp.Chars (Old + 1) = 'W' then Old := Old + 2; Widechar.Set_Wide (Char_Code (Hex (8)), New_Buf, New_Len); -- W (wide character insertion) elsif C = 'W' and then Old < Temp.Length and then Temp.Chars (Old + 1) not in 'A' .. 'Z' | '_' then Old := Old + 1; Widechar.Set_Wide (Char_Code (Hex (4)), New_Buf, New_Len); -- Any other character is copied unchanged else Insert_Character (C); Old := Old + 1; end if; end Copy_One_Character; --------- -- Hex -- --------- function Hex (N : Natural) return Word is T : Word := 0; C : Character; begin for J in 1 .. N loop C := Temp.Chars (Old); Old := Old + 1; pragma Assert (C in '0' .. '9' | 'a' .. 'f'); if C <= '9' then T := 16 * T + Character'Pos (C) - Character'Pos ('0'); else -- C in 'a' .. 'f' T := 16 * T + Character'Pos (C) - (Character'Pos ('a') - 10); end if; end loop; return T; end Hex; ---------------------- -- Insert_Character -- ---------------------- procedure Insert_Character (C : Character) is begin New_Len := New_Len + 1; New_Buf (New_Len) := C; end Insert_Character; -- Start of processing for Decode begin New_Len := 0; Old := 1; -- Loop through characters of name while Old <= Temp.Length loop -- Case of character literal, put apostrophes around character if Temp.Chars (Old) = 'Q' and then Old < Temp.Length then Old := Old + 1; Insert_Character ('''); Copy_One_Character; Insert_Character ('''); -- Case of operator name elsif Temp.Chars (Old) = 'O' and then Old < Temp.Length and then Temp.Chars (Old + 1) not in 'A' .. 'Z' | '_' then Old := Old + 1; declare -- This table maps the 2nd and 3rd characters of the name -- into the required output. Two blanks means leave the -- name alone Map : constant String := "ab " & -- Oabs => "abs" "ad+ " & -- Oadd => "+" "an " & -- Oand => "and" "co& " & -- Oconcat => "&" "di/ " & -- Odivide => "/" "eq= " & -- Oeq => "=" "ex**" & -- Oexpon => "**" "gt> " & -- Ogt => ">" "ge>=" & -- Oge => ">=" "le<=" & -- Ole => "<=" "lt< " & -- Olt => "<" "mo " & -- Omod => "mod" "mu* " & -- Omutliply => "*" "ne/=" & -- One => "/=" "no " & -- Onot => "not" "or " & -- Oor => "or" "re " & -- Orem => "rem" "su- " & -- Osubtract => "-" "xo "; -- Oxor => "xor" J : Integer; begin Insert_Character ('"'); -- Search the map. Note that this loop must terminate, if -- not we have some kind of internal error, and a constraint -- error may be raised. J := Map'First; loop exit when Temp.Chars (Old) = Map (J) and then Temp.Chars (Old + 1) = Map (J + 1); J := J + 4; end loop; -- Special operator name if Map (J + 2) /= ' ' then Insert_Character (Map (J + 2)); if Map (J + 3) /= ' ' then Insert_Character (Map (J + 3)); end if; Insert_Character ('"'); -- Skip past original operator name in input while Old <= Temp.Length and then Temp.Chars (Old) in 'a' .. 'z' loop Old := Old + 1; end loop; -- For other operator names, leave them in lower case, -- surrounded by apostrophes else -- Copy original operator name from input to output while Old <= Temp.Length and then Temp.Chars (Old) in 'a' .. 'z' loop Copy_One_Character; end loop; Insert_Character ('"'); end if; end; -- Else copy one character and keep going else Copy_One_Character; end if; end loop; -- Copy new buffer as result Temp.Length := New_Len; Temp.Chars (1 .. New_Len) := New_Buf (1 .. New_Len); end Decode; <> Append (Buf, Temp); end Append_Decoded; ---------------------------------- -- Append_Decoded_With_Brackets -- ---------------------------------- procedure Append_Decoded_With_Brackets (Buf : in out Bounded_String; Id : Valid_Name_Id) is P : Natural; begin -- Case of operator name, normal decoding is fine if Buf.Chars (1) = 'O' then Append_Decoded (Buf, Id); -- For character literals, normal decoding is fine elsif Buf.Chars (1) = 'Q' then Append_Decoded (Buf, Id); -- Only remaining issue is U/W/WW sequences else declare Temp : Bounded_String; begin Append (Temp, Id); P := 1; while P < Temp.Length loop if Temp.Chars (P + 1) in 'A' .. 'Z' then P := P + 1; -- Uhh encoding elsif Temp.Chars (P) = 'U' then for J in reverse P + 3 .. P + Temp.Length loop Temp.Chars (J + 3) := Temp.Chars (J); end loop; Temp.Length := Temp.Length + 3; Temp.Chars (P + 3) := Temp.Chars (P + 2); Temp.Chars (P + 2) := Temp.Chars (P + 1); Temp.Chars (P) := '['; Temp.Chars (P + 1) := '"'; Temp.Chars (P + 4) := '"'; Temp.Chars (P + 5) := ']'; P := P + 6; -- WWhhhhhhhh encoding elsif Temp.Chars (P) = 'W' and then P + 9 <= Temp.Length and then Temp.Chars (P + 1) = 'W' and then Temp.Chars (P + 2) not in 'A' .. 'Z' | '_' then Temp.Chars (P + 12 .. Temp.Length + 2) := Temp.Chars (P + 10 .. Temp.Length); Temp.Chars (P) := '['; Temp.Chars (P + 1) := '"'; Temp.Chars (P + 10) := '"'; Temp.Chars (P + 11) := ']'; Temp.Length := Temp.Length + 2; P := P + 12; -- Whhhh encoding elsif Temp.Chars (P) = 'W' and then P < Temp.Length and then Temp.Chars (P + 1) not in 'A' .. 'Z' | '_' then Temp.Chars (P + 8 .. P + Temp.Length + 3) := Temp.Chars (P + 5 .. Temp.Length); Temp.Chars (P + 2 .. P + 5) := Temp.Chars (P + 1 .. P + 4); Temp.Chars (P) := '['; Temp.Chars (P + 1) := '"'; Temp.Chars (P + 6) := '"'; Temp.Chars (P + 7) := ']'; Temp.Length := Temp.Length + 3; P := P + 8; else P := P + 1; end if; end loop; Append (Buf, Temp); end; end if; end Append_Decoded_With_Brackets; -------------------- -- Append_Encoded -- -------------------- procedure Append_Encoded (Buf : in out Bounded_String; C : Char_Code) is procedure Set_Hex_Chars (C : Char_Code); -- Stores given value, which is in the range 0 .. 255, as two hex -- digits (using lower case a-f) in Buf.Chars, incrementing Buf.Length. ------------------- -- Set_Hex_Chars -- ------------------- procedure Set_Hex_Chars (C : Char_Code) is Hexd : constant String := "0123456789abcdef"; N : constant Natural := Natural (C); begin Buf.Chars (Buf.Length + 1) := Hexd (N / 16 + 1); Buf.Chars (Buf.Length + 2) := Hexd (N mod 16 + 1); Buf.Length := Buf.Length + 2; end Set_Hex_Chars; -- Start of processing for Append_Encoded begin Buf.Length := Buf.Length + 1; if In_Character_Range (C) then declare CC : constant Character := Get_Character (C); begin if CC in 'a' .. 'z' | '0' .. '9' then Buf.Chars (Buf.Length) := CC; else Buf.Chars (Buf.Length) := 'U'; Set_Hex_Chars (C); end if; end; elsif In_Wide_Character_Range (C) then Buf.Chars (Buf.Length) := 'W'; Set_Hex_Chars (C / 256); Set_Hex_Chars (C mod 256); else Buf.Chars (Buf.Length) := 'W'; Buf.Length := Buf.Length + 1; Buf.Chars (Buf.Length) := 'W'; Set_Hex_Chars (C / 2 ** 24); Set_Hex_Chars ((C / 2 ** 16) mod 256); Set_Hex_Chars ((C / 256) mod 256); Set_Hex_Chars (C mod 256); end if; end Append_Encoded; ------------------------ -- Append_Unqualified -- ------------------------ procedure Append_Unqualified (Buf : in out Bounded_String; Id : Valid_Name_Id) is Temp : Bounded_String; begin Append (Temp, Id); Strip_Qualification_And_Suffixes (Temp); Append (Buf, Temp); end Append_Unqualified; -------------------------------- -- Append_Unqualified_Decoded -- -------------------------------- procedure Append_Unqualified_Decoded (Buf : in out Bounded_String; Id : Valid_Name_Id) is Temp : Bounded_String; begin Append_Decoded (Temp, Id); Strip_Qualification_And_Suffixes (Temp); Append (Buf, Temp); end Append_Unqualified_Decoded; -------------------------------- -- Destroy_Global_Name_Buffer -- -------------------------------- procedure Destroy_Global_Name_Buffer is procedure Do_It; -- Do the work. Needed only for "pragma Debug" below, so we don't do -- anything in production mode. procedure Do_It is begin Global_Name_Buffer.Length := Global_Name_Buffer.Max_Length; Global_Name_Buffer.Chars := (others => '!'); end Do_It; pragma Debug (Do_It); begin null; end Destroy_Global_Name_Buffer; -------------- -- Finalize -- -------------- procedure Finalize is F : array (Int range 0 .. 50) of Int; -- N'th entry is the number of chains of length N, except last entry, -- which is the number of chains of length F'Last or more. Max_Chain_Length : Nat := 0; -- Maximum length of all chains Probes : Nat := 0; -- Used to compute average number of probes Nsyms : Nat := 0; -- Number of symbols in table Verbosity : constant Int range 1 .. 3 := 1; pragma Warnings (Off, Verbosity); -- This constant indicates the level of verbosity in the output from -- this procedure. Currently this can only be changed by editing the -- declaration above and recompiling. That's good enough in practice, -- since we very rarely need to use this debug option. Settings are: -- -- 1 => print basic summary information -- 2 => in addition print number of entries per hash chain -- 3 => in addition print content of entries Zero : constant Int := Character'Pos ('0'); begin if not Debug_Flag_H then return; end if; for J in F'Range loop F (J) := 0; end loop; for J in Hash_Index_Type loop if Hash_Table (J) = No_Name then F (0) := F (0) + 1; else declare C : Nat; N : Name_Id; S : Int; begin C := 0; N := Hash_Table (J); while N /= No_Name loop N := Name_Entries.Table (N).Hash_Link; C := C + 1; end loop; Nsyms := Nsyms + 1; Probes := Probes + (1 + C) * 100; if C > Max_Chain_Length then Max_Chain_Length := C; end if; if Verbosity >= 2 then Write_Str ("Hash_Table ("); Write_Int (J); Write_Str (") has "); Write_Int (C); Write_Str (" entries"); Write_Eol; end if; if C < F'Last then F (C) := F (C) + 1; else F (F'Last) := F (F'Last) + 1; end if; if Verbosity >= 3 then N := Hash_Table (J); while N /= No_Name loop S := Name_Entries.Table (N).Name_Chars_Index; Write_Str (" "); for J in 1 .. Name_Entries.Table (N).Name_Len loop Write_Char (Name_Chars.Table (S + Int (J))); end loop; Write_Eol; N := Name_Entries.Table (N).Hash_Link; end loop; end if; end; end if; end loop; Write_Eol; for J in F'Range loop if F (J) /= 0 then Write_Str ("Number of hash chains of length "); if J < 10 then Write_Char (' '); end if; Write_Int (J); if J = F'Last then Write_Str (" or greater"); end if; Write_Str (" = "); Write_Int (F (J)); Write_Eol; end if; end loop; -- Print out average number of probes, in the case where Name_Find is -- called for a string that is already in the table. Write_Eol; Write_Str ("Average number of probes for lookup = "); pragma Assert (Nsyms /= 0); -- Add assumption to facilitate static analysis. Here Nsyms cannot be -- zero because many symbols are added to the table by default. Probes := Probes / Nsyms; Write_Int (Probes / 200); Write_Char ('.'); Probes := (Probes mod 200) / 2; Write_Char (Character'Val (Zero + Probes / 10)); Write_Char (Character'Val (Zero + Probes mod 10)); Write_Eol; Write_Str ("Max_Chain_Length = "); Write_Int (Max_Chain_Length); Write_Eol; Write_Str ("Name_Chars'Length = "); Write_Int (Name_Chars.Last - Name_Chars.First + 1); Write_Eol; Write_Str ("Name_Entries'Length = "); Write_Int (Int (Name_Entries.Last - Name_Entries.First + 1)); Write_Eol; Write_Str ("Nsyms = "); Write_Int (Nsyms); Write_Eol; end Finalize; ----------------------------- -- Get_Decoded_Name_String -- ----------------------------- procedure Get_Decoded_Name_String (Id : Valid_Name_Id) is begin Global_Name_Buffer.Length := 0; Append_Decoded (Global_Name_Buffer, Id); end Get_Decoded_Name_String; ------------------------------------------- -- Get_Decoded_Name_String_With_Brackets -- ------------------------------------------- procedure Get_Decoded_Name_String_With_Brackets (Id : Valid_Name_Id) is begin Global_Name_Buffer.Length := 0; Append_Decoded_With_Brackets (Global_Name_Buffer, Id); end Get_Decoded_Name_String_With_Brackets; ------------------------ -- Get_Last_Two_Chars -- ------------------------ procedure Get_Last_Two_Chars (N : Valid_Name_Id; C1 : out Character; C2 : out Character) is NE : Name_Entry renames Name_Entries.Table (N); NEL : constant Int := Int (NE.Name_Len); begin if NEL >= 2 then C1 := Name_Chars.Table (NE.Name_Chars_Index + NEL - 1); C2 := Name_Chars.Table (NE.Name_Chars_Index + NEL - 0); else C1 := ASCII.NUL; C2 := ASCII.NUL; end if; end Get_Last_Two_Chars; --------------------- -- Get_Name_String -- --------------------- procedure Get_Name_String (Id : Valid_Name_Id) is begin Global_Name_Buffer.Length := 0; Append (Global_Name_Buffer, Id); end Get_Name_String; function Get_Name_String (Id : Valid_Name_Id) return String is Buf : Bounded_String (Max_Length => Natural (Length_Of_Name (Id))); begin Append (Buf, Id); return +Buf; end Get_Name_String; -------------------------------- -- Get_Name_String_And_Append -- -------------------------------- procedure Get_Name_String_And_Append (Id : Valid_Name_Id) is begin Append (Global_Name_Buffer, Id); end Get_Name_String_And_Append; ----------------------------- -- Get_Name_Table_Boolean1 -- ----------------------------- function Get_Name_Table_Boolean1 (Id : Valid_Name_Id) return Boolean is begin pragma Assert (Is_Valid_Name (Id)); return Name_Entries.Table (Id).Boolean1_Info; end Get_Name_Table_Boolean1; ----------------------------- -- Get_Name_Table_Boolean2 -- ----------------------------- function Get_Name_Table_Boolean2 (Id : Valid_Name_Id) return Boolean is begin pragma Assert (Is_Valid_Name (Id)); return Name_Entries.Table (Id).Boolean2_Info; end Get_Name_Table_Boolean2; ----------------------------- -- Get_Name_Table_Boolean3 -- ----------------------------- function Get_Name_Table_Boolean3 (Id : Valid_Name_Id) return Boolean is begin pragma Assert (Is_Valid_Name (Id)); return Name_Entries.Table (Id).Boolean3_Info; end Get_Name_Table_Boolean3; ------------------------- -- Get_Name_Table_Byte -- ------------------------- function Get_Name_Table_Byte (Id : Valid_Name_Id) return Byte is begin pragma Assert (Is_Valid_Name (Id)); return Name_Entries.Table (Id).Byte_Info; end Get_Name_Table_Byte; ------------------------- -- Get_Name_Table_Int -- ------------------------- function Get_Name_Table_Int (Id : Valid_Name_Id) return Int is begin pragma Assert (Is_Valid_Name (Id)); return Name_Entries.Table (Id).Int_Info; end Get_Name_Table_Int; ----------------------------------------- -- Get_Unqualified_Decoded_Name_String -- ----------------------------------------- procedure Get_Unqualified_Decoded_Name_String (Id : Valid_Name_Id) is begin Global_Name_Buffer.Length := 0; Append_Unqualified_Decoded (Global_Name_Buffer, Id); end Get_Unqualified_Decoded_Name_String; --------------------------------- -- Get_Unqualified_Name_String -- --------------------------------- procedure Get_Unqualified_Name_String (Id : Valid_Name_Id) is begin Global_Name_Buffer.Length := 0; Append_Unqualified (Global_Name_Buffer, Id); end Get_Unqualified_Name_String; ---------- -- Hash -- ---------- function Hash (Buf : Bounded_String) return Hash_Index_Type is -- This hash function looks at every character, in order to make it -- likely that similar strings get different hash values. The rotate by -- 7 bits has been determined empirically to be good, and it doesn't -- lose bits like a shift would. The final conversion can't overflow, -- because the table is 2**16 in size. This function probably needs to -- be changed if the hash table size is changed. -- Note that we could get some speed improvement by aligning the string -- to 32 or 64 bits, and doing word-wise xor's. We could also implement -- a growable table. It doesn't seem worth the trouble to do those -- things, for now. Result : Unsigned_16 := 0; begin for J in 1 .. Buf.Length loop Result := Rotate_Left (Result, 7) xor Character'Pos (Buf.Chars (J)); end loop; return Hash_Index_Type (Result); end Hash; ---------------- -- Initialize -- ---------------- procedure Initialize is begin null; end Initialize; ---------------- -- Insert_Str -- ---------------- procedure Insert_Str (Buf : in out Bounded_String; S : String; Index : Positive) is SL : constant Natural := S'Length; begin Buf.Chars (Index + SL .. Buf.Length + SL) := Buf.Chars (Index .. Buf.Length); Buf.Chars (Index .. Index + SL - 1) := S; Buf.Length := Buf.Length + SL; end Insert_Str; ------------------------------- -- Insert_Str_In_Name_Buffer -- ------------------------------- procedure Insert_Str_In_Name_Buffer (S : String; Index : Positive) is begin Insert_Str (Global_Name_Buffer, S, Index); end Insert_Str_In_Name_Buffer; ---------------------- -- Is_Internal_Name -- ---------------------- function Is_Internal_Name (Buf : Bounded_String) return Boolean is J : Natural; begin -- Any name starting or ending with underscore is internal if Buf.Chars (1) = '_' or else Buf.Chars (Buf.Length) = '_' then return True; -- Allow quoted character elsif Buf.Chars (1) = ''' then return False; -- All other cases, scan name else -- Test backwards, because we only want to test the last entity -- name if the name we have is qualified with other entities. J := Buf.Length; while J /= 0 loop -- Skip stuff between brackets (A-F OK there) if Buf.Chars (J) = ']' then loop J := J - 1; exit when J = 1 or else Buf.Chars (J) = '['; end loop; -- Test for internal letter elsif Is_OK_Internal_Letter (Buf.Chars (J)) then return True; -- Quit if we come to terminating double underscore (note that -- if the current character is an underscore, we know that -- there is a previous character present, since we already -- filtered out the case of Buf.Chars (1) = '_' above. elsif Buf.Chars (J) = '_' and then Buf.Chars (J - 1) = '_' and then Buf.Chars (J - 2) /= '_' then return False; end if; J := J - 1; end loop; end if; return False; end Is_Internal_Name; function Is_Internal_Name (Id : Valid_Name_Id) return Boolean is Buf : Bounded_String (Max_Length => Natural (Length_Of_Name (Id))); begin Append (Buf, Id); return Is_Internal_Name (Buf); end Is_Internal_Name; function Is_Internal_Name return Boolean is begin return Is_Internal_Name (Global_Name_Buffer); end Is_Internal_Name; --------------------------- -- Is_OK_Internal_Letter -- --------------------------- function Is_OK_Internal_Letter (C : Character) return Boolean is begin return C in 'A' .. 'Z' and then C not in 'O' | 'Q' | 'U' | 'W' | 'X'; end Is_OK_Internal_Letter; ---------------------- -- Is_Operator_Name -- ---------------------- function Is_Operator_Name (Id : Valid_Name_Id) return Boolean is S : Int; begin pragma Assert (Is_Valid_Name (Id)); S := Name_Entries.Table (Id).Name_Chars_Index; return Name_Chars.Table (S + 1) = 'O'; end Is_Operator_Name; ------------------- -- Is_Valid_Name -- ------------------- function Is_Valid_Name (Id : Name_Id) return Boolean is begin return Id in Name_Entries.First .. Name_Entries.Last; end Is_Valid_Name; ------------------ -- Last_Name_Id -- ------------------ function Last_Name_Id return Name_Id is begin return Name_Id (Int (First_Name_Id) + Name_Entries_Count - 1); end Last_Name_Id; -------------------- -- Length_Of_Name -- -------------------- function Length_Of_Name (Id : Valid_Name_Id) return Nat is begin return Int (Name_Entries.Table (Id).Name_Len); end Length_Of_Name; ---------- -- Lock -- ---------- procedure Lock is begin Name_Chars.Set_Last (Name_Chars.Last + Name_Chars_Reserve); Name_Entries.Set_Last (Name_Entries.Last + Name_Entries_Reserve); Name_Chars.Release; Name_Chars.Locked := True; Name_Entries.Release; Name_Entries.Locked := True; end Lock; ---------------- -- Name_Enter -- ---------------- function Name_Enter (Buf : Bounded_String := Global_Name_Buffer) return Valid_Name_Id is begin Name_Entries.Append ((Name_Chars_Index => Name_Chars.Last, Name_Len => Short (Buf.Length), Byte_Info => 0, Int_Info => 0, Hash_Link => No_Name, Name_Has_No_Encodings => False, Boolean1_Info => False, Boolean2_Info => False, Boolean3_Info => False, Spare => False)); -- Set corresponding string entry in the Name_Chars table for J in 1 .. Buf.Length loop Name_Chars.Append (Buf.Chars (J)); end loop; Name_Chars.Append (ASCII.NUL); return Name_Entries.Last; end Name_Enter; function Name_Enter (S : String) return Valid_Name_Id is Buf : Bounded_String (Max_Length => S'Length); begin Append (Buf, S); return Name_Enter (Buf); end Name_Enter; ------------------------ -- Name_Entries_Count -- ------------------------ function Name_Entries_Count return Nat is begin return Int (Name_Entries.Last - Name_Entries.First + 1); end Name_Entries_Count; --------------- -- Name_Find -- --------------- function Name_Find (Buf : Bounded_String := Global_Name_Buffer) return Valid_Name_Id is New_Id : Name_Id; -- Id of entry in hash search, and value to be returned S : Int; -- Pointer into string table Hash_Index : Hash_Index_Type; -- Computed hash index Result : Valid_Name_Id; begin -- Quick handling for one character names if Buf.Length = 1 then Result := First_Name_Id + Character'Pos (Buf.Chars (1)); -- Otherwise search hash table for existing matching entry else Hash_Index := Namet.Hash (Buf); New_Id := Hash_Table (Hash_Index); if New_Id = No_Name then Hash_Table (Hash_Index) := Name_Entries.Last + 1; else Search : loop if Buf.Length /= Integer (Name_Entries.Table (New_Id).Name_Len) then goto No_Match; end if; S := Name_Entries.Table (New_Id).Name_Chars_Index; for J in 1 .. Buf.Length loop if Name_Chars.Table (S + Int (J)) /= Buf.Chars (J) then goto No_Match; end if; end loop; Result := New_Id; goto Done; -- Current entry in hash chain does not match <> if Name_Entries.Table (New_Id).Hash_Link /= No_Name then New_Id := Name_Entries.Table (New_Id).Hash_Link; else Name_Entries.Table (New_Id).Hash_Link := Name_Entries.Last + 1; exit Search; end if; end loop Search; end if; -- We fall through here only if a matching entry was not found in the -- hash table. We now create a new entry in the names table. The hash -- link pointing to the new entry (Name_Entries.Last+1) has been set. Name_Entries.Append ((Name_Chars_Index => Name_Chars.Last, Name_Len => Short (Buf.Length), Hash_Link => No_Name, Int_Info => 0, Byte_Info => 0, Name_Has_No_Encodings => False, Boolean1_Info => False, Boolean2_Info => False, Boolean3_Info => False, Spare => False)); -- Set corresponding string entry in the Name_Chars table for J in 1 .. Buf.Length loop Name_Chars.Append (Buf.Chars (J)); end loop; Name_Chars.Append (ASCII.NUL); Result := Name_Entries.Last; end if; <> return Result; end Name_Find; function Name_Find (S : String) return Valid_Name_Id is Buf : Bounded_String (Max_Length => S'Length); begin Append (Buf, S); return Name_Find (Buf); end Name_Find; ----------------- -- Name_Equals -- ----------------- function Name_Equals (N1 : Valid_Name_Id; N2 : Valid_Name_Id) return Boolean is begin return N1 = N2 or else Get_Name_String (N1) = Get_Name_String (N2); end Name_Equals; ------------- -- Present -- ------------- function Present (Nam : File_Name_Type) return Boolean is begin return Nam /= No_File; end Present; ------------- -- Present -- ------------- function Present (Nam : Name_Id) return Boolean is begin return Nam /= No_Name; end Present; ------------- -- Present -- ------------- function Present (Nam : Unit_Name_Type) return Boolean is begin return Nam /= No_Unit_Name; end Present; ------------------ -- Reinitialize -- ------------------ procedure Reinitialize is begin Name_Chars.Init; Name_Entries.Init; -- Initialize entries for one character names for C in Character loop Name_Entries.Append ((Name_Chars_Index => Name_Chars.Last, Name_Len => 1, Byte_Info => 0, Int_Info => 0, Hash_Link => No_Name, Name_Has_No_Encodings => True, Boolean1_Info => False, Boolean2_Info => False, Boolean3_Info => False, Spare => False)); Name_Chars.Append (C); Name_Chars.Append (ASCII.NUL); end loop; -- Clear hash table for J in Hash_Index_Type loop Hash_Table (J) := No_Name; end loop; end Reinitialize; ---------------------- -- Reset_Name_Table -- ---------------------- procedure Reset_Name_Table is begin for J in First_Name_Id .. Name_Entries.Last loop Name_Entries.Table (J).Int_Info := 0; Name_Entries.Table (J).Byte_Info := 0; end loop; end Reset_Name_Table; -------------------------------- -- Set_Character_Literal_Name -- -------------------------------- procedure Set_Character_Literal_Name (Buf : in out Bounded_String; C : Char_Code) is begin Buf.Length := 0; Append (Buf, 'Q'); Append_Encoded (Buf, C); end Set_Character_Literal_Name; procedure Set_Character_Literal_Name (C : Char_Code) is begin Set_Character_Literal_Name (Global_Name_Buffer, C); end Set_Character_Literal_Name; ----------------------------- -- Set_Name_Table_Boolean1 -- ----------------------------- procedure Set_Name_Table_Boolean1 (Id : Valid_Name_Id; Val : Boolean) is begin pragma Assert (Is_Valid_Name (Id)); Name_Entries.Table (Id).Boolean1_Info := Val; end Set_Name_Table_Boolean1; ----------------------------- -- Set_Name_Table_Boolean2 -- ----------------------------- procedure Set_Name_Table_Boolean2 (Id : Valid_Name_Id; Val : Boolean) is begin pragma Assert (Is_Valid_Name (Id)); Name_Entries.Table (Id).Boolean2_Info := Val; end Set_Name_Table_Boolean2; ----------------------------- -- Set_Name_Table_Boolean3 -- ----------------------------- procedure Set_Name_Table_Boolean3 (Id : Valid_Name_Id; Val : Boolean) is begin pragma Assert (Is_Valid_Name (Id)); Name_Entries.Table (Id).Boolean3_Info := Val; end Set_Name_Table_Boolean3; ------------------------- -- Set_Name_Table_Byte -- ------------------------- procedure Set_Name_Table_Byte (Id : Valid_Name_Id; Val : Byte) is begin pragma Assert (Is_Valid_Name (Id)); Name_Entries.Table (Id).Byte_Info := Val; end Set_Name_Table_Byte; ------------------------- -- Set_Name_Table_Int -- ------------------------- procedure Set_Name_Table_Int (Id : Valid_Name_Id; Val : Int) is begin pragma Assert (Is_Valid_Name (Id)); Name_Entries.Table (Id).Int_Info := Val; end Set_Name_Table_Int; ----------------------------- -- Store_Encoded_Character -- ----------------------------- procedure Store_Encoded_Character (C : Char_Code) is begin Append_Encoded (Global_Name_Buffer, C); end Store_Encoded_Character; -------------------------------------- -- Strip_Qualification_And_Suffixes -- -------------------------------------- procedure Strip_Qualification_And_Suffixes (Buf : in out Bounded_String) is J : Integer; begin -- Strip package body qualification string off end for J in reverse 2 .. Buf.Length loop if Buf.Chars (J) = 'X' then Buf.Length := J - 1; exit; end if; exit when Buf.Chars (J) not in 'b' | 'n' | 'p'; end loop; -- Find rightmost __ or $ separator if one exists. First we position -- to start the search. If we have a character constant, position -- just before it, otherwise position to last character but one if Buf.Chars (Buf.Length) = ''' then J := Buf.Length - 2; while J > 0 and then Buf.Chars (J) /= ''' loop J := J - 1; end loop; else J := Buf.Length - 1; end if; -- Loop to search for rightmost __ or $ (homonym) separator while J > 1 loop -- If $ separator, homonym separator, so strip it and keep looking if Buf.Chars (J) = '$' then Buf.Length := J - 1; J := Buf.Length - 1; -- Else check for __ found elsif Buf.Chars (J) = '_' and then Buf.Chars (J + 1) = '_' then -- Found __ so see if digit follows, and if so, this is a -- homonym separator, so strip it and keep looking. if Buf.Chars (J + 2) in '0' .. '9' then Buf.Length := J - 1; J := Buf.Length - 1; -- If not a homonym separator, then we simply strip the -- separator and everything that precedes it, and we are done else Buf.Chars (1 .. Buf.Length - J - 1) := Buf.Chars (J + 2 .. Buf.Length); Buf.Length := Buf.Length - J - 1; exit; end if; else J := J - 1; end if; end loop; end Strip_Qualification_And_Suffixes; --------------- -- To_String -- --------------- function To_String (Buf : Bounded_String) return String is begin return Buf.Chars (1 .. Buf.Length); end To_String; ------------ -- Unlock -- ------------ procedure Unlock is begin Name_Chars.Locked := False; Name_Chars.Set_Last (Name_Chars.Last - Name_Chars_Reserve); Name_Chars.Release; Name_Entries.Locked := False; Name_Entries.Set_Last (Name_Entries.Last - Name_Entries_Reserve); Name_Entries.Release; end Unlock; -------- -- wn -- -------- procedure wn (Id : Name_Id) is begin Write_Name_For_Debug (Id); Write_Eol; end wn; ---------------- -- Write_Name -- ---------------- procedure Write_Name (Id : Valid_Name_Id) is Buf : Bounded_String (Max_Length => Natural (Length_Of_Name (Id))); begin Append (Buf, Id); Write_Str (Buf.Chars (1 .. Buf.Length)); end Write_Name; ------------------------ -- Write_Name_Decoded -- ------------------------ procedure Write_Name_Decoded (Id : Valid_Name_Id) is Buf : Bounded_String; begin Append_Decoded (Buf, Id); Write_Str (Buf.Chars (1 .. Buf.Length)); end Write_Name_Decoded; -------------------------- -- Write_Name_For_Debug -- -------------------------- procedure Write_Name_For_Debug (Id : Name_Id; Quote : String := "") is begin if Is_Valid_Name (Id) then Write_Str (Quote); declare Buf : Bounded_String (Max_Length => Natural (Length_Of_Name (Id))); begin Append (Buf, Id); Write_Str (Buf.Chars (1 .. Buf.Length)); end; Write_Str (Quote); elsif Id = No_Name then Write_Str (""); elsif Id = Error_Name then Write_Str (""); else Write_Str (""); end if; end Write_Name_For_Debug; -- Package initialization, initialize tables begin Reinitialize; end Namet;