1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 1996-2002, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 2, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
20 -- MA 02111-1307, USA. --
22 -- GNAT was originally developed by the GNAT team at New York University. --
23 -- Extensive contributions were provided by Ada Core Technologies Inc. --
25 ------------------------------------------------------------------------------
27 with Ada
.Characters
.Handling
; use Ada
.Characters
.Handling
;
28 with Ada
.Strings
.Fixed
;
29 with Ada
.Command_Line
; use Ada
.Command_Line
;
30 with Ada
.Text_IO
; use Ada
.Text_IO
;
32 with GNAT
.Heap_Sort_G
;
33 with GNAT
.Command_Line
;
39 type Strptr
is access String;
41 Usage_Error
: exception;
42 -- Raised if a usage error is detected, causes termination of processing
43 -- with an appropriate error message and error exit status set.
45 Fatal_Error
: exception;
46 -- Exception raised if fatal error detected
48 Expression_Error
: exception;
49 -- Exception raised when an invalid boolean expression is found
50 -- on a preprocessor line
52 ------------------------
53 -- Argument Line Data --
54 ------------------------
56 Outfile_Name
: Strptr
;
57 Deffile_Name
: Strptr
;
61 type Input_Ptr
is access Input
;
67 Line_Num
: Natural := 0;
69 -- Data for the current input file (main input file or included file
70 -- or definition file).
72 Infile
: Input_Ptr
:= new Input
;
76 Opt_Comment_Deleted_Lines
: Boolean := False; -- Set if -c switch set
77 Blank_Deleted_Lines
: Boolean := False; -- Set if -b switch set
78 List_Symbols
: Boolean := False; -- Set if -s switch set
79 Source_Ref_Pragma
: Boolean := False; -- Set if -r switch set
80 Undefined_Is_False
: Boolean := False; -- Set if -u switch set
81 -- Record command line options
83 ---------------------------
84 -- Definitions File Data --
85 ---------------------------
87 Num_Syms
: Natural := 0;
88 -- Number of symbols defined in definitions file
90 Symbols
: array (0 .. 10_000
) of Strptr
;
91 Values
: array (0 .. 10_000
) of Strptr
;
92 -- Symbol names and values. Note that the zero'th element is used only
93 -- during the call to Sort (to hold a temporary value, as required by
94 -- the GNAT.Heap_Sort_G interface).
100 Current_File_Name
: Strptr
;
101 -- Holds name of file being read (definitions file or input file)
103 Line_Buffer
: String (1 .. 20_000
);
106 Line_Length
: Natural;
107 -- Length of line in Line_Buffer
110 -- Input scan pointer for line in Line_Buffer
112 type Keyword
is (K_Not
, K_Then
, K_If
, K_Else
, K_End
, K_Elsif
,
113 K_And
, K_Or
, K_Open_Paren
, K_Close_Paren
,
114 K_Defined
, K_Andthen
, K_Orelse
, K_Equal
, K_Include
,
116 -- Keywords that are recognized on preprocessor lines. K_None indicates
117 -- that no keyword was present.
122 Start_Sym
, End_Sym
: Natural;
123 -- First and last positions of scanned symbol
125 Num_Errors
: Natural := 0;
126 -- Number of errors detected
128 -----------------------
129 -- Preprocessor Data --
130 -----------------------
132 -- The following record represents the state of an #if structure:
134 type PP_Rec
is record
136 -- Line number for #if line
139 -- File name of #if line
142 -- Line number for #else line, zero = no else seen yet
145 -- True if lines currently being deleted
147 Match_Seen
: Boolean;
148 -- True if either the #if condition or one of the previously seen
149 -- #elsif lines was true, meaning that any future #elsif sections
150 -- or the #else section, is to be deleted.
155 -- Preprocessor #if nesting level. A value of zero means that we are
156 -- outside any #if structure.
158 PP
: array (0 .. 100) of PP_Rec
;
159 -- Stack of records showing state of #if structures. PP (1) is the
160 -- outer level entry, and PP (PP_Depth) is the active entry. PP (0)
161 -- contains a dummy entry whose Deleting flag is always set to False.
167 function At_End_Of_Line
return Boolean;
168 -- First advances Ptr using Skip_Spaces. Then returns True if Ptr is
169 -- either at the end of the line, or at a -- comment sequence.
171 procedure Error
(Msg
: String);
172 -- Post error message with given text. The line number is taken from
173 -- Infile.Line_Num, and the column number from Ptr.
175 function Eval_Condition
176 (Parenthesis
: Natural := 0;
177 Do_Eval
: Boolean := True)
179 -- Eval the condition found in the current Line. The condition can
180 -- include any of the 'and', 'or', 'not', and parenthesis subexpressions.
181 -- If Line is an invalid expression, then Expression_Error is raised,
182 -- after an error message has been printed. Line can include 'then'
183 -- followed by a comment, which is automatically ignored. If Do_Eval
184 -- is False, then the expression is not evaluated at all, and symbols
187 function Eval_Symbol
(Do_Eval
: Boolean) return Boolean;
188 -- Read and evaluate the next symbol or expression (A, A'Defined, A=...)
189 -- If it is followed by 'Defined or an equality test, read as many symbols
190 -- as needed. Do_Eval has the same meaning as in Eval_Condition
193 -- Print a help page to summarize the usage of gnatprep
195 function Image
(N
: Natural) return String;
196 -- Returns Natural'Image (N) without the initial space
198 function Is_Preprocessor_Line
return Boolean;
199 -- Tests if current line is a preprocessor line, i.e. that its first
200 -- non-blank character is a # character. If so, then a result of True
201 -- is returned, and Ptr is set to point to the character following the
202 -- # character. If not, False is returned and Ptr is undefined.
205 -- Make sure no junk is present on a preprocessor line. Ptr points past
206 -- the scanned preprocessor syntax.
208 function OK_Identifier
(S
: String) return Boolean;
209 -- Tests if given referenced string is valid Ada identifier
211 function Matching_Strings
(S1
, S2
: String) return Boolean;
212 -- Check if S1 and S2 are the same string (this is a case independent
213 -- comparison, lower and upper case letters are considered to match).
214 -- Duplicate quotes in S2 are considered as a single quote ("" => ")
216 procedure Parse_Def_File
;
217 -- Parse the deffile given by the user
219 function Scan_Keyword
return Keyword
;
220 -- Advances Ptr to end of line or next non-blank using Skip_Spaces. Then
221 -- attempts to scan out a recognized keyword. if a recognized keyword is
222 -- found, sets Ptr past it, and returns the code for the keyword, if not,
223 -- then Ptr is left unchanged pointing to a non-blank character or to the
226 function Symbol_Scanned
return Boolean;
227 -- On entry, Start_Sym is set to the first character of an identifier
228 -- symbol to be scanned out. On return, End_Sym is set to the last
229 -- character of the identifier, and the result indicates if the scanned
230 -- symbol is a valid identifier (True = valid). Ptr is not changed.
232 procedure Skip_Spaces
;
233 -- Skips Ptr past tabs and spaces to next non-blank, or one character
234 -- past the end of line.
236 function Variable_Index
(Name
: String) return Natural;
237 -- Returns the index of the variable in the table. If the variable is not
238 -- found, returns Natural'Last
244 function At_End_Of_Line
return Boolean is
248 return Ptr
> Line_Length
250 (Ptr
< Line_Length
and then Line_Buffer
(Ptr
.. Ptr
+ 1) = "--");
257 procedure Error
(Msg
: String) is
258 L
: constant String := Natural'Image (Infile
.Line_Num
);
259 C
: constant String := Natural'Image (Ptr
);
262 Put
(Standard_Error
, Current_File_Name
.all);
263 Put
(Standard_Error
, ':');
264 Put
(Standard_Error
, L
(2 .. L
'Length));
265 Put
(Standard_Error
, ':');
266 Put
(Standard_Error
, C
(2 .. C
'Length));
267 Put
(Standard_Error
, ": ");
269 Put_Line
(Standard_Error
, Msg
);
270 Num_Errors
:= Num_Errors
+ 1;
277 function Eval_Condition
278 (Parenthesis
: Natural := 0;
279 Do_Eval
: Boolean := True)
282 Symbol_Is_True
: Boolean := False; -- init to avoid warning
286 -- Find the next subexpression
292 Symbol_Is_True
:= Eval_Symbol
(Do_Eval
);
296 -- Not applies to the next subexpression (either a simple
297 -- evaluation like A or A'Defined, or a parenthesis expression)
301 if K
= K_Open_Paren
then
302 Symbol_Is_True
:= not Eval_Condition
(Parenthesis
+ 1, Do_Eval
);
304 elsif K
= K_None
then
305 Symbol_Is_True
:= not Eval_Symbol
(Do_Eval
);
308 Ptr
:= Start_Sym
; -- Puts the keyword back
312 Symbol_Is_True
:= Eval_Condition
(Parenthesis
+ 1, Do_Eval
);
316 Error
("invalid syntax in preprocessor line");
317 raise Expression_Error
;
320 -- Do we have a compound expression with AND, OR, ...
325 if not At_End_Of_Line
then
326 Error
("Invalid Syntax at end of line");
327 raise Expression_Error
;
330 if Parenthesis
/= 0 then
331 Error
("Unmatched opening parenthesis");
332 raise Expression_Error
;
335 return Symbol_Is_True
;
338 if Parenthesis
/= 0 then
339 Error
("Unmatched opening parenthesis");
340 raise Expression_Error
;
343 return Symbol_Is_True
;
345 when K_Close_Paren
=>
346 if Parenthesis
= 0 then
347 Error
("Unmatched closing parenthesis");
348 raise Expression_Error
;
351 return Symbol_Is_True
;
354 return Symbol_Is_True
and Eval_Condition
(Parenthesis
, Do_Eval
);
357 if not Symbol_Is_True
then
359 -- Just skip the symbols for the remaining part
361 Symbol_Is_True
:= Eval_Condition
(Parenthesis
, False);
365 return Eval_Condition
(Parenthesis
, Do_Eval
);
369 return Symbol_Is_True
or Eval_Condition
(Parenthesis
, Do_Eval
);
372 if Symbol_Is_True
then
374 -- Just skip the symbols for the remaining part
376 Symbol_Is_True
:= Eval_Condition
(Parenthesis
, False);
380 return Eval_Condition
(Parenthesis
, Do_Eval
);
384 Error
("invalid syntax in preprocessor line");
385 raise Expression_Error
;
394 function Eval_Symbol
(Do_Eval
: Boolean) return Boolean is
395 Sym
: constant String := Line_Buffer
(Start_Sym
.. End_Sym
);
398 Symbol_Defined
: Boolean := False;
399 Symbol_Is_True
: Boolean := False;
407 if not Symbol_Scanned
then
408 Error
("invalid symbol name");
409 raise Expression_Error
;
414 -- Test if we have a simple test (A) or a more complicated one
419 if K
/= K_Defined
and then K
/= K_Equal
then
420 Ptr
:= Start_Sym
; -- Puts the keyword back
423 Index
:= Variable_Index
(Sym
);
427 Symbol_Defined
:= Index
/= Natural'Last;
428 Symbol_Is_True
:= Symbol_Defined
;
432 -- Read the second part of the statement
437 if not Symbol_Scanned
438 and then End_Sym
< Start_Sym
440 Error
("No right part for the equality test");
441 raise Expression_Error
;
446 -- If the variable was not found
449 if Index
= Natural'Last then
450 if not Undefined_Is_False
then
451 Error
("symbol name """ & Sym
&
452 """ is not defined in definitions file");
457 Right
: constant String
458 := Line_Buffer
(Start_Sym
.. End_Sym
);
461 if Right
(Right
'First) = '"' then
465 Right
(Right
'First + 1 .. Right
'Last - 1));
467 Index_R
:= Variable_Index
(Right
);
468 if Index_R
= Natural'Last then
469 Error
("Variable " & Right
& " in test is "
471 raise Expression_Error
;
474 Matching_Strings
(Values
(Index
).all,
475 Values
(Index_R
).all);
484 if Index
= Natural'Last then
486 Symbol_Defined
:= False;
487 if Do_Eval
and then not Symbol_Defined
then
488 if Undefined_Is_False
then
489 Symbol_Defined
:= True;
490 Symbol_Is_True
:= False;
494 ("symbol name """ & Sym
&
495 """ is not defined in definitions file");
499 elsif not Do_Eval
then
500 Symbol_Is_True
:= True;
502 elsif Matching_Strings
(Values
(Index
).all, "True") then
503 Symbol_Is_True
:= True;
505 elsif Matching_Strings
(Values
(Index
).all, "False") then
506 Symbol_Is_True
:= False;
509 Error
("symbol value is not True or False");
510 Symbol_Is_True
:= False;
515 return Symbol_Is_True
;
522 procedure Help_Page
is
524 Put_Line
(Standard_Error
,
525 "GNAT Preprocessor " &
526 Gnatvsn
.Gnat_Version_String
&
527 " Copyright 1996-2002 Free Software Foundation, Inc.");
528 Put_Line
(Standard_Error
,
529 "Usage: gnatprep [-bcrsu] [-Dsymbol=value] infile " &
530 "outfile [deffile]");
531 New_Line
(Standard_Error
);
532 Put_Line
(Standard_Error
, " infile Name of the input file");
533 Put_Line
(Standard_Error
, " outfile Name of the output file");
534 Put_Line
(Standard_Error
, " deffile Name of the definition file");
535 New_Line
(Standard_Error
);
536 Put_Line
(Standard_Error
, "gnatprep switches:");
537 Put_Line
(Standard_Error
, " -b Replace preprocessor lines by " &
539 Put_Line
(Standard_Error
, " -c Keep preprocessor lines as comments");
540 Put_Line
(Standard_Error
, " -D Associate symbol with value");
541 Put_Line
(Standard_Error
, " -r Generate Source_Reference pragma");
542 Put_Line
(Standard_Error
, " -s Print a sorted list of symbol names " &
544 Put_Line
(Standard_Error
, " -u Treat undefined symbols as FALSE");
545 New_Line
(Standard_Error
);
552 function Image
(N
: Natural) return String is
553 Result
: constant String := Natural'Image (N
);
555 return Result
(Result
'First + 1 .. Result
'Last);
558 --------------------------
559 -- Is_Preprocessor_Line --
560 --------------------------
562 function Is_Preprocessor_Line
return Boolean is
566 while Ptr
<= Line_Length
loop
567 if Line_Buffer
(Ptr
) = '#' then
571 elsif Line_Buffer
(Ptr
) > ' ' then
580 end Is_Preprocessor_Line
;
582 ----------------------
583 -- Matching_Strings --
584 ----------------------
586 function Matching_Strings
(S1
, S2
: String) return Boolean is
587 S2_Index
: Integer := S2
'First;
590 for S1_Index
in S1
'Range loop
592 if To_Upper
(S1
(S1_Index
)) /= To_Upper
(S2
(S2_Index
)) then
596 if S2
(S2_Index
) = '"'
597 and then S2_Index
< S2
'Last
598 and then S2
(S2_Index
+ 1) = '"'
600 S2_Index
:= S2_Index
+ 2;
602 S2_Index
:= S2_Index
+ 1;
605 -- If S2 was too short then
607 if S2_Index
> S2
'Last and then S1_Index
< S1
'Last then
613 return S2_Index
= S2
'Last + 1;
614 end Matching_Strings
;
625 or else (Ptr
< Line_Length
626 and then Line_Buffer
(Ptr
.. Ptr
+ 1) /= "--")
628 Error
("extraneous text on preprocessor line ignored");
636 function OK_Identifier
(S
: String) return Boolean is
637 P
: Natural := S
'First;
640 if S
'Length /= 0 and then S
(P
) = Character'Val (39) then -- '''
645 or else not Is_Letter
(S
(P
))
650 while P
<= S
'Last loop
651 if Is_Letter
(S
(P
)) or Is_Digit
(S
(P
)) then
656 and then S
(P
+ 1) /= '_'
675 procedure Parse_Def_File
is
677 Open
(Deffile
, In_File
, Deffile_Name
.all);
679 -- Initialize data for procedure Error
681 Infile
.Line_Num
:= 0;
682 Current_File_Name
:= Deffile_Name
;
684 -- Loop through lines in symbol definitions file
686 while not End_Of_File
(Deffile
) loop
687 Get_Line
(Deffile
, Line_Buffer
, Line_Length
);
688 Infile
.Line_Num
:= Infile
.Line_Num
+ 1;
694 or else (Ptr
< Line_Length
696 Line_Buffer
(Ptr
.. Ptr
+ 1) = "--")
703 if not Symbol_Scanned
then
704 Error
("invalid symbol identifier """ &
705 Line_Buffer
(Start_Sym
.. End_Sym
) &
713 if Ptr
>= Line_Length
714 or else Line_Buffer
(Ptr
.. Ptr
+ 1) /= ":="
716 Error
("missing "":="" in symbol definition line");
723 Num_Syms
:= Num_Syms
+ 1;
724 Symbols
(Num_Syms
) := new String'(Line_Buffer (Start_Sym .. End_Sym));
729 if At_End_Of_Line then
732 elsif Line_Buffer (Start_Sym) = '"' then
733 End_Sym := End_Sym + 1;
735 End_Sym := End_Sym + 1;
737 if End_Sym > Line_Length then
738 Error ("no closing quote
for string constant");
741 elsif End_Sym < Line_Length
742 and then Line_Buffer (End_Sym .. End_Sym + 1) = """"""
744 End_Sym := End_Sym + 1;
746 elsif Line_Buffer (End_Sym) = '"' then
754 while End_Sym < Line_Length
755 and then (Is_Alphanumeric (Line_Buffer (End_Sym + 1))
757 Line_Buffer (End_Sym + 1) = '_
'
759 Line_Buffer (End_Sym + 1) = '.')
761 End_Sym := End_Sym + 1;
766 if not At_End_Of_Line then
767 Error ("incorrect symbol value syntax");
772 Values (Num_Syms) := new String'(Line_Buffer
(Start_Sym
.. End_Sym
));
779 -- Could not open the file
782 Put_Line
(Standard_Error
, "cannot open " & Deffile_Name
.all);
790 function Scan_Keyword
return Keyword
is
791 Kptr
: constant Natural := Ptr
;
797 if Symbol_Scanned
then
799 -- If the symbol was the last thing on the line, End_Sym will
800 -- point too far in Line_Buffer
802 if End_Sym
> Line_Length
then
803 End_Sym
:= Line_Length
;
809 Sym
: constant String := Line_Buffer
(Start_Sym
.. End_Sym
);
812 if Matching_Strings
(Sym
, "not") then
815 elsif Matching_Strings
(Sym
, "then") then
818 elsif Matching_Strings
(Sym
, "if") then
821 elsif Matching_Strings
(Sym
, "else") then
824 elsif Matching_Strings
(Sym
, "end") then
827 elsif Matching_Strings
(Sym
, "elsif") then
830 elsif Matching_Strings
(Sym
, "and") then
831 if Scan_Keyword
= K_Then
then
835 Ptr
:= Start_Sym
; -- Put back the last keyword read
840 elsif Matching_Strings
(Sym
, "or") then
841 if Scan_Keyword
= K_Else
then
845 Ptr
:= Start_Sym
; -- Put back the last keyword read
850 elsif Matching_Strings
(Sym
, "'defined") then
853 elsif Matching_Strings
(Sym
, "include") then
860 return K_Close_Paren
;
876 procedure Skip_Spaces
is
878 while Ptr
<= Line_Length
loop
879 if Line_Buffer
(Ptr
) /= ' '
880 and then Line_Buffer
(Ptr
) /= ASCII
.HT
893 function Symbol_Scanned
return Boolean is
895 End_Sym
:= Start_Sym
- 1;
897 case Line_Buffer
(End_Sym
+ 1) is
899 when '(' |
')' |
'=' =>
900 End_Sym
:= End_Sym
+ 1;
904 End_Sym
:= End_Sym
+ 1;
905 while End_Sym
< Line_Length
loop
907 if Line_Buffer
(End_Sym
+ 1) = '"' then
909 if End_Sym
+ 2 < Line_Length
910 and then Line_Buffer
(End_Sym
+ 2) = '"'
912 End_Sym
:= End_Sym
+ 2;
917 End_Sym
:= End_Sym
+ 1;
921 if End_Sym
>= Line_Length
then
922 Error
("Invalid string ");
923 raise Expression_Error
;
926 End_Sym
:= End_Sym
+ 1;
930 End_Sym
:= End_Sym
+ 1;
936 while End_Sym
< Line_Length
937 and then (Is_Alphanumeric
(Line_Buffer
(End_Sym
+ 1))
938 or else Line_Buffer
(End_Sym
+ 1) = '_')
940 End_Sym
:= End_Sym
+ 1;
943 return OK_Identifier
(Line_Buffer
(Start_Sym
.. End_Sym
));
950 function Variable_Index
(Name
: String) return Natural is
952 for J
in 1 .. Num_Syms
loop
953 if Matching_Strings
(Symbols
(J
).all, Name
) then
961 -- Start of processing for GNATprep
965 -- Parse the switches
968 case GNAT
.Command_Line
.Getopt
("D: b c r s u") is
974 S
: String := GNAT
.Command_Line
.Parameter
;
978 Index
:= Ada
.Strings
.Fixed
.Index
(S
, "=");
981 Num_Syms
:= Num_Syms
+ 1;
982 Symbols
(Num_Syms
) := new String'(S);
983 Values (Num_Syms) := new String'("True");
986 Num_Syms
:= Num_Syms
+ 1;
987 Symbols
(Num_Syms
) := new String'(S (S'First .. Index - 1));
988 Values (Num_Syms) := new String'(S
(Index
+ 1 .. S
'Last));
993 Blank_Deleted_Lines
:= True;
996 Opt_Comment_Deleted_Lines
:= True;
999 Source_Ref_Pragma
:= True;
1002 List_Symbols
:= True;
1005 Undefined_Is_False
:= True;
1012 -- Get the file names
1016 S
: constant String := GNAT
.Command_Line
.Get_Argument
;
1019 exit when S
'Length = 0;
1021 if Infile
.Name
= null then
1022 Infile
.Name
:= new String'(S);
1023 elsif Outfile_Name = null then
1024 Outfile_Name := new String'(S
);
1025 elsif Deffile_Name
= null then
1026 Deffile_Name
:= new String'(S);
1033 -- Test we had all the arguments needed
1035 if Infile.Name = null
1036 or else Outfile_Name = null
1041 if Source_Ref_Pragma and (not Opt_Comment_Deleted_Lines) then
1042 Blank_Deleted_Lines := True;
1045 -- Get symbol definitions
1047 if Deffile_Name /= null then
1051 if Num_Errors > 0 then
1054 elsif List_Symbols and then Num_Syms > 0 then
1055 List_Symbols_Case : declare
1057 function Lt (Op1, Op2 : Natural) return Boolean;
1058 -- Comparison routine for sort call
1060 procedure Move (From : Natural; To : Natural);
1061 -- Move routine for sort call
1063 function Lt (Op1, Op2 : Natural) return Boolean is
1064 L1 : constant Natural := Symbols (Op1)'Length;
1065 L2 : constant Natural := Symbols (Op2)'Length;
1066 MinL : constant Natural := Natural'Min (L1, L2);
1071 for J in 0 .. MinL - 1 loop
1072 C1 := To_Upper (Symbols (Op1).all (Symbols (Op1)'First + J));
1073 C2 := To_Upper (Symbols (Op2).all (Symbols (Op2)'First + J));
1086 procedure Move (From : Natural; To : Natural) is
1088 Symbols (To) := Symbols (From);
1089 Values (To) := Values (From);
1092 package Sort_Syms is new GNAT.Heap_Sort_G (Move, Lt);
1095 -- Maximum length of any symbol
1097 -- Start of processing for List_Symbols_Case
1100 Sort_Syms.Sort (Num_Syms);
1103 for J in 1 .. Num_Syms loop
1104 Max_L := Natural'Max (Max_L, Symbols (J)'Length);
1110 for J in 1 .. Max_L - 5 loop
1118 for J in 1 .. Max_L - 5 loop
1122 Put_Line ("------");
1124 for J in 1 .. Num_Syms loop
1125 Put (Symbols (J).all);
1127 for K in 1 .. Max_L - Symbols (J)'Length + 1 loop
1131 Put_Line (Values (J).all);
1135 end List_Symbols_Case;
1138 -- Open files and initialize preprocessing
1141 Open (Infile.File, In_File, Infile.Name.all);
1145 Put_Line (Standard_Error, "cannot open " & Infile.Name.all);
1150 Create (Outfile, Out_File, Outfile_Name.all);
1154 Put_Line (Standard_Error, "cannot create " & Outfile_Name.all);
1158 Infile.Line_Num := 0;
1159 Current_File_Name := Infile.Name;
1162 PP (0).Deleting := False;
1164 -- We return here after we start reading an include file and after
1165 -- we have finished reading an include file.
1169 -- If we generate Source_Reference pragmas, then generate one
1170 -- either with line number 1 for a newly included file, or
1171 -- with the number of the next line when we have returned to the
1174 if Source_Ref_Pragma then
1176 (Outfile, "pragma Source_Reference (" &
1177 Image (Infile.Line_Num + 1) &
1178 ", """ & Infile.Name.all & """);");
1181 -- Loop through lines in input file
1183 while not End_Of_File (Infile.File) loop
1184 Get_Line (Infile.File, Line_Buffer, Line_Length);
1185 Infile.Line_Num := Infile.Line_Num + 1;
1187 -- Handle preprocessor line
1189 if Is_Preprocessor_Line then
1197 -- Ignore if Deleting is True
1199 if PP (PP_Depth).Deleting then
1205 if Ptr >= Line_Length then
1206 Error ("no file to include");
1208 elsif Line_Buffer (Ptr) /= '"' then
1210 ("file to include must be specified as a literal
string");
1214 Start_File : constant Positive := Ptr + 1;
1219 while Line_Buffer (Ptr) = ' '
1220 or else Line_Buffer (Ptr) = ASCII.HT
1225 if Ptr <= Start_File
1226 or else Line_Buffer (Ptr) /= '"'
1228 Error ("no string literal for included file");
1231 if Infile.Next = null then
1232 Infile.Next := new Input;
1233 Infile.Next.Prev := Infile;
1236 Infile := Infile.Next;
1238 new String'(Line_Buffer
(Start_File
.. Ptr
- 1));
1240 -- Check for circularity: an file including itself,
1241 -- either directly or indirectly.
1244 File
: Input_Ptr
:= Infile
.Prev
;
1248 and then File
.Name
.all /= Infile
.Name
.all
1253 if File
/= null then
1254 Infile
:= Infile
.Prev
;
1255 Error
("circularity in included files");
1257 while File
.Prev
/= null loop
1261 while File
/= Infile
.Next
loop
1262 Error
('"' & File
.Name
.all &
1264 File
.Next
.Name
.all & '"');
1269 -- We have a file name and no circularity.
1270 -- Open the file and record an error if the
1271 -- file cannot be opened.
1274 Open
(Infile
.File
, In_File
, Infile
.Name
.all);
1275 Current_File_Name
:= Infile
.Name
;
1276 Infile
.Line_Num
:= 0;
1278 -- If we use Source_Reference pragma,
1279 -- we need to output one for this new file.
1285 -- We need to set the input file to
1286 -- the including file, so that the
1287 -- line number is correct when reporting
1290 Infile
:= Infile
.Prev
;
1291 Error
("cannot open """ &
1292 Infile
.Next
.Name
.all & '"');
1300 -- If/Elsif processing
1302 when K_If | K_Elsif
=>
1304 -- If differs from elsif only in that an initial stack entry
1305 -- must be made for the new if range. We set the match seen
1306 -- entry to a copy of the deleting status in the range above
1307 -- us. If we are deleting in the range above us, then we want
1308 -- all the branches of the nested #if to delete.
1311 PP_Depth
:= PP_Depth
+ 1;
1313 (If_Line
=> Infile
.Line_Num
,
1314 If_Name
=> Infile
.Name
,
1317 Match_Seen
=> PP
(PP_Depth
- 1).Deleting
);
1319 elsif PP_Depth
= 0 then
1320 Error
("no matching #if for this #elsif");
1325 PP
(PP_Depth
).Deleting
:= True;
1327 if not PP
(PP_Depth
).Match_Seen
1328 and then Eval_Condition
= True
1331 -- Case of match and no match yet in this #if
1333 PP
(PP_Depth
).Deleting
:= False;
1334 PP
(PP_Depth
).Match_Seen
:= True;
1338 -- Processing for #else
1342 if PP_Depth
= 0 then
1343 Error
("no matching #if for this #else");
1345 elsif PP
(PP_Depth
).Else_Line
/= 0 then
1346 Error
("duplicate #else line (previous was on line" &
1347 Natural'Image (PP
(PP_Depth
).Else_Line
) &
1351 PP
(PP_Depth
).Else_Line
:= Infile
.Line_Num
;
1352 PP
(PP_Depth
).Deleting
:= PP
(PP_Depth
).Match_Seen
;
1361 if PP_Depth
= 0 then
1362 Error
("no matching #if for this #end");
1367 if Scan_Keyword
/= K_If
then
1368 Error
("expected if after #end");
1369 Ptr
:= Line_Length
+ 1;
1374 if Ptr
> Line_Length
1375 or else Line_Buffer
(Ptr
) /= ';'
1377 Error
("missing semicolon after #end if");
1384 PP_Depth
:= PP_Depth
- 1;
1388 Error
("invalid preprocessor keyword syntax");
1392 -- Handle symbol substitution
1394 -- Substitution is not allowed in string (which we simply skip),
1395 -- but is allowed inside character constants. The last case is
1396 -- because there is no way to know whether the user want to
1397 -- substitute the name of an attribute ('Min or 'Max for instance)
1398 -- or actually meant to substitue a character ('$name' is probably
1399 -- a character constant, but my_type'$name'Min is probably an
1400 -- attribute, with $name=Base)
1405 while Ptr
< Line_Length
loop
1406 exit when At_End_Of_Line
;
1408 case Line_Buffer
(Ptr
) is
1412 -- Two special cases here:
1413 -- '"' => we don't want the " sign to appear as belonging
1415 -- '$' => this is obviously not a substitution, just skip it
1417 if Ptr
< Line_Length
- 1
1418 and then Line_Buffer
(Ptr
+ 1) = '"'
1421 elsif Ptr
< Line_Length
- 2
1422 and then Line_Buffer
(Ptr
+ 1 .. Ptr
+ 2) = "$'"
1429 -- The special case of "" inside the string is easy to
1430 -- handle: just ignore them. The second one will be seen
1431 -- as the beginning of a second string
1434 while Ptr
< Line_Length
1435 and then Line_Buffer
(Ptr
) /= '"'
1442 -- $ found, so scan out possible following symbol
1444 Start_Sym
:= Ptr
+ 1;
1446 if Symbol_Scanned
then
1448 -- Look up symbol in table and if found do replacement
1450 for J
in 1 .. Num_Syms
loop
1452 (Symbols
(J
).all, Line_Buffer
(Start_Sym
.. End_Sym
))
1455 OldL
: constant Positive :=
1456 End_Sym
- Start_Sym
+ 2;
1457 NewL
: constant Positive := Values
(J
)'Length;
1458 AdjL
: constant Integer := NewL
- OldL
;
1459 NewP
: constant Positive := Ptr
+ NewL
- 1;
1462 Line_Buffer
(NewP
+ 1 .. Line_Length
+ AdjL
) :=
1463 Line_Buffer
(End_Sym
+ 1 .. Line_Length
);
1464 Line_Buffer
(Ptr
.. NewP
) := Values
(J
).all;
1467 Line_Length
:= Line_Length
+ AdjL
;
1483 -- Here after dealing with preprocessor line, output current line
1487 if Is_Preprocessor_Line
or else PP
(PP_Depth
).Deleting
then
1488 if Blank_Deleted_Lines
then
1491 elsif Opt_Comment_Deleted_Lines
then
1492 if Line_Length
= 0 then
1493 Put_Line
(Outfile
, "--!");
1495 Put
(Outfile
, "--! ");
1496 Put_Line
(Outfile
, Line_Buffer
(1 .. Line_Length
));
1501 Put_Line
(Outfile
, Line_Buffer
(1 .. Line_Length
));
1505 -- If we have finished reading an included file, close it and continue
1506 -- with the next line of the including file.
1508 if Infile
.Prev
/= null then
1509 Close
(Infile
.File
);
1510 Infile
:= Infile
.Prev
;
1511 Current_File_Name
:= Infile
.Name
;
1515 for J
in 1 .. PP_Depth
loop
1516 if PP
(J
).If_Name
= Infile
.Name
then
1517 Error
("no matching #end for #if at line" &
1518 Natural'Image (PP
(J
).If_Line
));
1520 Error
("no matching #end for #if at line" &
1521 Natural'Image (PP
(J
).If_Line
) &
1522 " of file """ & PP
(J
).If_Name
.all & '"');
1526 if Num_Errors
= 0 then
1528 Set_Exit_Status
(0);
1531 Set_Exit_Status
(1);
1537 Set_Exit_Status
(1);
1539 when GNAT
.Command_Line
.Invalid_Parameter
=>
1540 Put_Line
(Standard_Error
, "No parameter given for -"
1541 & GNAT
.Command_Line
.Full_Switch
);
1543 Set_Exit_Status
(1);
1545 when GNAT
.Command_Line
.Invalid_Switch
=>
1546 Put_Line
(Standard_Error
, "Invalid Switch: -"
1547 & GNAT
.Command_Line
.Full_Switch
);
1549 Set_Exit_Status
(1);
1552 Set_Exit_Status
(1);
1554 when Expression_Error
=>
1555 Set_Exit_Status
(1);