------------------------------------------------------------------------------ -- -- -- GNAT COMPILER COMPONENTS -- -- -- -- P R E P -- -- -- -- B o d y -- -- -- -- Copyright (C) 2002-2003, 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 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 -- -- 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. -- -- -- -- GNAT was originally developed by the GNAT team at New York University. -- -- Extensive contributions were provided by Ada Core Technologies Inc. -- -- -- ------------------------------------------------------------------------------ with Csets; use Csets; with Err_Vars; use Err_Vars; with Namet; use Namet; with Opt; use Opt; with Osint; use Osint; with Output; use Output; with Scans; use Scans; with Snames; use Snames; with Sinput; with Stringt; use Stringt; with Table; with Types; use Types; with GNAT.Heap_Sort_G; package body Prep is use Symbol_Table; type Token_Name_Array is array (Token_Type) of Name_Id; Token_Names : constant Token_Name_Array := (Tok_Abort => Name_Abort, Tok_Abs => Name_Abs, Tok_Abstract => Name_Abstract, Tok_Accept => Name_Accept, Tok_Aliased => Name_Aliased, Tok_All => Name_All, Tok_Array => Name_Array, Tok_And => Name_And, Tok_At => Name_At, Tok_Begin => Name_Begin, Tok_Body => Name_Body, Tok_Case => Name_Case, Tok_Constant => Name_Constant, Tok_Declare => Name_Declare, Tok_Delay => Name_Delay, Tok_Delta => Name_Delta, Tok_Digits => Name_Digits, Tok_Else => Name_Else, Tok_Elsif => Name_Elsif, Tok_End => Name_End, Tok_Entry => Name_Entry, Tok_Exception => Name_Exception, Tok_Exit => Name_Exit, Tok_For => Name_For, Tok_Function => Name_Function, Tok_Generic => Name_Generic, Tok_Goto => Name_Goto, Tok_If => Name_If, Tok_Is => Name_Is, Tok_Limited => Name_Limited, Tok_Loop => Name_Loop, Tok_Mod => Name_Mod, Tok_New => Name_New, Tok_Null => Name_Null, Tok_Of => Name_Of, Tok_Or => Name_Or, Tok_Others => Name_Others, Tok_Out => Name_Out, Tok_Package => Name_Package, Tok_Pragma => Name_Pragma, Tok_Private => Name_Private, Tok_Procedure => Name_Procedure, Tok_Protected => Name_Protected, Tok_Raise => Name_Raise, Tok_Range => Name_Range, Tok_Record => Name_Record, Tok_Rem => Name_Rem, Tok_Renames => Name_Renames, Tok_Requeue => Name_Requeue, Tok_Return => Name_Return, Tok_Reverse => Name_Reverse, Tok_Select => Name_Select, Tok_Separate => Name_Separate, Tok_Subtype => Name_Subtype, Tok_Tagged => Name_Tagged, Tok_Task => Name_Task, Tok_Terminate => Name_Terminate, Tok_Then => Name_Then, Tok_Type => Name_Type, Tok_Until => Name_Until, Tok_Use => Name_Use, Tok_When => Name_When, Tok_While => Name_While, Tok_With => Name_With, Tok_Xor => Name_Xor, others => No_Name); Already_Initialized : Boolean := False; -- Used to avoid repetition of the part of the initialisation that needs -- to be done only once. Empty_String : String_Id; -- "", as a string_id String_False : String_Id; -- "false", as a string_id Name_Defined : Name_Id; -- defined, as a name_id --------------- -- Behaviour -- --------------- -- Accesses to procedure specified by procedure Initialize. Error_Msg : Error_Msg_Proc; -- Report an error Scan : Scan_Proc; -- Scan one token Set_Ignore_Errors : Set_Ignore_Errors_Proc; -- Indicate if error should be taken into account Put_Char : Put_Char_Proc; -- Output one character New_EOL : New_EOL_Proc; -- Output an end of line indication ------------------------------- -- State of the Preprocessor -- ------------------------------- type Pp_State is record If_Ptr : Source_Ptr; -- The location of the #if statement. -- Used to flag #if with no corresponding #end if, at the end. Else_Ptr : Source_Ptr; -- The location of the #else statement. -- Used to detect multiple #else. Deleting : Boolean; -- Set to True when the code should be deleted or commented out. Match_Seen : Boolean; -- Set to True when a condition in an #if or an #elsif is True. -- Also set to True if Deleting at the previous level is True. -- Used to decide if Deleting should be set to True in a following -- #elsif or #else. end record; type Pp_Depth is new Nat; Ground : constant Pp_Depth := 0; package Pp_States is new Table.Table (Table_Component_Type => Pp_State, Table_Index_Type => Pp_Depth, Table_Low_Bound => 1, Table_Initial => 10, Table_Increment => 10, Table_Name => "Prep.Pp_States"); -- A stack of the states of the preprocessor, for nested #if type Operator is (None, Op_Or, Op_And); ----------------- -- Subprograms -- ----------------- function Deleting return Boolean; -- Return True if code should be deleted or commented out function Expression (Evaluate_It : Boolean) return Boolean; -- Evaluate a condition in an #if or an #elsif statement. -- If Evaluate_It is False, the condition is effectively evaluated, -- otherwise, only the syntax is checked. procedure Go_To_End_Of_Line; -- Advance the scan pointer until we reach an end of line or the end -- of the buffer. function Matching_Strings (S1, S2 : String_Id) return Boolean; -- Returns True if the two string parameters are equal (case insensitive) --------------------------------------- -- Change_Reserved_Keyword_To_Symbol -- --------------------------------------- procedure Change_Reserved_Keyword_To_Symbol (All_Keywords : Boolean := False) is New_Name : constant Name_Id := Token_Names (Token); begin if New_Name /= No_Name then case Token is when Tok_If | Tok_Else | Tok_Elsif | Tok_End | Tok_And | Tok_Or | Tok_Then => if All_Keywords then Token := Tok_Identifier; Token_Name := New_Name; end if; when others => Token := Tok_Identifier; Token_Name := New_Name; end case; end if; end Change_Reserved_Keyword_To_Symbol; ------------------------------------------ -- Check_Command_Line_Symbol_Definition -- ------------------------------------------ procedure Check_Command_Line_Symbol_Definition (Definition : String; Data : out Symbol_Data) is Index : Natural := 0; Result : Symbol_Data; begin -- Look for the character '=' for J in Definition'Range loop if Definition (J) = '=' then Index := J; exit; end if; end loop; -- If no character '=', then the value is True if Index = 0 then -- Put the symbol in the name buffer Name_Len := Definition'Length; Name_Buffer (1 .. Name_Len) := Definition; Result := True_Value; elsif Index = Definition'First then Fail ("invalid symbol definition """, Definition, """"); else -- Put the symbol in the name buffer Name_Len := Index - Definition'First; Name_Buffer (1 .. Name_Len) := String'(Definition (Definition'First .. Index - 1)); -- Check the syntax of the value if Definition (Index + 1) /= '"' or else Definition (Definition'Last) /= '"' then for J in Index + 1 .. Definition'Last loop case Definition (J) is when '_' | '.' | '0' .. '9' | 'a' .. 'z' | 'A' .. 'Z' => null; when others => Fail ("illegal value """, Definition (Index + 1 .. Definition'Last), """"); end case; end loop; end if; -- And put the value in the result Result.Is_A_String := False; Start_String; Store_String_Chars (Definition (Index + 1 .. Definition'Last)); Result.Value := End_String; end if; -- Now, check the syntax of the symbol (we don't allow accented and -- wide characters) if Name_Buffer (1) not in 'a' .. 'z' and then Name_Buffer (1) not in 'A' .. 'Z' then Fail ("symbol """, Name_Buffer (1 .. Name_Len), """ does not start with a letter"); end if; for J in 2 .. Name_Len loop case Name_Buffer (J) is when 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' => null; when '_' => if J = Name_Len then Fail ("symbol """, Name_Buffer (1 .. Name_Len), """ end with a '_'"); elsif Name_Buffer (J + 1) = '_' then Fail ("symbol """, Name_Buffer (1 .. Name_Len), """ contains consecutive '_'"); end if; when others => Fail ("symbol """, Name_Buffer (1 .. Name_Len), """ contains illegal character(s)"); end case; end loop; Result.On_The_Command_Line := True; -- Put the symbol name in the result declare Sym : constant String := Name_Buffer (1 .. Name_Len); begin for Index in 1 .. Name_Len loop Name_Buffer (Index) := Fold_Lower (Name_Buffer (Index)); end loop; Result.Symbol := Name_Find; Name_Len := Sym'Length; Name_Buffer (1 .. Name_Len) := Sym; Result.Original := Name_Find; end; Data := Result; end Check_Command_Line_Symbol_Definition; -------------- -- Deleting -- -------------- function Deleting return Boolean is begin -- Always return False when not inside an #if statement if Pp_States.Last = Ground then return False; else return Pp_States.Table (Pp_States.Last).Deleting; end if; end Deleting; ---------------- -- Expression -- ---------------- function Expression (Evaluate_It : Boolean) return Boolean is Evaluation : Boolean := Evaluate_It; -- Is set to False after an "or else" when left term is True and -- after an "and then" when left term is False. Final_Result : Boolean := False; Current_Result : Boolean := False; -- Value of a term Current_Operator : Operator := None; Symbol1 : Symbol_Id; Symbol2 : Symbol_Id; Symbol_Name1 : Name_Id; Symbol_Name2 : Name_Id; Symbol_Pos1 : Source_Ptr; Symbol_Pos2 : Source_Ptr; Symbol_Value1 : String_Id; Symbol_Value2 : String_Id; begin -- Loop for each term loop Change_Reserved_Keyword_To_Symbol; Current_Result := False; case Token is when Tok_Left_Paren => -- ( expression ) Scan.all; Current_Result := Expression (Evaluation); if Token = Tok_Right_Paren then Scan.all; else Error_Msg ("`)` expected", Token_Ptr); end if; when Tok_Not => -- not expression Scan.all; Current_Result := not Expression (Evaluation); when Tok_Identifier => Symbol_Name1 := Token_Name; Symbol_Pos1 := Token_Ptr; Scan.all; if Token = Tok_Apostrophe then -- symbol'Defined Scan.all; if Token = Tok_Identifier and then Token_Name = Name_Defined then Scan.all; else Error_Msg ("identifier `Defined` expected", Token_Ptr); end if; if Evaluation then Current_Result := Index_Of (Symbol_Name1) /= No_Symbol; end if; elsif Token = Tok_Equal then Scan.all; Change_Reserved_Keyword_To_Symbol; if Token = Tok_Identifier then -- symbol = symbol Symbol_Name2 := Token_Name; Symbol_Pos2 := Token_Ptr; Scan.all; if Evaluation then Symbol1 := Index_Of (Symbol_Name1); if Symbol1 = No_Symbol then if Undefined_Symbols_Are_False then Symbol_Value1 := String_False; else Error_Msg_Name_1 := Symbol_Name1; Error_Msg ("unknown symbol %", Symbol_Pos1); Symbol_Value1 := No_String; end if; else Symbol_Value1 := Mapping.Table (Symbol1).Value; end if; Symbol2 := Index_Of (Symbol_Name2); if Symbol2 = No_Symbol then if Undefined_Symbols_Are_False then Symbol_Value2 := String_False; else Error_Msg_Name_1 := Symbol_Name2; Error_Msg ("unknown symbol %", Symbol_Pos2); Symbol_Value2 := No_String; end if; else Symbol_Value2 := Mapping.Table (Symbol2).Value; end if; if Symbol_Value1 /= No_String and then Symbol_Value2 /= No_String then Current_Result := Matching_Strings (Symbol_Value1, Symbol_Value2); end if; end if; elsif Token = Tok_String_Literal then -- symbol = "value" if Evaluation then Symbol1 := Index_Of (Symbol_Name1); if Symbol1 = No_Symbol then if Undefined_Symbols_Are_False then Symbol_Value1 := String_False; else Error_Msg_Name_1 := Symbol_Name1; Error_Msg ("unknown symbol %", Symbol_Pos1); Symbol_Value1 := No_String; end if; else Symbol_Value1 := Mapping.Table (Symbol1).Value; end if; if Symbol_Value1 /= No_String then Current_Result := Matching_Strings (Symbol_Value1, String_Literal_Id); end if; end if; Scan.all; else Error_Msg ("symbol or literal string expected", Token_Ptr); end if; else -- symbol (True or False) if Evaluation then Symbol1 := Index_Of (Symbol_Name1); if Symbol1 = No_Symbol then if Undefined_Symbols_Are_False then Symbol_Value1 := String_False; else Error_Msg_Name_1 := Symbol_Name1; Error_Msg ("unknown symbol %", Symbol_Pos1); Symbol_Value1 := No_String; end if; else Symbol_Value1 := Mapping.Table (Symbol1).Value; end if; if Symbol_Value1 /= No_String then String_To_Name_Buffer (Symbol_Value1); for Index in 1 .. Name_Len loop Name_Buffer (Index) := Fold_Lower (Name_Buffer (Index)); end loop; if Name_Buffer (1 .. Name_Len) = "true" then Current_Result := True; elsif Name_Buffer (1 .. Name_Len) = "false" then Current_Result := False; else Error_Msg_Name_1 := Symbol_Name1; Error_Msg ("value of symbol % is not True or False", Symbol_Pos1); end if; end if; end if; end if; when others => Error_Msg ("`(`, NOT or symbol expected", Token_Ptr); end case; -- Update the cumulative final result case Current_Operator is when None => Final_Result := Current_Result; when Op_Or => Final_Result := Final_Result or Current_Result; when Op_And => Final_Result := Final_Result and Current_Result; end case; -- Check the next operator if Token = Tok_And then if Current_Operator = Op_Or then Error_Msg ("mixing OR and AND is not allowed", Token_Ptr); end if; Current_Operator := Op_And; Scan.all; if Token = Tok_Then then Scan.all; if Final_Result = False then Evaluation := False; end if; end if; elsif Token = Tok_Or then if Current_Operator = Op_And then Error_Msg ("mixing AND and OR is not allowed", Token_Ptr); end if; Current_Operator := Op_Or; Scan.all; if Token = Tok_Else then Scan.all; if Final_Result then Evaluation := False; end if; end if; else -- No operator: exit the term loop exit; end if; end loop; return Final_Result; end Expression; ----------------------- -- Go_To_End_Of_Line -- ----------------------- procedure Go_To_End_Of_Line is begin -- Scan until we get an end of line or we reach the end of the buffer while Token /= Tok_End_Of_Line and then Token /= Tok_EOF loop Scan.all; end loop; end Go_To_End_Of_Line; -------------- -- Index_Of -- -------------- function Index_Of (Symbol : Name_Id) return Symbol_Id is begin if Mapping.Table /= null then for J in Symbol_Id range 1 .. Symbol_Table.Last (Mapping) loop if Mapping.Table (J).Symbol = Symbol then return J; end if; end loop; end if; return No_Symbol; end Index_Of; ---------------- -- Preprocess -- ---------------- procedure Preprocess is Start_Of_Processing : Source_Ptr; Cond : Boolean; Preprocessor_Line : Boolean := False; procedure Output (From, To : Source_Ptr); -- Output the characters with indices From .. To in the buffer -- to the output file. procedure Output_Line (From, To : Source_Ptr); -- Output a line or the end of a line from the buffer to the output -- file, followed by an end of line terminator. -- Depending on the value of Deleting and the switches, the line -- may be commented out, blank or not output at all. ------------ -- Output -- ------------ procedure Output (From, To : Source_Ptr) is begin for J in From .. To loop Put_Char (Sinput.Source (J)); end loop; end Output; ----------------- -- Output_Line -- ----------------- procedure Output_Line (From, To : Source_Ptr) is begin if Deleting or Preprocessor_Line then if Blank_Deleted_Lines then New_EOL.all; elsif Comment_Deleted_Lines then Put_Char ('-'); Put_Char ('-'); Put_Char ('!'); if From < To then Put_Char (' '); Output (From, To); end if; New_EOL.all; end if; else Output (From, To); New_EOL.all; end if; end Output_Line; -- Start of processing for Preprocess begin Start_Of_Processing := Scan_Ptr; -- We need to call Scan for the first time, because Initialyze_Scanner -- is no longer doing it. Scan.all; Input_Line_Loop : loop exit Input_Line_Loop when Token = Tok_EOF; Preprocessor_Line := False; if Token /= Tok_End_Of_Line then -- Preprocessor line if Token = Tok_Special and then Special_Character = '#' then Preprocessor_Line := True; Scan.all; case Token is when Tok_If => -- #if declare If_Ptr : constant Source_Ptr := Token_Ptr; begin Scan.all; Cond := Expression (not Deleting); -- Check for an eventual "then" if Token = Tok_Then then Scan.all; end if; -- It is an error to have trailing characters after -- the condition or "then". if Token /= Tok_End_Of_Line and then Token /= Tok_EOF then Error_Msg ("extraneous text on preprocessor line", Token_Ptr); Go_To_End_Of_Line; end if; declare -- Set the initial state of this new "#if". -- This must be done before incrementing the -- Last of the table, otherwise function -- Deleting does not report the correct value. New_State : constant Pp_State := (If_Ptr => If_Ptr, Else_Ptr => 0, Deleting => Deleting or (not Cond), Match_Seen => Deleting or Cond); begin Pp_States.Increment_Last; Pp_States.Table (Pp_States.Last) := New_State; end; end; when Tok_Elsif => -- #elsif Cond := False; if Pp_States.Last = 0 or else Pp_States.Table (Pp_States.Last).Else_Ptr /= 0 then Error_Msg ("no IF for this ELSIF", Token_Ptr); else Cond := not Pp_States.Table (Pp_States.Last).Match_Seen; end if; Scan.all; Cond := Expression (Cond); -- Check for an eventual "then" if Token = Tok_Then then Scan.all; end if; -- It is an error to have trailing characters after -- the condition or "then". if Token /= Tok_End_Of_Line and then Token /= Tok_EOF then Error_Msg ("extraneous text on preprocessor line", Token_Ptr); Go_To_End_Of_Line; end if; -- Depending on the value of the condition, set the -- new values of Deleting and Match_Seen. if Pp_States.Last > 0 then if Pp_States.Table (Pp_States.Last).Match_Seen then Pp_States.Table (Pp_States.Last).Deleting := True; else if Cond then Pp_States.Table (Pp_States.Last).Match_Seen := True; Pp_States.Table (Pp_States.Last).Deleting := False; end if; end if; end if; when Tok_Else => -- #else if Pp_States.Last = 0 then Error_Msg ("no IF for this ELSE", Token_Ptr); elsif Pp_States.Table (Pp_States.Last).Else_Ptr /= 0 then Error_Msg ("duplicate ELSE line", Token_Ptr); end if; -- Set the possibly new values of Deleting and -- Match_Seen. if Pp_States.Last > 0 then if Pp_States.Table (Pp_States.Last).Match_Seen then Pp_States.Table (Pp_States.Last).Deleting := True; else Pp_States.Table (Pp_States.Last).Match_Seen := True; Pp_States.Table (Pp_States.Last).Deleting := False; end if; -- Set the Else_Ptr to check for illegal #elsif -- later. Pp_States.Table (Pp_States.Last).Else_Ptr := Token_Ptr; end if; Scan.all; -- It is an error to have characters after "#else" if Token /= Tok_End_Of_Line and then Token /= Tok_EOF then Error_Msg ("extraneous text on preprocessor line", Token_Ptr); Go_To_End_Of_Line; end if; when Tok_End => -- #end if; if Pp_States.Last = 0 then Error_Msg ("no IF for this END", Token_Ptr); end if; Scan.all; if Token /= Tok_If then Error_Msg ("IF expected", Token_Ptr); else Scan.all; if Token /= Tok_Semicolon then Error_Msg ("`;` Expected", Token_Ptr); else Scan.all; -- It is an error to have character after -- "#end if;". if Token /= Tok_End_Of_Line and then Token /= Tok_EOF then Error_Msg ("extraneous text on preprocessor line", Token_Ptr); end if; end if; end if; -- In case of one of the errors above, skip the tokens -- until the end of line is reached. Go_To_End_Of_Line; -- Decrement the depth of the #if stack. if Pp_States.Last > 0 then Pp_States.Decrement_Last; end if; when others => -- Illegal preprocessor line if Pp_States.Last = 0 then Error_Msg ("IF expected", Token_Ptr); elsif Pp_States.Table (Pp_States.Last).Else_Ptr = 0 then Error_Msg ("IF, ELSIF, ELSE, or `END IF` expected", Token_Ptr); else Error_Msg ("IF or `END IF` expected", Token_Ptr); end if; -- Skip to the end of this illegal line Go_To_End_Of_Line; end case; -- Not a preprocessor line else -- Do not report errors for those lines, even if there are -- Ada parsing errors. Set_Ignore_Errors (To => True); if Deleting then Go_To_End_Of_Line; else while Token /= Tok_End_Of_Line and then Token /= Tok_EOF loop if Token = Tok_Special and then Special_Character = '$' then declare Dollar_Ptr : constant Source_Ptr := Token_Ptr; Symbol : Symbol_Id; begin Scan.all; Change_Reserved_Keyword_To_Symbol; if Token = Tok_Identifier and then Token_Ptr = Dollar_Ptr + 1 then -- $symbol Symbol := Index_Of (Token_Name); -- If there is such a symbol, replace it by its -- value. if Symbol /= No_Symbol then Output (Start_Of_Processing, Dollar_Ptr - 1); Start_Of_Processing := Scan_Ptr; String_To_Name_Buffer (Mapping.Table (Symbol).Value); if Mapping.Table (Symbol).Is_A_String then -- Value is an Ada string Put_Char ('"'); for J in 1 .. Name_Len loop Put_Char (Name_Buffer (J)); if Name_Buffer (J) = '"' then Put_Char ('"'); end if; end loop; Put_Char ('"'); else -- Value is a sequence of characters, not -- an Ada string. for J in 1 .. Name_Len loop Put_Char (Name_Buffer (J)); end loop; end if; end if; end if; end; end if; Scan.all; end loop; end if; Set_Ignore_Errors (To => False); end if; end if; pragma Assert (Token = Tok_End_Of_Line or Token = Tok_EOF); -- At this point, the token is either end of line or EOF. -- The line to possibly output stops just before the token. Output_Line (Start_Of_Processing, Token_Ptr - 1); -- If we are at the end of a line, the scan pointer is at the first -- non blank character, not necessarily the first character of the -- line; so, we have to deduct Start_Of_Processing from the token -- pointer. if Token = Tok_End_Of_Line then if (Sinput.Source (Token_Ptr) = ASCII.CR and then Sinput.Source (Token_Ptr + 1) = ASCII.LF) or else (Sinput.Source (Token_Ptr) = ASCII.CR and then Sinput.Source (Token_Ptr + 1) = ASCII.LF) then Start_Of_Processing := Token_Ptr + 2; else Start_Of_Processing := Token_Ptr + 1; end if; end if; -- Now, we scan the first token of the next line. -- If the token is EOF, the scan ponter will not move, and the token -- will still be EOF. Scan.all; end loop Input_Line_Loop; -- Report an error for any missing some "#end if;" for Level in reverse 1 .. Pp_States.Last loop Error_Msg ("no `END IF` for this IF", Pp_States.Table (Level).If_Ptr); end loop; end Preprocess; ---------------- -- Initialize -- ---------------- procedure Initialize (Error_Msg : Error_Msg_Proc; Scan : Scan_Proc; Set_Ignore_Errors : Set_Ignore_Errors_Proc; Put_Char : Put_Char_Proc; New_EOL : New_EOL_Proc) is begin if not Already_Initialized then Start_String; Store_String_Chars ("True"); True_Value.Value := End_String; Start_String; Empty_String := End_String; Name_Len := 7; Name_Buffer (1 .. Name_Len) := "defined"; Name_Defined := Name_Find; Start_String; Store_String_Chars ("False"); String_False := End_String; Already_Initialized := True; end if; Prep.Error_Msg := Error_Msg; Prep.Scan := Scan; Prep.Set_Ignore_Errors := Set_Ignore_Errors; Prep.Put_Char := Put_Char; Prep.New_EOL := New_EOL; end Initialize; ------------------ -- List_Symbols -- ------------------ procedure List_Symbols (Foreword : String) is Order : array (0 .. Integer (Symbol_Table.Last (Mapping))) of Symbol_Id; -- After alphabetical sorting, this array stores thehe indices of -- the symbols in the order they are displayed. function Lt (Op1, Op2 : Natural) return Boolean; -- Comparison routine for sort call procedure Move (From : Natural; To : Natural); -- Move routine for sort call -------- -- Lt -- -------- function Lt (Op1, Op2 : Natural) return Boolean is S1 : constant String := Get_Name_String (Mapping.Table (Order (Op1)).Symbol); S2 : constant String := Get_Name_String (Mapping.Table (Order (Op2)).Symbol); begin return S1 < S2; end Lt; ---------- -- Move -- ---------- procedure Move (From : Natural; To : Natural) is begin Order (To) := Order (From); end Move; package Sort_Syms is new GNAT.Heap_Sort_G (Move, Lt); Max_L : Natural; -- Maximum length of any symbol -- Start of processing for List_Symbols_Case begin if Symbol_Table.Last (Mapping) = 0 then return; end if; if Foreword'Length > 0 then Write_Eol; Write_Line (Foreword); for J in Foreword'Range loop Write_Char ('='); end loop; end if; -- Initialize the order for J in Order'Range loop Order (J) := Symbol_Id (J); end loop; -- Sort alphabetically Sort_Syms.Sort (Order'Last); Max_L := 7; for J in 1 .. Symbol_Table.Last (Mapping) loop Get_Name_String (Mapping.Table (J).Original); Max_L := Integer'Max (Max_L, Name_Len); end loop; Write_Eol; Write_Str ("Symbol"); for J in 1 .. Max_L - 5 loop Write_Char (' '); end loop; Write_Line ("Value"); Write_Str ("------"); for J in 1 .. Max_L - 5 loop Write_Char (' '); end loop; Write_Line ("------"); for J in 1 .. Order'Last loop declare Data : constant Symbol_Data := Mapping.Table (Order (J)); begin Get_Name_String (Data.Original); Write_Str (Name_Buffer (1 .. Name_Len)); for K in Name_Len .. Max_L loop Write_Char (' '); end loop; String_To_Name_Buffer (Data.Value); if Data.Is_A_String then Write_Char ('"'); for J in 1 .. Name_Len loop Write_Char (Name_Buffer (J)); if Name_Buffer (J) = '"' then Write_Char ('"'); end if; end loop; Write_Char ('"'); else Write_Str (Name_Buffer (1 .. Name_Len)); end if; end; Write_Eol; end loop; Write_Eol; end List_Symbols; ---------------------- -- Matching_Strings -- ---------------------- function Matching_Strings (S1, S2 : String_Id) return Boolean is begin String_To_Name_Buffer (S1); for Index in 1 .. Name_Len loop Name_Buffer (Index) := Fold_Lower (Name_Buffer (Index)); end loop; declare String1 : constant String := Name_Buffer (1 .. Name_Len); begin String_To_Name_Buffer (S2); for Index in 1 .. Name_Len loop Name_Buffer (Index) := Fold_Lower (Name_Buffer (Index)); end loop; return String1 = Name_Buffer (1 .. Name_Len); end; end Matching_Strings; -------------------- -- Parse_Def_File -- -------------------- procedure Parse_Def_File is Symbol : Symbol_Id; Symbol_Name : Name_Id; Original_Name : Name_Id; Data : Symbol_Data; Value_Start : Source_Ptr; Value_End : Source_Ptr; Ch : Character; use ASCII; begin Def_Line_Loop : loop Scan.all; exit Def_Line_Loop when Token = Tok_EOF; if Token /= Tok_End_Of_Line then Change_Reserved_Keyword_To_Symbol; if Token /= Tok_Identifier then Error_Msg ("identifier expected", Token_Ptr); goto Cleanup; end if; Symbol_Name := Token_Name; Name_Len := 0; for Ptr in Token_Ptr .. Scan_Ptr - 1 loop Name_Len := Name_Len + 1; Name_Buffer (Name_Len) := Sinput.Source (Ptr); end loop; Original_Name := Name_Find; Scan.all; if Token /= Tok_Colon_Equal then Error_Msg ("`:=` expected", Token_Ptr); goto Cleanup; end if; Scan.all; if Token = Tok_String_Literal then Data := (Symbol => Symbol_Name, Original => Original_Name, On_The_Command_Line => False, Is_A_String => True, Value => String_Literal_Id); Scan.all; if Token /= Tok_End_Of_Line and then Token /= Tok_EOF then Error_Msg ("extraneous text in definition", Token_Ptr); goto Cleanup; end if; elsif Token = Tok_End_Of_Line or Token = Tok_EOF then Data := (Symbol => Symbol_Name, Original => Original_Name, On_The_Command_Line => False, Is_A_String => False, Value => Empty_String); else Value_Start := Token_Ptr; Value_End := Token_Ptr - 1; Scan_Ptr := Token_Ptr; Value_Chars_Loop : loop Ch := Sinput.Source (Scan_Ptr); case Ch is when '_' | '.' | '0' .. '9' | 'a' .. 'z' | 'A' .. 'Z' => Value_End := Scan_Ptr; Scan_Ptr := Scan_Ptr + 1; when ' ' | HT | VT | CR | LF | FF => exit Value_Chars_Loop; when others => Error_Msg ("illegal character", Scan_Ptr); goto Cleanup; end case; end loop Value_Chars_Loop; Scan.all; if Token /= Tok_End_Of_Line and then Token /= Tok_EOF then Error_Msg ("extraneous text in definition", Token_Ptr); goto Cleanup; end if; Start_String; while Value_Start <= Value_End loop Store_String_Char (Sinput.Source (Value_Start)); Value_Start := Value_Start + 1; end loop; Data := (Symbol => Symbol_Name, Original => Original_Name, On_The_Command_Line => False, Is_A_String => False, Value => End_String); end if; -- Now that we have the value, get the symbol index Symbol := Index_Of (Symbol_Name); if Symbol /= No_Symbol then -- If we already have an entry for this symbol, replace it -- with the new value, except if the symbol was declared -- on the command line. if Mapping.Table (Symbol).On_The_Command_Line then goto Continue; end if; else -- As it is the first time we see this symbol, create a new -- entry in the table. if Mapping.Table = null then Symbol_Table.Init (Mapping); end if; Symbol_Table.Increment_Last (Mapping); Symbol := Symbol_Table.Last (Mapping); end if; Mapping.Table (Symbol) := Data; goto Continue; <> Set_Ignore_Errors (To => True); while Token /= Tok_End_Of_Line and Token /= Tok_EOF loop Scan.all; end loop; Set_Ignore_Errors (To => False); <> null; end if; end loop Def_Line_Loop; end Parse_Def_File; end Prep;