| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- G N A T P R E P -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- $Revision: 1.27 $ |
| -- -- |
| -- Copyright (C) 1996-2001, 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. -- |
| -- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with Ada.Characters.Handling; use Ada.Characters.Handling; |
| with Ada.Strings.Fixed; |
| with Ada.Command_Line; use Ada.Command_Line; |
| with Ada.Text_IO; use Ada.Text_IO; |
| |
| with GNAT.Heap_Sort_G; |
| with GNAT.Command_Line; |
| |
| with Gnatvsn; |
| |
| procedure GNATprep is |
| pragma Ident (Gnatvsn.Gnat_Version_String); |
| |
| Version_String : constant String := "$Revision: 1.27 $"; |
| |
| type Strptr is access String; |
| |
| Usage_Error : exception; |
| -- Raised if a usage error is detected, causes termination of processing |
| -- with an appropriate error message and error exit status set. |
| |
| Fatal_Error : exception; |
| -- Exception raised if fatal error detected |
| |
| Expression_Error : exception; |
| -- Exception raised when an invalid boolean expression is found |
| -- on a preprocessor line |
| |
| ------------------------ |
| -- Argument Line Data -- |
| ------------------------ |
| |
| Infile_Name : Strptr; |
| Outfile_Name : Strptr; |
| Deffile_Name : Strptr; |
| -- Names of files |
| |
| Infile : File_Type; |
| Outfile : File_Type; |
| Deffile : File_Type; |
| |
| Opt_Comment_Deleted_Lines : Boolean := False; -- Set if -c switch set |
| Blank_Deleted_Lines : Boolean := False; -- Set if -b switch set |
| List_Symbols : Boolean := False; -- Set if -s switch set |
| Source_Ref_Pragma : Boolean := False; -- Set if -r switch set |
| Undefined_Is_False : Boolean := False; -- Set if -u switch set |
| -- Record command line options |
| |
| --------------------------- |
| -- Definitions File Data -- |
| --------------------------- |
| |
| Num_Syms : Natural := 0; |
| -- Number of symbols defined in definitions file |
| |
| Symbols : array (0 .. 10_000) of Strptr; |
| Values : array (0 .. 10_000) of Strptr; |
| -- Symbol names and values. Note that the zero'th element is used only |
| -- during the call to Sort (to hold a temporary value, as required by |
| -- the GNAT.Heap_Sort_G interface). |
| |
| --------------------- |
| -- Input File Data -- |
| --------------------- |
| |
| Current_File_Name : Strptr; |
| -- Holds name of file being read (definitions file or input file) |
| |
| Line_Buffer : String (1 .. 20_000); |
| -- Hold one line |
| |
| Line_Length : Natural; |
| -- Length of line in Line_Buffer |
| |
| Line_Num : Natural; |
| -- Current input file line number |
| |
| Ptr : Natural; |
| -- Input scan pointer for line in Line_Buffer |
| |
| type Keyword is (K_Not, K_Then, K_If, K_Else, K_End, K_Elsif, |
| K_And, K_Or, K_Open_Paren, K_Close_Paren, |
| K_Defined, K_Andthen, K_Orelse, K_Equal, K_None); |
| -- Keywords that are recognized on preprocessor lines. K_None indicates |
| -- that no keyword was present. |
| |
| K : Keyword; |
| -- Scanned keyword |
| |
| Start_Sym, End_Sym : Natural; |
| -- First and last positions of scanned symbol |
| |
| Num_Errors : Natural := 0; |
| -- Number of errors detected |
| |
| ----------------------- |
| -- Preprocessor Data -- |
| ----------------------- |
| |
| -- The following record represents the state of an #if structure: |
| |
| type PP_Rec is record |
| If_Line : Positive; |
| -- Line number for #if line |
| |
| Else_Line : Natural; |
| -- Line number for #else line, zero = no else seen yet |
| |
| Deleting : Boolean; |
| -- True if lines currently being deleted |
| |
| Match_Seen : Boolean; |
| -- True if either the #if condition or one of the previously seen |
| -- #elsif lines was true, meaning that any future #elsif sections |
| -- or the #else section, is to be deleted. |
| end record; |
| |
| PP_Depth : Natural; |
| -- Preprocessor #if nesting level. A value of zero means that we are |
| -- outside any #if structure. |
| |
| PP : array (0 .. 100) of PP_Rec; |
| -- Stack of records showing state of #if structures. PP (1) is the |
| -- outer level entry, and PP (PP_Depth) is the active entry. PP (0) |
| -- contains a dummy entry whose Deleting flag is always set to False. |
| |
| ----------------- |
| -- Subprograms -- |
| ----------------- |
| |
| function At_End_Of_Line return Boolean; |
| -- First advances Ptr using Skip_Spaces. Then returns True if Ptr is |
| -- either at the end of the line, or at a -- comment sequence. |
| |
| procedure Error (Msg : String); |
| -- Post error message with given text. The line number is taken from |
| -- Line_Num, and the column number from Ptr. |
| |
| function Eval_Condition |
| (Parenthesis : Natural := 0; |
| Do_Eval : Boolean := True) |
| return Boolean; |
| -- Eval the condition found in the current Line. The condition can |
| -- include any of the 'and', 'or', 'not', and parenthesis subexpressions. |
| -- If Line is an invalid expression, then Expression_Error is raised, |
| -- after an error message has been printed. Line can include 'then' |
| -- followed by a comment, which is automatically ignored. If Do_Eval |
| -- is False, then the expression is not evaluated at all, and symbols |
| -- are just skipped. |
| |
| function Eval_Symbol (Do_Eval : Boolean) return Boolean; |
| -- Read and evaluate the next symbol or expression (A, A'Defined, A=...) |
| -- If it is followed by 'Defined or an equality test, read as many symbols |
| -- as needed. Do_Eval has the same meaning as in Eval_Condition |
| |
| procedure Help_Page; |
| -- Print a help page to summarize the usage of gnatprep |
| |
| function Is_Preprocessor_Line return Boolean; |
| -- Tests if current line is a preprocessor line, i.e. that its first |
| -- non-blank character is a # character. If so, then a result of True |
| -- is returned, and Ptr is set to point to the character following the |
| -- # character. If not, False is returned and Ptr is undefined. |
| |
| procedure No_Junk; |
| -- Make sure no junk is present on a preprocessor line. Ptr points past |
| -- the scanned preprocessor syntax. |
| |
| function OK_Identifier (S : String) return Boolean; |
| -- Tests if given referenced string is valid Ada identifier |
| |
| function Matching_Strings (S1, S2 : String) return Boolean; |
| -- Check if S1 and S2 are the same string (this is a case independent |
| -- comparison, lower and upper case letters are considered to match). |
| -- Duplicate quotes in S2 are considered as a single quote ("" => ") |
| |
| procedure Parse_Def_File; |
| -- Parse the deffile given by the user |
| |
| function Scan_Keyword return Keyword; |
| -- Advances Ptr to end of line or next non-blank using Skip_Spaces. Then |
| -- attempts to scan out a recognized keyword. if a recognized keyword is |
| -- found, sets Ptr past it, and returns the code for the keyword, if not, |
| -- then Ptr is left unchanged pointing to a non-blank character or to the |
| -- end of the line. |
| |
| function Symbol_Scanned return Boolean; |
| -- On entry, Start_Sym is set to the first character of an identifier |
| -- symbol to be scanned out. On return, End_Sym is set to the last |
| -- character of the identifier, and the result indicates if the scanned |
| -- symbol is a valid identifier (True = valid). Ptr is not changed. |
| |
| procedure Skip_Spaces; |
| -- Skips Ptr past tabs and spaces to next non-blank, or one character |
| -- past the end of line. |
| |
| function Variable_Index (Name : String) return Natural; |
| -- Returns the index of the variable in the table. If the variable is not |
| -- found, returns Natural'Last |
| |
| -------------------- |
| -- At_End_Of_Line -- |
| -------------------- |
| |
| function At_End_Of_Line return Boolean is |
| begin |
| Skip_Spaces; |
| |
| return Ptr > Line_Length |
| or else |
| (Ptr < Line_Length and then Line_Buffer (Ptr .. Ptr + 1) = "--"); |
| end At_End_Of_Line; |
| |
| ----------- |
| -- Error -- |
| ----------- |
| |
| procedure Error (Msg : String) is |
| L : constant String := Natural'Image (Line_Num); |
| C : constant String := Natural'Image (Ptr); |
| |
| begin |
| Put (Standard_Error, Current_File_Name.all); |
| Put (Standard_Error, ':'); |
| Put (Standard_Error, L (2 .. L'Length)); |
| Put (Standard_Error, ':'); |
| Put (Standard_Error, C (2 .. C'Length)); |
| Put (Standard_Error, ": "); |
| |
| Put_Line (Standard_Error, Msg); |
| Num_Errors := Num_Errors + 1; |
| end Error; |
| |
| -------------------- |
| -- Eval_Condition -- |
| -------------------- |
| |
| function Eval_Condition |
| (Parenthesis : Natural := 0; |
| Do_Eval : Boolean := True) |
| return Boolean |
| is |
| Symbol_Is_True : Boolean := False; -- init to avoid warning |
| K : Keyword; |
| |
| begin |
| -- Find the next subexpression |
| |
| K := Scan_Keyword; |
| |
| case K is |
| when K_None => |
| Symbol_Is_True := Eval_Symbol (Do_Eval); |
| |
| when K_Not => |
| |
| -- Not applies to the next subexpression (either a simple |
| -- evaluation like A or A'Defined, or a parenthesis expression) |
| |
| K := Scan_Keyword; |
| |
| if K = K_Open_Paren then |
| Symbol_Is_True := not Eval_Condition (Parenthesis + 1, Do_Eval); |
| |
| elsif K = K_None then |
| Symbol_Is_True := not Eval_Symbol (Do_Eval); |
| |
| else |
| Ptr := Start_Sym; -- Puts the keyword back |
| end if; |
| |
| when K_Open_Paren => |
| Symbol_Is_True := Eval_Condition (Parenthesis + 1, Do_Eval); |
| |
| when others => |
| Ptr := Start_Sym; |
| Error ("invalid syntax in preprocessor line"); |
| raise Expression_Error; |
| end case; |
| |
| -- Do we have a compound expression with AND, OR, ... |
| |
| K := Scan_Keyword; |
| case K is |
| when K_None => |
| if not At_End_Of_Line then |
| Error ("Invalid Syntax at end of line"); |
| raise Expression_Error; |
| end if; |
| |
| if Parenthesis /= 0 then |
| Error ("Unmatched opening parenthesis"); |
| raise Expression_Error; |
| end if; |
| |
| return Symbol_Is_True; |
| |
| when K_Then => |
| if Parenthesis /= 0 then |
| Error ("Unmatched opening parenthesis"); |
| raise Expression_Error; |
| end if; |
| |
| return Symbol_Is_True; |
| |
| when K_Close_Paren => |
| if Parenthesis = 0 then |
| Error ("Unmatched closing parenthesis"); |
| raise Expression_Error; |
| end if; |
| |
| return Symbol_Is_True; |
| |
| when K_And => |
| return Symbol_Is_True and Eval_Condition (Parenthesis, Do_Eval); |
| |
| when K_Andthen => |
| if not Symbol_Is_True then |
| |
| -- Just skip the symbols for the remaining part |
| |
| Symbol_Is_True := Eval_Condition (Parenthesis, False); |
| return False; |
| |
| else |
| return Eval_Condition (Parenthesis, Do_Eval); |
| end if; |
| |
| when K_Or => |
| return Symbol_Is_True or Eval_Condition (Parenthesis, Do_Eval); |
| |
| when K_Orelse => |
| if Symbol_Is_True then |
| |
| -- Just skip the symbols for the remaining part |
| |
| Symbol_Is_True := Eval_Condition (Parenthesis, False); |
| return True; |
| |
| else |
| return Eval_Condition (Parenthesis, Do_Eval); |
| end if; |
| |
| when others => |
| Error ("invalid syntax in preprocessor line"); |
| raise Expression_Error; |
| end case; |
| |
| end Eval_Condition; |
| |
| ----------------- |
| -- Eval_Symbol -- |
| ----------------- |
| |
| function Eval_Symbol (Do_Eval : Boolean) return Boolean is |
| Sym : constant String := Line_Buffer (Start_Sym .. End_Sym); |
| K : Keyword; |
| Index : Natural; |
| Symbol_Defined : Boolean := False; |
| Symbol_Is_True : Boolean := False; |
| |
| begin |
| -- Read the symbol |
| |
| Skip_Spaces; |
| Start_Sym := Ptr; |
| |
| if not Symbol_Scanned then |
| Error ("invalid symbol name"); |
| raise Expression_Error; |
| end if; |
| |
| Ptr := End_Sym + 1; |
| |
| -- Test if we have a simple test (A) or a more complicated one |
| -- (A'Defined) |
| |
| K := Scan_Keyword; |
| |
| if K /= K_Defined and then K /= K_Equal then |
| Ptr := Start_Sym; -- Puts the keyword back |
| end if; |
| |
| Index := Variable_Index (Sym); |
| |
| case K is |
| when K_Defined => |
| Symbol_Defined := Index /= Natural'Last; |
| Symbol_Is_True := Symbol_Defined; |
| |
| when K_Equal => |
| |
| -- Read the second part of the statement |
| Skip_Spaces; |
| Start_Sym := Ptr; |
| |
| if not Symbol_Scanned |
| and then End_Sym < Start_Sym |
| then |
| Error ("No right part for the equality test"); |
| raise Expression_Error; |
| end if; |
| |
| Ptr := End_Sym + 1; |
| |
| -- If the variable was not found |
| |
| if Do_Eval then |
| if Index = Natural'Last then |
| if not Undefined_Is_False then |
| Error ("symbol name """ & Sym & |
| """ is not defined in definitions file"); |
| end if; |
| |
| else |
| declare |
| Right : constant String |
| := Line_Buffer (Start_Sym .. End_Sym); |
| Index_R : Natural; |
| begin |
| if Right (Right'First) = '"' then |
| Symbol_Is_True := |
| Matching_Strings |
| (Values (Index).all, |
| Right (Right'First + 1 .. Right'Last - 1)); |
| else |
| Index_R := Variable_Index (Right); |
| if Index_R = Natural'Last then |
| Error ("Variable " & Right & " in test is " |
| & "not defined"); |
| raise Expression_Error; |
| else |
| Symbol_Is_True := |
| Matching_Strings (Values (Index).all, |
| Values (Index_R).all); |
| end if; |
| end if; |
| end; |
| end if; |
| end if; |
| |
| when others => |
| |
| if Index = Natural'Last then |
| |
| Symbol_Defined := False; |
| if Do_Eval and then not Symbol_Defined then |
| if Undefined_Is_False then |
| Symbol_Defined := True; |
| Symbol_Is_True := False; |
| |
| else |
| Error |
| ("symbol name """ & Sym & |
| """ is not defined in definitions file"); |
| end if; |
| end if; |
| |
| elsif not Do_Eval then |
| Symbol_Is_True := True; |
| |
| elsif Matching_Strings (Values (Index).all, "True") then |
| Symbol_Is_True := True; |
| |
| elsif Matching_Strings (Values (Index).all, "False") then |
| Symbol_Is_True := False; |
| |
| else |
| Error ("symbol value is not True or False"); |
| Symbol_Is_True := False; |
| end if; |
| |
| end case; |
| |
| return Symbol_Is_True; |
| end Eval_Symbol; |
| |
| --------------- |
| -- Help_Page -- |
| --------------- |
| |
| procedure Help_Page is |
| begin |
| Put_Line (Standard_Error, |
| "GNAT Preprocessor Version " & |
| Version_String (12 .. 15) & |
| " Copyright 1996-2001 Free Software Foundation, Inc."); |
| Put_Line (Standard_Error, |
| "Usage: gnatprep [-bcrsu] [-Dsymbol=value] infile " & |
| "outfile [deffile]"); |
| New_Line (Standard_Error); |
| Put_Line (Standard_Error, " infile Name of the input file"); |
| Put_Line (Standard_Error, " outfile Name of the output file"); |
| Put_Line (Standard_Error, " deffile Name of the definition file"); |
| New_Line (Standard_Error); |
| Put_Line (Standard_Error, "gnatprep switches:"); |
| Put_Line (Standard_Error, " -b Replace preprocessor lines by " & |
| "blank lines"); |
| Put_Line (Standard_Error, " -c Keep preprocessor lines as comments"); |
| Put_Line (Standard_Error, " -D Associate symbol with value"); |
| Put_Line (Standard_Error, " -r Generate Source_Reference pragma"); |
| Put_Line (Standard_Error, " -s Print a sorted list of symbol names " & |
| "and values"); |
| Put_Line (Standard_Error, " -u Treat undefined symbols as FALSE"); |
| New_Line (Standard_Error); |
| end Help_Page; |
| |
| -------------------------- |
| -- Is_Preprocessor_Line -- |
| -------------------------- |
| |
| function Is_Preprocessor_Line return Boolean is |
| begin |
| Ptr := 1; |
| |
| while Ptr <= Line_Length loop |
| if Line_Buffer (Ptr) = '#' then |
| Ptr := Ptr + 1; |
| return True; |
| |
| elsif Line_Buffer (Ptr) > ' ' then |
| return False; |
| |
| else |
| Ptr := Ptr + 1; |
| end if; |
| end loop; |
| |
| return False; |
| end Is_Preprocessor_Line; |
| |
| ---------------------- |
| -- Matching_Strings -- |
| ---------------------- |
| |
| function Matching_Strings (S1, S2 : String) return Boolean is |
| S2_Index : Integer := S2'First; |
| |
| begin |
| for S1_Index in S1'Range loop |
| |
| if To_Upper (S1 (S1_Index)) /= To_Upper (S2 (S2_Index)) then |
| return False; |
| |
| else |
| if S2 (S2_Index) = '"' |
| and then S2_Index < S2'Last |
| and then S2 (S2_Index + 1) = '"' |
| then |
| S2_Index := S2_Index + 2; |
| else |
| S2_Index := S2_Index + 1; |
| end if; |
| |
| -- If S2 was too short then |
| |
| if S2_Index > S2'Last and then S1_Index < S1'Last then |
| return False; |
| end if; |
| end if; |
| end loop; |
| |
| return S2_Index = S2'Last + 1; |
| end Matching_Strings; |
| |
| ------------- |
| -- No_Junk -- |
| ------------- |
| |
| procedure No_Junk is |
| begin |
| Skip_Spaces; |
| |
| if Ptr = Line_Length |
| or else (Ptr < Line_Length |
| and then Line_Buffer (Ptr .. Ptr + 1) /= "--") |
| then |
| Error ("extraneous text on preprocessor line ignored"); |
| end if; |
| end No_Junk; |
| |
| ------------------- |
| -- OK_Identifier -- |
| ------------------- |
| |
| function OK_Identifier (S : String) return Boolean is |
| P : Natural := S'First; |
| |
| begin |
| if S'Length /= 0 and then S (P) = Character'Val (39) then -- ''' |
| P := P + 1; |
| end if; |
| |
| if S'Length = 0 |
| or else not Is_Letter (S (P)) |
| then |
| return False; |
| |
| else |
| while P <= S'Last loop |
| if Is_Letter (S (P)) or Is_Digit (S (P)) then |
| null; |
| |
| elsif S (P) = '_' |
| and then P < S'Last |
| and then S (P + 1) /= '_' |
| then |
| null; |
| |
| else |
| return False; |
| end if; |
| |
| P := P + 1; |
| end loop; |
| |
| return True; |
| end if; |
| end OK_Identifier; |
| |
| -------------------- |
| -- Parse_Def_File -- |
| -------------------- |
| |
| procedure Parse_Def_File is |
| begin |
| Open (Deffile, In_File, Deffile_Name.all); |
| |
| Line_Num := 0; |
| Current_File_Name := Deffile_Name; |
| |
| -- Loop through lines in symbol definitions file |
| |
| while not End_Of_File (Deffile) loop |
| Get_Line (Deffile, Line_Buffer, Line_Length); |
| Line_Num := Line_Num + 1; |
| |
| Ptr := 1; |
| Skip_Spaces; |
| |
| if Ptr > Line_Length |
| or else (Ptr < Line_Length |
| and then |
| Line_Buffer (Ptr .. Ptr + 1) = "--") |
| then |
| goto Continue; |
| end if; |
| |
| Start_Sym := Ptr; |
| |
| if not Symbol_Scanned then |
| Error ("invalid symbol identifier """ & |
| Line_Buffer (Start_Sym .. End_Sym) & |
| '"'); |
| goto Continue; |
| end if; |
| |
| Ptr := End_Sym + 1; |
| Skip_Spaces; |
| |
| if Ptr >= Line_Length |
| or else Line_Buffer (Ptr .. Ptr + 1) /= ":=" |
| then |
| Error ("missing "":="" in symbol definition line"); |
| goto Continue; |
| end if; |
| |
| Ptr := Ptr + 2; |
| Skip_Spaces; |
| |
| Num_Syms := Num_Syms + 1; |
| Symbols (Num_Syms) := new String'(Line_Buffer (Start_Sym .. End_Sym)); |
| |
| Start_Sym := Ptr; |
| End_Sym := Ptr - 1; |
| |
| if At_End_Of_Line then |
| null; |
| |
| elsif Line_Buffer (Start_Sym) = '"' then |
| End_Sym := End_Sym + 1; |
| loop |
| End_Sym := End_Sym + 1; |
| |
| if End_Sym > Line_Length then |
| Error ("no closing quote for string constant"); |
| goto Continue; |
| |
| elsif End_Sym < Line_Length |
| and then Line_Buffer (End_Sym .. End_Sym + 1) = """""" |
| then |
| End_Sym := End_Sym + 1; |
| |
| elsif Line_Buffer (End_Sym) = '"' then |
| exit; |
| end if; |
| end loop; |
| |
| else |
| End_Sym := Ptr - 1; |
| |
| while End_Sym < Line_Length |
| and then (Is_Alphanumeric (Line_Buffer (End_Sym + 1)) |
| or else |
| Line_Buffer (End_Sym + 1) = '_' |
| or else |
| Line_Buffer (End_Sym + 1) = '.') |
| loop |
| End_Sym := End_Sym + 1; |
| end loop; |
| |
| Ptr := End_Sym + 1; |
| |
| if not At_End_Of_Line then |
| Error ("incorrect symbol value syntax"); |
| goto Continue; |
| end if; |
| end if; |
| |
| Values (Num_Syms) := new String'(Line_Buffer (Start_Sym .. End_Sym)); |
| |
| <<Continue>> |
| null; |
| end loop; |
| |
| exception |
| -- Could not open the file |
| |
| when Name_Error => |
| Put_Line (Standard_Error, "cannot open " & Deffile_Name.all); |
| raise Fatal_Error; |
| end Parse_Def_File; |
| |
| ------------------ |
| -- Scan_Keyword -- |
| ------------------ |
| |
| function Scan_Keyword return Keyword is |
| Kptr : constant Natural := Ptr; |
| |
| begin |
| Skip_Spaces; |
| Start_Sym := Ptr; |
| |
| if Symbol_Scanned then |
| |
| -- If the symbol was the last thing on the line, End_Sym will |
| -- point too far in Line_Buffer |
| |
| if End_Sym > Line_Length then |
| End_Sym := Line_Length; |
| end if; |
| |
| Ptr := End_Sym + 1; |
| |
| declare |
| Sym : constant String := Line_Buffer (Start_Sym .. End_Sym); |
| |
| begin |
| if Matching_Strings (Sym, "not") then |
| return K_Not; |
| |
| elsif Matching_Strings (Sym, "then") then |
| return K_Then; |
| |
| elsif Matching_Strings (Sym, "if") then |
| return K_If; |
| |
| elsif Matching_Strings (Sym, "else") then |
| return K_Else; |
| |
| elsif Matching_Strings (Sym, "end") then |
| return K_End; |
| |
| elsif Matching_Strings (Sym, "elsif") then |
| return K_Elsif; |
| |
| elsif Matching_Strings (Sym, "and") then |
| if Scan_Keyword = K_Then then |
| Start_Sym := Kptr; |
| return K_Andthen; |
| else |
| Ptr := Start_Sym; -- Put back the last keyword read |
| Start_Sym := Kptr; |
| return K_And; |
| end if; |
| |
| elsif Matching_Strings (Sym, "or") then |
| if Scan_Keyword = K_Else then |
| Start_Sym := Kptr; |
| return K_Orelse; |
| else |
| Ptr := Start_Sym; -- Put back the last keyword read |
| Start_Sym := Kptr; |
| return K_Or; |
| end if; |
| |
| elsif Matching_Strings (Sym, "'defined") then |
| return K_Defined; |
| |
| elsif Sym = "(" then |
| return K_Open_Paren; |
| |
| elsif Sym = ")" then |
| return K_Close_Paren; |
| |
| elsif Sym = "=" then |
| return K_Equal; |
| end if; |
| end; |
| end if; |
| |
| Ptr := Kptr; |
| return K_None; |
| end Scan_Keyword; |
| |
| ----------------- |
| -- Skip_Spaces -- |
| ----------------- |
| |
| procedure Skip_Spaces is |
| begin |
| while Ptr <= Line_Length loop |
| if Line_Buffer (Ptr) /= ' ' |
| and then Line_Buffer (Ptr) /= ASCII.HT |
| then |
| return; |
| else |
| Ptr := Ptr + 1; |
| end if; |
| end loop; |
| end Skip_Spaces; |
| |
| -------------------- |
| -- Symbol_Scanned -- |
| -------------------- |
| |
| function Symbol_Scanned return Boolean is |
| begin |
| End_Sym := Start_Sym - 1; |
| |
| case Line_Buffer (End_Sym + 1) is |
| |
| when '(' | ')' | '=' => |
| End_Sym := End_Sym + 1; |
| return True; |
| |
| when '"' => |
| End_Sym := End_Sym + 1; |
| while End_Sym < Line_Length loop |
| |
| if Line_Buffer (End_Sym + 1) = '"' then |
| |
| if End_Sym + 2 < Line_Length |
| and then Line_Buffer (End_Sym + 2) = '"' |
| then |
| End_Sym := End_Sym + 2; |
| else |
| exit; |
| end if; |
| else |
| End_Sym := End_Sym + 1; |
| end if; |
| end loop; |
| |
| if End_Sym >= Line_Length then |
| Error ("Invalid string "); |
| raise Expression_Error; |
| end if; |
| |
| End_Sym := End_Sym + 1; |
| return False; |
| |
| when ''' => |
| End_Sym := End_Sym + 1; |
| |
| when others => |
| null; |
| end case; |
| |
| while End_Sym < Line_Length |
| and then (Is_Alphanumeric (Line_Buffer (End_Sym + 1)) |
| or else Line_Buffer (End_Sym + 1) = '_') |
| loop |
| End_Sym := End_Sym + 1; |
| end loop; |
| |
| return OK_Identifier (Line_Buffer (Start_Sym .. End_Sym)); |
| end Symbol_Scanned; |
| |
| -------------------- |
| -- Variable_Index -- |
| -------------------- |
| |
| function Variable_Index (Name : String) return Natural is |
| begin |
| for J in 1 .. Num_Syms loop |
| if Matching_Strings (Symbols (J).all, Name) then |
| return J; |
| end if; |
| end loop; |
| |
| return Natural'Last; |
| end Variable_Index; |
| |
| -- Start of processing for GNATprep |
| |
| begin |
| |
| -- Parse the switches |
| |
| loop |
| case GNAT.Command_Line.Getopt ("D: b c r s u") is |
| when ASCII.NUL => |
| exit; |
| |
| when 'D' => |
| declare |
| S : String := GNAT.Command_Line.Parameter; |
| Index : Natural; |
| |
| begin |
| Index := Ada.Strings.Fixed.Index (S, "="); |
| |
| if Index = 0 then |
| Num_Syms := Num_Syms + 1; |
| Symbols (Num_Syms) := new String'(S); |
| Values (Num_Syms) := new String'("True"); |
| |
| else |
| Num_Syms := Num_Syms + 1; |
| Symbols (Num_Syms) := new String'(S (S'First .. Index - 1)); |
| Values (Num_Syms) := new String'(S (Index + 1 .. S'Last)); |
| end if; |
| end; |
| |
| when 'b' => |
| Blank_Deleted_Lines := True; |
| |
| when 'c' => |
| Opt_Comment_Deleted_Lines := True; |
| |
| when 'r' => |
| Source_Ref_Pragma := True; |
| |
| when 's' => |
| List_Symbols := True; |
| |
| when 'u' => |
| Undefined_Is_False := True; |
| |
| when others => |
| raise Usage_Error; |
| end case; |
| end loop; |
| |
| -- Get the file names |
| |
| loop |
| declare |
| S : constant String := GNAT.Command_Line.Get_Argument; |
| |
| begin |
| exit when S'Length = 0; |
| |
| if Infile_Name = null then |
| Infile_Name := new String'(S); |
| elsif Outfile_Name = null then |
| Outfile_Name := new String'(S); |
| elsif Deffile_Name = null then |
| Deffile_Name := new String'(S); |
| else |
| raise Usage_Error; |
| end if; |
| end; |
| end loop; |
| |
| -- Test we had all the arguments needed |
| |
| if Infile_Name = null |
| or else Outfile_Name = null |
| then |
| raise Usage_Error; |
| end if; |
| |
| if Source_Ref_Pragma and (not Opt_Comment_Deleted_Lines) then |
| Blank_Deleted_Lines := True; |
| end if; |
| |
| -- Get symbol definitions |
| |
| if Deffile_Name /= null then |
| Parse_Def_File; |
| end if; |
| |
| if Num_Errors > 0 then |
| raise Fatal_Error; |
| |
| elsif List_Symbols and then Num_Syms > 0 then |
| List_Symbols_Case : declare |
| |
| function Lt (Op1, Op2 : Natural) return Boolean; |
| -- Comparison routine for sort call |
| |
| procedure Move (From : Natural; To : Natural); |
| -- Move routine for sort call |
| |
| function Lt (Op1, Op2 : Natural) return Boolean is |
| L1 : constant Natural := Symbols (Op1)'Length; |
| L2 : constant Natural := Symbols (Op2)'Length; |
| MinL : constant Natural := Natural'Min (L1, L2); |
| |
| C1, C2 : Character; |
| |
| begin |
| for J in 0 .. MinL - 1 loop |
| C1 := To_Upper (Symbols (Op1).all (Symbols (Op1)'First + J)); |
| C2 := To_Upper (Symbols (Op2).all (Symbols (Op2)'First + J)); |
| |
| if C1 < C2 then |
| return True; |
| |
| elsif C1 > C2 then |
| return False; |
| end if; |
| end loop; |
| |
| return L1 < L2; |
| end Lt; |
| |
| procedure Move (From : Natural; To : Natural) is |
| begin |
| Symbols (To) := Symbols (From); |
| Values (To) := Values (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 |
| Sort_Syms.Sort (Num_Syms); |
| |
| Max_L := 7; |
| for J in 1 .. Num_Syms loop |
| Max_L := Natural'Max (Max_L, Symbols (J)'Length); |
| end loop; |
| |
| New_Line; |
| Put ("Symbol"); |
| |
| for J in 1 .. Max_L - 5 loop |
| Put (' '); |
| end loop; |
| |
| Put_Line ("Value"); |
| |
| Put ("------"); |
| |
| for J in 1 .. Max_L - 5 loop |
| Put (' '); |
| end loop; |
| |
| Put_Line ("------"); |
| |
| for J in 1 .. Num_Syms loop |
| Put (Symbols (J).all); |
| |
| for K in 1 .. Max_L - Symbols (J)'Length + 1 loop |
| Put (' '); |
| end loop; |
| |
| Put_Line (Values (J).all); |
| end loop; |
| |
| New_Line; |
| end List_Symbols_Case; |
| end if; |
| |
| -- Open files and initialize preprocessing |
| |
| begin |
| Open (Infile, In_File, Infile_Name.all); |
| |
| exception |
| when Name_Error => |
| Put_Line (Standard_Error, "cannot open " & Infile_Name.all); |
| raise Fatal_Error; |
| end; |
| |
| begin |
| Create (Outfile, Out_File, Outfile_Name.all); |
| |
| exception |
| when Name_Error => |
| Put_Line (Standard_Error, "cannot create " & Outfile_Name.all); |
| raise Fatal_Error; |
| end; |
| |
| if Source_Ref_Pragma then |
| Put_Line |
| (Outfile, "pragma Source_Reference (1, """ & Infile_Name.all & """);"); |
| end if; |
| |
| Line_Num := 0; |
| Current_File_Name := Infile_Name; |
| |
| PP_Depth := 0; |
| PP (0).Deleting := False; |
| |
| -- Loop through lines in input file |
| |
| while not End_Of_File (Infile) loop |
| Get_Line (Infile, Line_Buffer, Line_Length); |
| Line_Num := Line_Num + 1; |
| |
| -- Handle preprocessor line |
| |
| if Is_Preprocessor_Line then |
| K := Scan_Keyword; |
| |
| case K is |
| |
| -- If/Elsif processing |
| |
| when K_If | K_Elsif => |
| |
| -- If differs from elsif only in that an initial stack entry |
| -- must be made for the new if range. We set the match seen |
| -- entry to a copy of the deleting status in the range above |
| -- us. If we are deleting in the range above us, then we want |
| -- all the branches of the nested #if to delete. |
| |
| if K = K_If then |
| PP_Depth := PP_Depth + 1; |
| PP (PP_Depth) := |
| (If_Line => Line_Num, |
| Else_Line => 0, |
| Deleting => False, |
| Match_Seen => PP (PP_Depth - 1).Deleting); |
| |
| elsif PP_Depth = 0 then |
| Error ("no matching #if for this #elsif"); |
| goto Output; |
| |
| end if; |
| |
| PP (PP_Depth).Deleting := True; |
| |
| if not PP (PP_Depth).Match_Seen |
| and then Eval_Condition = True |
| then |
| |
| -- Case of match and no match yet in this #if |
| |
| PP (PP_Depth).Deleting := False; |
| PP (PP_Depth).Match_Seen := True; |
| No_Junk; |
| end if; |
| |
| -- Processing for #else |
| |
| when K_Else => |
| |
| if PP_Depth = 0 then |
| Error ("no matching #if for this #else"); |
| |
| elsif PP (PP_Depth).Else_Line /= 0 then |
| Error ("duplicate #else line (previous was on line" & |
| Natural'Image (PP (PP_Depth).Else_Line) & |
| ")"); |
| |
| else |
| PP (PP_Depth).Else_Line := Line_Num; |
| PP (PP_Depth).Deleting := PP (PP_Depth).Match_Seen; |
| end if; |
| |
| No_Junk; |
| |
| -- Process for #end |
| |
| when K_End => |
| |
| if PP_Depth = 0 then |
| Error ("no matching #if for this #end"); |
| |
| else |
| Skip_Spaces; |
| |
| if Scan_Keyword /= K_If then |
| Error ("expected if after #end"); |
| Ptr := Line_Length + 1; |
| end if; |
| |
| Skip_Spaces; |
| |
| if Ptr > Line_Length |
| or else Line_Buffer (Ptr) /= ';' |
| then |
| Error ("missing semicolon after #end if"); |
| else |
| Ptr := Ptr + 1; |
| end if; |
| |
| No_Junk; |
| |
| PP_Depth := PP_Depth - 1; |
| end if; |
| |
| when others => |
| Error ("invalid preprocessor keyword syntax"); |
| |
| end case; |
| |
| -- Handle symbol substitution |
| |
| -- Substitution is not allowed in string (which we simply skip), |
| -- but is allowed inside character constants. The last case is |
| -- because there is no way to know whether the user want to |
| -- substitute the name of an attribute ('Min or 'Max for instance) |
| -- or actually meant to substitue a character ('$name' is probably |
| -- a character constant, but my_type'$name'Min is probably an |
| -- attribute, with $name=Base) |
| |
| else |
| Ptr := 1; |
| |
| while Ptr < Line_Length loop |
| exit when At_End_Of_Line; |
| |
| case Line_Buffer (Ptr) is |
| |
| when ''' => |
| |
| -- Two special cases here: |
| -- '"' => we don't want the " sign to appear as belonging |
| -- to a string. |
| -- '$' => this is obviously not a substitution, just skip it |
| |
| if Ptr < Line_Length - 1 |
| and then Line_Buffer (Ptr + 1) = '"' |
| then |
| Ptr := Ptr + 2; |
| elsif Ptr < Line_Length - 2 |
| and then Line_Buffer (Ptr + 1 .. Ptr + 2) = "$'" |
| then |
| Ptr := Ptr + 2; |
| end if; |
| |
| when '"' => |
| |
| -- The special case of "" inside the string is easy to |
| -- handle: just ignore them. The second one will be seen |
| -- as the beginning of a second string |
| |
| Ptr := Ptr + 1; |
| while Ptr < Line_Length |
| and then Line_Buffer (Ptr) /= '"' |
| loop |
| Ptr := Ptr + 1; |
| end loop; |
| |
| when '$' => |
| |
| -- $ found, so scan out possible following symbol |
| |
| Start_Sym := Ptr + 1; |
| |
| if Symbol_Scanned then |
| |
| -- Look up symbol in table and if found do replacement |
| |
| for J in 1 .. Num_Syms loop |
| if Matching_Strings |
| (Symbols (J).all, Line_Buffer (Start_Sym .. End_Sym)) |
| then |
| declare |
| OldL : constant Positive := |
| End_Sym - Start_Sym + 2; |
| NewL : constant Positive := Values (J)'Length; |
| AdjL : constant Integer := NewL - OldL; |
| NewP : constant Positive := Ptr + NewL - 1; |
| |
| begin |
| Line_Buffer (NewP + 1 .. Line_Length + AdjL) := |
| Line_Buffer (End_Sym + 1 .. Line_Length); |
| Line_Buffer (Ptr .. NewP) := Values (J).all; |
| |
| Ptr := NewP; |
| Line_Length := Line_Length + AdjL; |
| end; |
| |
| exit; |
| end if; |
| end loop; |
| end if; |
| |
| when others => |
| null; |
| |
| end case; |
| Ptr := Ptr + 1; |
| end loop; |
| end if; |
| |
| -- Here after dealing with preprocessor line, output current line |
| |
| <<Output>> |
| |
| if Is_Preprocessor_Line or else PP (PP_Depth).Deleting then |
| if Blank_Deleted_Lines then |
| New_Line (Outfile); |
| |
| elsif Opt_Comment_Deleted_Lines then |
| if Line_Length = 0 then |
| Put_Line (Outfile, "--!"); |
| else |
| Put (Outfile, "--! "); |
| Put_Line (Outfile, Line_Buffer (1 .. Line_Length)); |
| end if; |
| end if; |
| |
| else |
| Put_Line (Outfile, Line_Buffer (1 .. Line_Length)); |
| end if; |
| end loop; |
| |
| for J in 1 .. PP_Depth loop |
| Error ("no matching #end for #if at line" & |
| Natural'Image (PP (J).If_Line)); |
| end loop; |
| |
| if Num_Errors = 0 then |
| Close (Outfile); |
| Set_Exit_Status (0); |
| else |
| Delete (Outfile); |
| Set_Exit_Status (1); |
| end if; |
| |
| exception |
| when Usage_Error => |
| Help_Page; |
| Set_Exit_Status (1); |
| |
| when GNAT.Command_Line.Invalid_Parameter => |
| Put_Line (Standard_Error, "No parameter given for -" |
| & GNAT.Command_Line.Full_Switch); |
| Help_Page; |
| Set_Exit_Status (1); |
| |
| when GNAT.Command_Line.Invalid_Switch => |
| Put_Line (Standard_Error, "Invalid Switch: -" |
| & GNAT.Command_Line.Full_Switch); |
| Help_Page; |
| Set_Exit_Status (1); |
| |
| when Fatal_Error => |
| Set_Exit_Status (1); |
| |
| when Expression_Error => |
| Set_Exit_Status (1); |
| |
| end GNATprep; |