summaryrefslogtreecommitdiff
path: root/gcc/ada/gnatprep.adb
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/ada/gnatprep.adb')
-rw-r--r--gcc/ada/gnatprep.adb1395
1 files changed, 1395 insertions, 0 deletions
diff --git a/gcc/ada/gnatprep.adb b/gcc/ada/gnatprep.adb
new file mode 100644
index 00000000000..ccff6fc4a3c
--- /dev/null
+++ b/gcc/ada/gnatprep.adb
@@ -0,0 +1,1395 @@
+------------------------------------------------------------------------------
+-- --
+-- 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;